Jackson比较两个Json对象

777 阅读4分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。

作者:汤圆

个人博客:javalover.cc

前言

这里的JSON对象指的就是JsonNode对象,它有点类似HTML中的DOM节点;

有时候我们解析Json字符串,不知道具体的解析对象,可以将其解析为JsonNode对象,然后通过asText()等方法取出对应的值;

今天我们要讨论的就是如何比较两个JsonNode对象;

目录

  1. 比较00.0
  2. 比较nameNAME
  3. 比较嵌套的对象
  4. 比较嵌套的集合

正文

1. 比较00.0

a. 默认的比较方式:

比如有下面的两个Json字符串:

String s1 = "{\"name\":\"jalon\", \"weight\": 100}";
String s2 = "{\"name\":\"jalon\", \"weight\": 100.0}";

可以看到,一个体重100,一个体重100.0,严格意义来讲,他俩是不相等的;

通过JsonNode.equals()方法进行比较,代码如下所示:

ObjectMapper objectMapper = new ObjectMapper();

JsonNode jsonNode = objectMapper.readTree(s1);
JsonNode jsonNode2 = objectMapper.readTree(s2);
System.out.println(jsonNode.equals(jsonNode2));

此时输出为:false;符合预期

可以看到,默认的equals比较,会把100和100.0当作不同的数值进行对待;

但是有时候我们不希望看到这样的结果,我们希望的是100==100.0,这时就需要用到自定义的比较器;

b. 自定义的比较器:

这时我们就可以自己定义一个简易的比较器,专门用来处理这种0!=0.0的情况;

大致的思路就是向上转型,将0转为高精度的0.0,然后进行比较;这样可以保留数据的精度;

比较器如下所示:

public class NumericNodeComparator implements Comparator<JsonNode>
{
    @Override
    public int compare(JsonNode o1, JsonNode o2)
    {
        if (o1.equals(o2)){
            return 0;
        }
        if ((o1 instanceof NumericNode) && (o2 instanceof NumericNode)){
            Double d1 = o1.asDouble();
            Double d2 = o2.asDouble();
            if (d1.compareTo(d2) == 0) {
                return 0;
            }
        }
        return 1;
    }
}

这里面的NumericNode是JsonNode的一个字类,代表数值类型的Json对象;

下面我们用这个自定义的比较器进行比较,代码如下:

NumericNodeComparator comparator = new NumericNodeComparator();
System.out.println(jsonNode.equals(comparator, jsonNode2));

此时输出为true,符合新的预期;

2. 比较nameNAME

a. 默认的比较方式:

Java中的String类型进行比较时,可以选择是否忽略大小写,如下所示:

image-20220218164145976

但是JsonNode并没有提供这种方式,JsonNode对字符串的相等处理,默认的方式是关心大小写,既大小写不一致,则不相等;

比如有下面的两条数据:

String s3 = "{\"name\":\"jalon\"}";
String s4 = "{\"name\":\"Jalon\"}";

默认的equals会认为这俩name是不相等的:

boolean equals = objectMapper.readTree(s3).equals(objectMapper.readTree(s4));
System.out.println(equals);

输出false;

b. 自定义的比较器:

我们可以跟上面的0和0.0一样,自己定义一个字符串的比较器,然后内部进行对比时,用String.equalsIgnoreCase()来忽略大小写;

自定义的比较器如下所示:

public class TextNodeComparator implements Comparator<JsonNode>
{
    @Override
    public int compare(JsonNode o1, JsonNode o2) {
        if (o1.equals(o2)) {
            return 0;
        }
        if ((o1 instanceof TextNode) && (o2 instanceof TextNode)) {
            String s1 = o1.asText();
            String s2 = o2.asText();
            if (s1.equalsIgnoreCase(s2)) {
                return 0;
            }
        }
        return 1;
    }
}

测试代码如下:

TextNodeComparator comparator1 = new TextNodeComparator();
boolean equals2 = objectMapper.readTree(s3).equals(comparator1, objectMapper.readTree(s4));
System.out.println(equals2);

输出true,符合新的预期;

3. 比较嵌套的对象

很多时候,我们在操作Json数据都会遇到嵌套多个对象的情况,如下所示:里面嵌套了friend对象;

String s6 = "{\"name\":\"jalon\", \"friend\": {\"name\":\"xiao\"}}";

关于嵌套的对象,我们可以通过一个简单的例子来看下,会不会影响Json对象的比较;

这里我们再创建两个Json字符串,且都嵌套一个对象:

String s6 = "{\"name\":\"jalon\", \"friend\": {\"name\":\"xiao\"}}";
String s7 = "{\"name\":\"jalon\", \"friend\": {\"name\":\"da\"}}";

然后用JsonNode默认的比较器进行比较:

JsonNode jsonNode6 = objectMapper.readTree(s6);
JsonNode jsonNode7 = objectMapper.readTree(s7);
System.out.println(jsonNode6.equals(jsonNode7));

输出false,说明JsonNode对象支持嵌套对象的比较;

4. 比较嵌套的集合

如果有嵌套的集合在Json对象中,是否也可以进行比较操作呢?

按照上面嵌套对象的逻辑来看,应该是可以的,下面我们看下例子:

String s8 = "{\"name\":\"jalon\", \"girlFriend\": [{\"name\":\"xiaomei\"}]}";
String s9 = "{\"name\":\"jalon\", \"girlFriend\": [{\"name\":\"lili\"}]}";
JsonNode jsonNode8 = objectMapper.readTree(s8);
JsonNode jsonNode9 = objectMapper.readTree(s9);
System.out.println(jsonNode8.equals(jsonNode9));	

输出false,说明嵌套的集合也是可以进行比较的;

总结

Json对象的比较器,默认行为有:

  1. 比较0和0.0时,false,会关注数据精度;

  2. 比较name和Name,false,会关注大小写;

  3. 会比较嵌套的对象;

  4. 会比较嵌套的集合;

如果想要修改比较器的默认行为,可以自己实现一个比较器,然后在执行equals方法时传进去;