Java 比较两个JSON对象的数字容差

Java 比较两个JSON对象的数字容差,java,jackson,equals,Java,Jackson,Equals,我目前正在使用Jackson JSON库来比较几个JSON对象 JSON对象包含一些数学计算的结果。例如: { name:"result_set_1" result:[0.123151351353,1.0123151533,2.0123051353] } 当将这些与参考实现进行比较时,我注意到一些浏览器产生的结果略有不同。这很好,但我需要确保在比较数字时有一个公差 实际上,JSONNode.equals()做了一个非常好的深度相等,但它比较数字的方式迫使它们完全相等。我需要增加一个容忍

我目前正在使用Jackson JSON库来比较几个JSON对象

JSON对象包含一些数学计算的结果。例如:

{
  name:"result_set_1"
  result:[0.123151351353,1.0123151533,2.0123051353]
}
当将这些与参考实现进行比较时,我注意到一些浏览器产生的结果略有不同。这很好,但我需要确保在比较数字时有一个公差

实际上,JSONNode.equals()做了一个非常好的深度相等,但它比较数字的方式迫使它们完全相等。我需要增加一个容忍度

有没有办法用宽容来做一个深度的平等

现在我发现的唯一方法是迭代每个节点,检查它是否是一个数字,并进行公差检查,而不是等号。但是这个方法非常庞大,因为你必须检查节点是否是一个对象,一个数组,一个字符串。。。等并为每个人做具体的事情。我只想要数字的自定义行为


有没有更优雅的方法?任何第三方库?

前言:使用
反序列化功能配置您的
对象映射器
。使用\u BIGDECIMAL\u表示\u浮点值
;默认情况下,Jackson会将“非整数”JSON数字反序列化为
double
s,但如果这样做,将失去精度

这里有一个我写过的例子;您必须覆盖
doNumEquivalent()
doNumHash()
以满足您的需要。这意味着你用番石榴,但实际上,你应该

注意,是的,有一个散列;您可能需要它,也可能不需要它,但这样做并没有什么坏处,特别是因为这意味着您将能够使用
(您必须
.add(myEquivalence.wrap(myNode))

注2:
NodeType
是我的一个特定类;使用“core Jackson”时,您希望使用
JsonNode
.getNodeType()

用法:
if(myEquivalence.equivalence(a,b))//etc

public abstract class JsonNumEquivalence
    extends Equivalence<JsonNode>
{
    // Implement!
    protected abstract boolean doNumEquivalent(final JsonNode a, final JsonNode b);

    // Implement!
    protected abstract int doNumHash(final JsonNode t);

    @Override
    protected final boolean doEquivalent(final JsonNode a, final JsonNode b)
    {
        /*
         * If both are numbers, delegate to the helper method
         */
        if (a.isNumber() && b.isNumber())
            return doNumEquivalent(a, b);

        final NodeType typeA = NodeType.getNodeType(a);
        final NodeType typeB = NodeType.getNodeType(b);

        /*
         * If they are of different types, no dice
         */
        if (typeA != typeB)
            return false;

        /*
         * For all other primitive types than numbers, trust JsonNode
         */
        if (!a.isContainerNode())
            return a.equals(b);

        /*
         * OK, so they are containers (either both arrays or objects due to the
         * test on types above). They are obviously not equal if they do not
         * have the same number of elements/members.
         */
        if (a.size() != b.size())
            return false;

        /*
         * Delegate to the appropriate method according to their type.
         */
        return typeA == NodeType.ARRAY ? arrayEquals(a, b) : objectEquals(a, b);
    }

    @Override
    protected final int doHash(final JsonNode t)
    {
        /*
         * If this is a numeric node, delegate to the helper method
         */
        if (t.isNumber())
            return doNumHash(t);

        /*
         * If this is a primitive type (other than numbers, handled above),
         * delegate to JsonNode.
         */
        if (!t.isContainerNode())
            return t.hashCode();

        /*
         * The following hash calculations work, yes, but they are poor at best.
         * And probably slow, too.
         *
         * TODO: try and figure out those hash classes from Guava
         */
        int ret = 0;

        /*
         * If the container is empty, just return
         */
        if (t.size() == 0)
            return ret;

        /*
         * Array
         */
        if (t.isArray()) {
            for (final JsonNode element: t)
                ret = 31 * ret + doHash(element);
            return ret;
        }

        /*
         * Not an array? An object.
         */
        final Iterator<Map.Entry<String, JsonNode>> iterator = t.fields();

        Map.Entry<String, JsonNode> entry;

        while (iterator.hasNext()) {
            entry = iterator.next();
            ret = 31 * ret
                + (entry.getKey().hashCode() ^ doHash(entry.getValue()));
        }

        return ret;
    }

    private boolean arrayEquals(final JsonNode a, final JsonNode b)
    {
        /*
         * We are guaranteed here that arrays are the same size.
         */
        final int size = a.size();

        for (int i = 0; i < size; i++)
            if (!doEquivalent(a.get(i), b.get(i)))
                return false;

        return true;
    }

    private boolean objectEquals(final JsonNode a, final JsonNode b)
    {
        /*
         * Grab the key set from the first node
         */
        final Set<String> keys = Sets.newHashSet(a.fieldNames());

        /*
         * Grab the key set from the second node, and see if both sets are the
         * same. If not, objects are not equal, no need to check for children.
         */
        final Set<String> set = Sets.newHashSet(b.fieldNames());
        if (!set.equals(keys))
            return false;

        /*
         * Test each member individually.
         */
        for (final String key: keys)
            if (!doEquivalent(a.get(key), b.get(key)))
                return false;

        return true;
    }
}
公共抽象类JSONNUMEEquivalence
扩展等价性
{
//实施!
受保护抽象布尔doNumEquivalent(最终JsonNode a,最终JsonNode b);
//实施!
受保护的抽象int doNumHash(最终JsonNode t);
@凌驾
受保护的最终布尔DoeEquivalent(最终JsonNode a,最终JsonNode b)
{
/*
*如果两者都是数字,则委托给helper方法
*/
如果(a.isNumber()&&b.isNumber())
回报率等于(a,b);
最终节点类型a=NodeType.getNodeType(a);
最终节点类型b=NodeType.getNodeType(b);
/*
*如果它们属于不同类型,则不使用骰子
*/
如果(类型A!=类型B)
返回false;
/*
*对于除数字以外的所有其他基元类型,请信任JsonNode
*/
如果(!a.isContainerNode())
返回a等于(b);
/*
*好的,它们是容器(由于
*测试上述类型)。如果它们不相等,则它们显然不相等
*具有相同数量的元素/成员。
*/
如果(a.size()!=b.size())
返回false;
/*
*根据方法的类型委托给相应的方法。
*/
返回typeA==NodeType.ARRAY?arrayEquals(a,b):objectEquals(a,b);
}
@凌驾
受保护的最终int doHash(最终JsonNode t)
{
/*
*如果这是数值节点,请委托给helper方法
*/
if(t.isNumber())
返回doNumHash(t);
/*
*如果这是一种基本类型(数字除外,请在上面进行处理),
*委托给JsonNode。
*/
如果(!t.isContainerNode())
返回t.hashCode();
/*
*下面的散列计算确实有效,但充其量也很糟糕。
*而且可能也很慢。
*
*TODO:试着从番石榴中找出那些散列类
*/
int-ret=0;
/*
*如果容器是空的,只需返回即可
*/
如果(t.size()==0)
返回ret;
/*
*排列
*/
if(t.isArray()){
for(最终JsonNode元素:t)
ret=31*ret+doHash(元素);
返回ret;
}
/*
*不是数组?是对象。
*/
最终迭代器迭代器=t.fields();
地图。入口;
while(iterator.hasNext()){
entry=iterator.next();
ret=31*ret
+(entry.getKey().hashCode()^doHash(entry.getValue());
}
返回ret;
}
私有布尔数组相等(最终JsonNode a,最终JsonNode b)
{
/*
*我们在此保证阵列大小相同。
*/
最终整数大小=a.大小();
对于(int i=0;i