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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2253750/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 05:16:38  来源:igfitidea点击:

Testing two JSON objects for equality ignoring child order in Java

javajsonjunit

提问by Jeff

I'm looking for a JSON parsing library that supports comparing two JSON objects ignoring child order, specifically for unit testing JSON returning from a web service.

我正在寻找一个 JSON 解析库,它支持比较两个 JSON 对象而忽略子顺序,专门用于单元测试从 Web 服务返回的 JSON。

Do any of the major JSON libraries support this? The org.json library simply does a reference comparison.

是否有任何主要的 JSON 库支持这一点?org.json 库只是做一个引用比较。

采纳答案by Jolly Roger

As a general architectural point, I usually advise against letting dependencies on a particular serialization format bleed out beyond your storage/networking layer; thus, I'd first recommend that you consider testing equality between your own application objects rather than their JSON manifestations.

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

Having said that, I'm currently a big fan of Hymansonwhich my quick read of their ObjectNode.equals()implementation suggests does the set membership comparison that you want:

话虽如此,我目前是Hymanson 的忠实粉丝,我对他们的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;
}

回答by Yoni

I'd take the library at http://json.org/java/, and modify the equalsmethod of JSONObject and JSONArray to do a deep equality test. To make sure that it works regradless of the order of the children, all you need to do is replace the inner map with a TreeMap, or use something like Collections.sort().

我会使用http://json.org/java/ 上的库,并修改equalsJSONObject 和 JSONArray的方法来进行深度相等测试。为了确保它的工作不受子级顺序的影响,您需要做的就是用 a 替换内部映射TreeMap,或者使用类似Collections.sort().

回答by hertzsprung

You could try using json-lib's JSONAssertclass:

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

JSONAssert.assertEquals(
  "{foo: 'bar', baz: 'qux'}",
  JSONObject.fromObject("{foo: 'bar', baz: 'xyzzy'}")
);

Gives:

给出:

junit.framework.ComparisonFailure: objects differed at key [baz]; expected:<[qux]> but was:<[xyzzy]>

回答by Claudio Aguiar

One thing I did and it works wonders is to read both objects into HashMap and then compare with a regular assertEquals(). It will call the equals() method of the hashmaps, which will recursively compare all objects inside (they will be either other hashmaps or some single value object like a string or integer). This was done using Codehaus' Hymanson JSON parser.

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

assertEquals(mapper.readValue(expectedJson, new TypeReference<HashMap<String, Object>>(){}), mapper.readValue(actualJson, new TypeReference<HashMap<String, Object>>(){}));

A similar approach can be used if the JSON object is an array instead.

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

回答by axelhzf

Using GSON

使用 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);

Edit: Since GSON v2.8.6the instance method JsonParser.parseis deprecated. You have to use the static method JsonParser.parseString:

编辑:自GSON v2.8.6 起JsonParser.parse不推荐使用实例方法。您必须使用静态方法JsonParser.parseString

JsonElement o1 = JsonParser.parseString("{a : {a : 2}, b : 2}");
JsonElement o2 = JsonParser.parseString("{b : 2, a : {a : 2}}");
assertEquals(o1, o2);

回答by Lukas

You can try JsonUnit. It can compare two JSON objects and report differences. It's built on top of Hymanson.

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

For example

例如

assertJsonEquals("{\"test\":1}", "{\n\"test\": 2\n}");

Results in

结果是

java.lang.AssertionError: JSON documents are different:
Different value found in node "test". Expected 1, got 2.

回答by Carter Page

Try Skyscreamer's JSONAssert.

试试 Skyscreamer 的JSONAssert

Its non-strictmode has two major advantages that make it less brittle:

它的非严格模式有两个主要优点,使其不那么脆弱:

  • Object extensibility (e.g. With an expected value of {id:1}, this would still pass: {id:1,moredata:'x'}.)
  • Loose array ordering (e.g. ['dog','cat']==['cat','dog'])
  • 对象可扩展性(例如,如果预期值为{id:1},这仍然会通过:{id:1,moredata:'x'}。)
  • 松散的数组排序(例如 ['dog','cat']==['cat','dog'])

In strict mode it behaves more like json-lib's test class.

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

A test looks something like this:

一个测试看起来像这样:

@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);
}

The parameters in the JSONAssert.assertEquals() call are expectedJSONString, actualDataString, and isStrict.

JSONAssert.assertEquals() 调用中的参数是expectedJSONStringactualDataStringisStrict

The result messages are pretty clear, which is important when comparing really big JSON objects.

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

回答by JLouis

This Solution for me, work's very good:

这个解决方案对我来说,工作非常好:

try {           
                // Getting The Array "Courses" from json1 & json2   
                Courses1 =json1.getJSONArray(TAG_COURSES1);
                Courses2 = json2.getJSONArray(TAG_COURSES);

                //LOOP FOR JSON1
                for(int i = 0; i < Courses1.length(); i++){
                    //LOOP FOR JSON2
                    for(int ii = 0; ii < Courses2.length(); ii++){
                        JSONObject courses1 = Courses1.getJSONObject(i);
                        JSONObject courses2 = Courses2.getJSONObject(ii);

                        // Storing each json1 item in variable
                        int courseID1 = courses1.getInt(TAG_COURSEID1);
                        Log.e("COURSEID2:", Integer.toString(courseID1));
                        String Rating1 = courses1.getString(TAG_RATING1);
                        int Status1 = courses1.getInt(TAG_STATUS1);
                        Log.e("Status1:", Integer.toString(Status1));      //Put the actual value for Status1 in log.             

                        // Storing each json2 item in variable
                        int courseID2 = courses2.getInt(TAG_COURSEID);
                        Log.e("COURSEID2:", Integer.toString(courseID));   //Put the actual value for CourseID in log
                        String Title2 = courses2.getString(TAG_TITLE);                      
                        String instructor2 = courses2.getString(TAG_INSTRUCTOR);
                        String length2 = courses2.getString(TAG_LENGTH);
                        String rating2 = courses2.getString(TAG_RATING);
                        String subject2 = courses2.getString(TAG_SUBJECT);
                        String description2 = courses2.getString(TAG_DESCRIPTION);

                        //Status1 = 5 from json1; Incomplete, Status1 =-1 Complete 
                        if(Status1 == 5 && courseID2 == courseID1){                                  

                        // creating new HashMap
                        HashMap<String, String> map = new HashMap<String, String>();         
                        //Storing the elements if condition is true.
                        map.put(TAG_COURSEID, Integer.toString(courseID2)); //pend for compare
                        map.put(TAG_TITLE, Title2);
                        map.put(TAG_INSTRUCTOR, instructor2);
                        map.put(TAG_LENGTH, length2);
                        map.put(TAG_RATING, rating2);
                        map.put(TAG_SUBJECT, subject2); //show it
                        map.put(TAG_DESCRIPTION, description2);

                        //adding HashList to ArrayList
                        contactList.add(map);
                        }//if
                    }//for2 (json2)
                } //for1 (json1)                
            }//Try

Hope this help others.

希望这对其他人有帮助。

回答by Victor Ionescu

For org.json I've rolled out my own solution, a method that compares to JSONObject instances. I didn't work with complex JSON objects in that project, so I don't know whether this works in all scenarios. Also, given that I use this in unit tests, I didn't put effort into optimizations. Here it is:

对于 org.json,我推出了自己的解决方案,这是一种与 JSONObject 实例进行比较的方法。我没有在那个项目中使用复杂的 JSON 对象,所以我不知道这是否适用于所有场景。此外,考虑到我在单元测试中使用它,我没有在优化上付出努力。这里是:

public static boolean jsonObjsAreEqual (JSONObject js1, JSONObject js2) throws JSONException {
    if (js1 == null || js2 == null) {
        return (js1 == js2);
    }

    List<String> l1 =  Arrays.asList(JSONObject.getNames(js1));
    Collections.sort(l1);
    List<String> l2 =  Arrays.asList(JSONObject.getNames(js2));
    Collections.sort(l2);
    if (!l1.equals(l2)) {
        return false;
    }
    for (String key : l1) {
        Object val1 = js1.get(key);
        Object val2 = js2.get(key);
        if (val1 instanceof JSONObject) {
            if (!(val2 instanceof JSONObject)) {
                return false;
            }
            if (!jsonObjsAreEqual((JSONObject)val1, (JSONObject)val2)) {
                return false;
            }
        }

        if (val1 == null) {
            if (val2 != null) {
                return false;
            }
        }  else if (!val1.equals(val2)) {
            return false;
        }
    }
    return true;
}

回答by kevinarpe

If you are already using JUnit, the latest version now employs Hamcrest. It is a generic matching framework (especially useful for unit testing) that can be extended to build new matchers.

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

There is a small open source library called hamcrest-jsonwith JSON-aware matches. It is well documented, tested, and supported. Below are some useful links:

有一个hamcrest-json使用 JSON 感知匹配调用的小型开源库。它有很好的文档记录、测试和支持。以下是一些有用的链接:

Example code using objects from the JSON library org.json.simple:

使用来自 JSON 库的对象的示例代码org.json.simple

Assert.assertThat(
    jsonObject1.toJSONString(),
    SameJSONAs.sameJSONAs(jsonObject2.toJSONString()));

Optionally, you may (1) allow "any-order" arrays and (2) ignore extra fields.

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

Since there are a variety of JSON libraries for Java (Hymanson, GSON, json-lib, etc.), it is useful that hamcrest-jsonsupports JSON text (as java.lang.String), as well as natively supporting objects from Douglas Crockford's JSON library org.json.

由于 Java 有多种 JSON 库(HymansonGSONjson-lib等),因此hamcrest-json支持 JSON 文本 (as java.lang.String) 以及 Douglas Crockford 的 JSON 库中的本机支持对象非常有用org.json

Finally, if you are not using JUnit, you can use Hamcrest directly for assertions. (I wrote about it here.)

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