基于jackson的kotlin json字符串对比器

package comparator

import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.JsonNodeType
import com.fasterxml.jackson.databind.node.NumericNode
import java.util.*
import kotlin.Comparator


enum class CompareType {
    /*
     * both compare name and value, but if name missing found, compare stop, and print the result, or it will compare all values are the same until find difference.
     */
    COMPARE_NAME,
    /*
     * if name missing found, compare stop and print the result.
     */
    COMPARE_NAME_VALUE
}

internal class NumberComparator : Comparator<JsonNode> {
    override fun compare(o1: JsonNode, o2: JsonNode): Int {
        if (o1 == o2) {
            return 0
        }
        if (o1 is NumericNode && o2 is NumericNode) {
            val d1 = o1.asDouble()
            val d2 = o2.asDouble()
            if (d1.compareTo(d2) == 0) {
                return 0
            }
        }
        return 1
    }
}

class Comparator {
    constructor(){
        debug=false;
        TYPE_COMPARE = CompareType.COMPARE_NAME_VALUE
        IGNORED_FILED_LIST = java.util.ArrayList<String>();
    }
    constructor(debugI: Boolean?, compareType:String?, ignoreFieldList: String?){
        if(debugI != null) {debug=debugI;}

        var type = CompareType.COMPARE_NAME_VALUE
        if (compareType != null && compareType.trim { it <= ' ' } !== "") {
            if (CompareType.COMPARE_NAME_VALUE.toString().equals(compareType, ignoreCase = true)) {
                type = CompareType.COMPARE_NAME_VALUE
            } else if (CompareType.COMPARE_NAME.toString().equals(compareType, ignoreCase = true)) {
                type = CompareType.COMPARE_NAME
            }
        }
        TYPE_COMPARE = type


        var ignoredFields: ArrayList<String> = java.util.ArrayList<String>();
        if (ignoreFieldList == null || ignoreFieldList.trim { it <= ' ' }.length == 0) {
            if(debug){
                println("no valid ignored field list")
            }

        } else {
            var fs:kotlin.collections.List<String> = ignoreFieldList.split(",");
            for(f:String in fs){
                var fTrimed:String = f.trim();
                if(fTrimed.length > 0){
                    ignoredFields.add(fTrimed)
                }
            }
        }
        IGNORED_FILED_LIST = ignoredFields;


        if(debug){
            println("selected compare type:" + TYPE_COMPARE)
        }
        if(debug){
            println("selected ignored field list:" + IGNORED_FILED_LIST)
        }
    }

    fun compare(path: String, o1: JsonNode?, o2: JsonNode?, res: ArrayList<Diff?>) {
        // basic input check
        if (o1 == null) {
            if (o2 != null) {
                res.add(Diff(pathGetter(path, ""), MISS_NAME_TIP_WORD, ""))
            }
            return
        }
        if (o2 == null) {
            res.add(Diff(pathGetter(path, ""), "", MISS_NAME_TIP_WORD))
            return
        }
        val typeO1 = o1.nodeType
        val typeO2 = o2.nodeType
        if (typeO1 != typeO2) {
            val diff = Diff(pathGetter(path, ""), typeO1.name, typeO2.name)
            addAndfilterDiffRes(res, diff)
            return
        }
        val type = o1.nodeType
        if (type == JsonNodeType.ARRAY) {
            val nodeArrLeft: MutableList<JsonNode> = ArrayList()
            val iteratorLeft = o1.elements()
            while (iteratorLeft.hasNext()) {
                nodeArrLeft.add(iteratorLeft.next())
            }
            val nodeArrRight: MutableList<JsonNode> = ArrayList()
            val iteratorRight = o2.elements()
            while (iteratorRight.hasNext()) {
                nodeArrRight.add(iteratorRight.next())
            }
            if (nodeArrLeft.size == nodeArrRight.size) {
                for (i in 0 until o1.size()) {
                    compare(pathGetter(path, o1.toString()+"["+i+"]"), nodeArrLeft[i], nodeArrRight[i], res)
                }
            } else if (nodeArrLeft.size > nodeArrRight.size) {
                val lefti = nodeArrLeft[nodeArrRight.size]
                val diff = Diff(pathGetter(path, "["+nodeArrRight.size+"]"), lefti.toString(), MISS_NAME_TIP_WORD)
                addAndfilterDiffRes(res, diff)
            } else {
                val righti = nodeArrRight[nodeArrLeft.size]
                val diff = Diff(pathGetter(path, "["+nodeArrLeft.size+"]"), MISS_NAME_TIP_WORD, righti.toString())
                addAndfilterDiffRes(res, diff)
            }
        } else if (type == JsonNodeType.OBJECT) {
            val o1NameSet = getNamesSet(o1.fieldNames())
            val o2NameSet = getNamesSet(o2.fieldNames())
            val sameList: MutableList<String> = ArrayList()
            // compare node name size
            // check key is the same
            for (key in o1NameSet) {
                if (!o2NameSet.contains(key)) {
                    val diff = Diff(pathGetter(path, key), o1[key].toString(), MISS_NAME_TIP_WORD)
                    addAndfilterDiffRes(res, diff)
                } else {
                    sameList.add(key)
                }
            }
            for (key in o2NameSet) {
                if (!o1NameSet.contains(key)) {
                    val diff = Diff(pathGetter(path, key), MISS_NAME_TIP_WORD, o2[key].toString())
                    addAndfilterDiffRes(res, diff)
                }
            }

            // compare node value
            for (key in sameList) {
                val nodeleft = o1[key]
                val noderight = o2[key]
                val typeObjLeft = nodeleft.nodeType
                val typeObjRight = noderight.nodeType
                if (typeObjLeft != typeObjRight) {
                    val diff = Diff(pathGetter(path, key), typeObjLeft.name, typeObjRight.name)
                    addAndfilterDiffRes(res, diff)
                } else {
                    if (typeObjLeft == JsonNodeType.ARRAY) {
                        val nodeArrLeft: MutableList<JsonNode> = ArrayList()
                        val iteratorLeft = nodeleft.elements()
                        while (iteratorLeft.hasNext()) {
                            nodeArrLeft.add(iteratorLeft.next())
                        }
                        val nodeArrRight: MutableList<JsonNode> = ArrayList()
                        val iteratorRight = noderight.elements()
                        while (iteratorRight.hasNext()) {
                            nodeArrRight.add(iteratorRight.next())
                        }
                        if (nodeArrLeft.size == nodeArrRight.size) {
                            for (i in nodeArrLeft.indices) {
                                compare(pathGetter(path, key+"["+i+"]"), nodeArrLeft[i], nodeArrRight[i], res)
                            }
                        } else if (nodeArrLeft.size > nodeArrRight.size) {
                            val diff = Diff(
                                pathGetter(path, key+"["+nodeArrRight.size+"]"),
                                nodeArrLeft[nodeArrRight.size].toString(),
                                MISS_NAME_TIP_WORD
                            )
                            addAndfilterDiffRes(res, diff)
                        } else {
                            val diff = Diff(
                                pathGetter(path, key + "["+nodeArrLeft.size+"]"),
                                MISS_NAME_TIP_WORD,
                                nodeArrRight[nodeArrLeft.size].toString()
                            )
                            addAndfilterDiffRes(res, diff)
                        }
                    } else if (typeObjLeft == JsonNodeType.OBJECT) {
                        compare(pathGetter(path, key), nodeleft, noderight, res)
                    } else {
                        if (TYPE_COMPARE == CompareType.COMPARE_NAME) {
                            // just compare names
                            continue
                        }
                        if (!nodeleft.equals(NumberComparator(), noderight)) {
                            val diff = Diff(pathGetter(path, key), nodeleft.toString(), noderight.toString())
                            addAndfilterDiffRes(res, diff)
                        }
                    }
                }
            }
        } else {
            if (TYPE_COMPARE == CompareType.COMPARE_NAME) {
                // just compare names
                return
            }
            if (!o1.equals(NumberComparator(), o2)) {
                val diff = Diff(pathGetter(path, ""), o1.toString(), o2.toString())
                addAndfilterDiffRes(res, diff)
            }
        }
        return
    }

    fun getNamesSet(it: Iterator<String>): TreeSet<String> {
        val res = TreeSet<String>()
        while (it.hasNext()) {
            val name = it.next()
            res.add(name)
        }
        return res
    }

    fun pathGetter(parent: String, key: String?): String {
        val path = StringBuilder()
        if (parent == "") {
            path.append(key)
        } else if (key == null || "" == key.trim { it <= ' ' }) {
            path.append(parent)
        } else {
            path.append(parent).append(".").append(key)
        }
        return path.toString()
    }

    fun addAndfilterDiffRes(res: MutableList<Diff?>, diff: Diff) {
        if (!IGNORED_FILED_LIST.contains(diff.node)) {
            res.add(diff)
        }
    }

    /**
     *
     * @param compareType <br></br>
     * support two compare type set at agrs[0]: COMPARE_NAME_VALUE, COMPARE_NAME <br></br>
     *
     *
     * COMPARE_NAME_VALUE:both compare name and value, but if name missing found, compare stop, and print the result<br></br>
     * COMPARE_NAME:if name missing found, compare stop and print the result
     *
     */
    fun getCompareResult(left: String?, right: String?): ArrayList<Diff?> {

        var leftNode: JsonNode? = null
        try {
            leftNode = mapper.readTree(left)
        } catch (e: Exception) {
            if(debug){
                println("left parse failed:" + e.message)
            }
        }
        var rightNode: JsonNode? = null
        try {
            rightNode = mapper.readTree(right)
        } catch (e: Exception) {
            if(debug){
                println("right parse failed:" + e.message)
            }
        }
        return getCompareResultWithJson(leftNode, rightNode)
    }

    fun getCompareResultWithJson(
        left: JsonNode?,
        right: JsonNode?
    ): ArrayList<Diff?> {
        val res: ArrayList<Diff?> = ArrayList<Diff?>()
        try {
            compare("\$root", left, right, res)
        } catch (e: Exception) {
            if(debug){
                println("compare failed:" + e.message)
            }
        }
        return res
    }

    companion object {
        // fields to ignore when compare
        lateinit var IGNORED_FILED_LIST:ArrayList<String>;
        var TYPE_COMPARE = CompareType.COMPARE_NAME_VALUE
        var MISS_NAME_TIP_WORD = "not exist"
        public val mapper = ObjectMapper()
        public var debug:Boolean = false;

        init {
            mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true)
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        }
    }
}

  

posted @ 2022-04-06 17:59  原子切割员  阅读(110)  评论(0编辑  收藏  举报