测试两个JSON对象是否相等,忽略Java中的子顺序

Jef*_*eff 223 java junit json

我正在寻找一个JSON解析库,它支持比较忽略子命令的两个JSON对象,特别是用于测试从Web服务返回的JSON的单元测试.

任何主要的JSON库都支持这个吗?org.json库只是进行参考比较.

Car*_*age 145

试试Skyscreamer的JSONAssert.

它的非严格模式有两个主要优点,使其不易碎:

  • 对象可扩展性(例如,期望值为{id:1},这仍然会通过:{id:1,moredata:'x'}.)
  • 松散的数组排序(例如['dog','cat'] == ['cat','dog'])

在严格模式下,它的行为更像json-lib的测试类.

测试看起来像这样:

@Test
public void testGetFriends() {
    JSONObject data = getRESTData("/friends/367.json");
    String expected = "{friends:[{id:123,name:\"Corby Page\"}"
        + ",{id:456,name:\"Solomon Duskis\"}]}";
    JSONAssert.assertEquals(expected, data, false);
}
Run Code Online (Sandbox Code Playgroud)

JSONAssert.assertEquals()调用中的参数是expectedJSONString,actualDataStringisStrict.

结果消息非常清楚,这在比较非常大的JSON对象时很重要.

  • 我一直在使用这个解决方案,但我刚刚发现你也可以提供你选择的JSONCompareMode.其中一个是NON_EXTENSIBLE.所以你有这样的东西:`JSONAssert.assertEquals(expected,data,JSONCompareMode.NON_EXTENSIBLE);`NON_EXTENSIBLE模式意味着任何新的或缺少的字段都会导致失败,但顺序却没有.使用false将启动宽松模式,该模式不会报告任何额外或丢失的子元素. (15认同)
  • JSONAssert问题#44修复了PR 67中的洁净室库替换,今天发布为JSONAssert 1.4.0. (3认同)
  • 在我兴奋之前:这是否也支持嵌套的 JSON 对象和数组?:) (2认同)
  • 对.包含对象的对象包含包含对象的数组等. (2认同)
  • 人们可能希望在使用库之前考虑[此JSONassert问题](https://github.com/skyscreamer/JSONassert/issues/44):它表明目前存在与库的依赖关系的潜在许可问题. (2认同)

Jol*_*ger 79

作为一般的架构点,我通常建议不要让特定序列化格式的依赖性超出存储/网络层; 因此,我首先建议您考虑测试您自己的应用程序对象之间的相等性,而不是它们的JSON表现形式.

话虽如此,我现在是杰克逊的忠实粉丝,我快速阅读他们的ObjectNode.equals()实现建议您进行所需的集合成员资格比较:

public boolean equals(Object o)
{
    if (o == this) return true;
    if (o == null) return false;
    if (o.getClass() != getClass()) {
        return false;
    }
    ObjectNode other = (ObjectNode) o;
    if (other.size() != size()) {
        return false;
    }
    if (_children != null) {
        for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
            String key = en.getKey();
            JsonNode value = en.getValue();

            JsonNode otherValue = other.get(key);

            if (otherValue == null || !otherValue.equals(value)) {
                return false;
            }
        }
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

  • @Yoni:不对,因为有尺寸比较.他们必须拥有完全相同数量的孩子以及相同的孩子.@Jolly Roger:在这种情况下,我没有将JSON中的对象序列化为POJO,但是当发送JSON系统时,我不能依赖它以我发送的完全相同的格式发回它它. (22认同)

axe*_*hzf 44

使用GSON

JsonParser parser = new JsonParser();
JsonElement o1 = parser.parse("{a : {a : 2}, b : 2}");
JsonElement o2 = parser.parse("{b : 2, a : {a : 2}}");
assertEquals(o1, o2);
Run Code Online (Sandbox Code Playgroud)

  • 如果 jsonArray 元素不按顺序,它就不起作用。版本 2.8.2 (2认同)

jos*_*osh 29

我会做以下,

JSONObject obj1 = /*json*/;
JSONObject obj2 = /*json*/;

ObjectMapper mapper = new ObjectMapper();

JsonNode tree1 = mapper.readTree(obj1.toString());
JsonNode tree2 = mapper.readTree(obj2.toString());

return tree1.equals(tree2);
Run Code Online (Sandbox Code Playgroud)

  • 但我认为这是一个严格的比较.对于具有不同元素顺序的两个相等的json,它将返回false. (17认同)
  • 当您将数组作为嵌套对象并且数组中的顺序存在差异时,这不起作用 (3认同)
  • 这将忽略大多数顺序更改,但列表顺序更改必须相同。请参阅此处:https://www.baeldung.com/jackson-compare-two-json-objects (3认同)
  • @deadpool,可能是严格的比较,但现在不严格。 (2认同)

her*_*ung 16

您可以尝试使用json-lib的JSONAssert类:

JSONAssert.assertEquals(
  "{foo: 'bar', baz: 'qux'}",
  JSONObject.fromObject("{foo: 'bar', baz: 'xyzzy'}")
);
Run Code Online (Sandbox Code Playgroud)

得到:

junit.framework.ComparisonFailure: objects differed at key [baz]; expected:<[qux]> but was:<[xyzzy]>
Run Code Online (Sandbox Code Playgroud)

  • 如果此解决方案适用于JSON中的数据项的顺序,则如果数组中的元素顺序不匹配,则它将失败.例如,如果您的代码使用转换为JSON的Set.以下JSON比较将失败:`JSONAssert.assertJsonEquals("{foo:'bar',list:[{test:'1'},{rest:'2'}]}","{foo:'bar', list:[{rest:'2'},{test:'1'}]}");`带有消息:`junit.framework.AssertionFailedError :::对象[list]的对象不同;:数组首先不同于element [0] ;:对象[test];`的对象不同 (2认同)

小智 13

使用此库:https://github.com/lukas-krecan/JsonUnit

双响炮:

<dependency>
    <groupId>net.javacrumbs.json-unit</groupId>
    <artifactId>json-unit</artifactId>
    <version>1.5.0</version>
    <scope>test</scope>
</dependency>
Run Code Online (Sandbox Code Playgroud)

IGNORING_ARRAY_ORDER - 忽略数组中的顺序

assertJsonEquals("{\"test\":[1,2,3]}",
  "{\"test\":  [3,2,1]}",
  when(IGNORING_ARRAY_ORDER)
);
Run Code Online (Sandbox Code Playgroud)


kev*_*rpe 12

如果您已经在使用JUnit,最新版本现在使用Hamcrest.它是一个通用的匹配框架(特别适用于单元测试),可以扩展为构建新的匹配器.

有一个叫做hamcrest-jsonJSON感知匹配的小型开源库.它有详细记录,测试和支持.以下是一些有用的链接:

使用JSON库中的对象的示例代码org.json.simple:

Assert.assertThat(
    jsonObject1.toJSONString(),
    SameJSONAs.sameJSONAs(jsonObject2.toJSONString()));
Run Code Online (Sandbox Code Playgroud)

或者,您可以(1)允许"任意顺序"数组和(2)忽略额外字段.

由于有对Java(各种JSON库Jackson,GSON,json-lib等),它是有用的hamcrest-json支持JSON文本(如java.lang.String),以及原生支持从道格拉斯Crockford的JSON库对象org.json.

最后,如果您不使用JUnit,则可以直接使用Hamcrest进行断言.(我在这里写过.)

  • @Johannes:仅限风格. (2认同)

Luk*_*kas 11

你可以尝试JsonUnit.它可以比较两个JSON对象并报告差异.它建立在杰克逊之上.

例如

assertJsonEquals("{\"test\":1}", "{\n\"test\": 2\n}");
Run Code Online (Sandbox Code Playgroud)

结果是

java.lang.AssertionError: JSON documents are different:
Different value found in node "test". Expected 1, got 2.
Run Code Online (Sandbox Code Playgroud)


小智 6

我做过的一件事就是将两个对象都读入HashMap,然后与常规的assertEquals()进行比较.它将调用hashmaps的equals()方法,它将递归地比较内部的所有对象(它们将是其他哈希映射或某个单值对象,如字符串或整数).这是使用Codehaus的Jackson JSON解析器完成的.

assertEquals(mapper.readValue(expectedJson, new TypeReference<HashMap<String, Object>>(){}), mapper.readValue(actualJson, new TypeReference<HashMap<String, Object>>(){}));
Run Code Online (Sandbox Code Playgroud)

如果JSON对象是一个数组,则可以使用类似的方法.


小智 5

我正在使用它,并且对我来说很好(使用org.json。*):

package com.project1.helpers;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class JSONUtils {

    public static boolean areEqual(Object ob1, Object ob2) throws JSONException {
        Object obj1Converted = convertJsonElement(ob1);
        Object obj2Converted = convertJsonElement(ob2);
        return obj1Converted.equals(obj2Converted);
    }

    private static Object convertJsonElement(Object elem) throws JSONException {
        if (elem instanceof JSONObject) {
            JSONObject obj = (JSONObject) elem;
            Iterator<String> keys = obj.keys();
            Map<String, Object> jsonMap = new HashMap<>();
            while (keys.hasNext()) {
                String key = keys.next();
                jsonMap.put(key, convertJsonElement(obj.get(key)));
            }
            return jsonMap;
        } else if (elem instanceof JSONArray) {
            JSONArray arr = (JSONArray) elem;
            Set<Object> jsonSet = new HashSet<>();
            for (int i = 0; i < arr.length(); i++) {
                jsonSet.add(convertJsonElement(arr.get(i)));
            }
            return jsonSet;
        } else {
            return elem;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)