Java 比较两个JSON对象的数字容差
我目前正在使用Jackson JSON库来比较几个JSON对象 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()做了一个非常好的深度相等,但它比较数字的方式迫使它们完全相等。我需要增加一个容忍
{
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