测试两个 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
Testing two JSON objects for equality ignoring child order in Java
提问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 equals
method 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/ 上的库,并修改equals
JSONObject 和 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.parse
is 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() 调用中的参数是expectedJSONString、actualDataString和isStrict。
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-json
with 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-json
supports JSON text (as java.lang.String
), as well as natively supporting objects from Douglas Crockford's JSON library org.json
.
由于 Java 有多种 JSON 库(Hymanson
、GSON
、json-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 进行断言。(我在这里写过。)