比较Java中的两个JSON对象
我正在寻找一个JSONparsing库,支持比较两个JSON对象,忽略子订单,特别是unit testing从Web服务返回的JSON。
任何主要的JSON库都支持这个吗? org.json库只是做参考比较。
作为一个通用的架构点,我通常build议不要让依赖于特定的序列化格式stream出存储/networking层; 因此,我首先build议您考虑testing自己的应用程序对象之间的平等,而不是他们的JSONperformance。
话虽如此,我现在是jackson的忠实粉丝,我快速阅读了他们的ObjectNode.equals()实现build议做你想要的设置成员比较:
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; }
试试Skyscreamer的JSONAssert 。
它的非严格模式有两个主要优点,使它不脆:
- 对象可扩展性(例如,对于期望值{id:1} ,这仍然会传递: {id:1,moredata:'x'} 。)
- 松散的数组sorting(例如['狗','猫'] == ['猫','狗'])
在严格模式下,它的行为更像是json-lib的testing类。
一个testing看起来像这样:
@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); }
JSONAssert.assertEquals()调用中的参数预期为 JSONString , actualDataString和isStrict 。
结果消息非常清晰,这在比较真正的大JSON对象时非常重要。
使用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);
你可以尝试使用json-lib的JSONAssert类:
JSONAssert.assertEquals( "{foo: 'bar', baz: 'qux'}", JSONObject.fromObject("{foo: 'bar', baz: 'xyzzy'}"));
得到:
junit.framework.ComparisonFailure: objects differed at key [baz]; expected:<[qux]> but was:<[xyzzy]>
我会做以下事情,
final JSONObject obj1 = /*json*/; final JSONObject obj2 = /*json*/; final ObjectMapper mapper = new ObjectMapper(); final JsonNode tree1 = mapper.readTree(obj1.toString()); final JsonNode tree2 = mapper.readTree(obj2.toString()); return tree1.equals(tree2);
如果您已经在使用JUnit,最新版本现在使用Hamcrest。 这是一个通用的匹配框架(对于unit testing特别有用),可以扩展来构build新的匹配器。
有一个叫做hamcrest-json
的小型开源库,带有JSON意识的匹配。 这是有据可查的,testing和支持。 以下是一些有用的链接:
- 源代码
- 主页
- 主匹配器的Javadocs :
使用JSON库org.json.simple
对象的示例代码:
Assert.assertThat( jsonObject1.toJSONString(), SameJSONAs.sameJSONAs(jsonObject2.toJSONString()));
或者,您可以(1)允许“任意顺序”数组和(2)忽略额外的字段。
由于Java有各种各样的JSON库( Jackson
, GSON
, json-lib
等),因此hamcrest-json
支持JSON文本(如java.lang.String
),以及本地支持Douglas对象Crockford的JSON库org.json
。
最后,如果你不使用JUnit,你可以直接使用Hamcrest进行断言。 ( 我在这里写过 )
你可以试试JsonUnit 。 它可以比较两个JSON对象并报告差异。 它build立在jackson之上。
例如
assertJsonEquals("{\"test\":1}", "{\n\"test\": 2\n}");
结果是
java.lang.AssertionError: JSON documents are different: Different value found in node "test". Expected 1, got 2.
使用这个库: 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>
IGNORING_ARRAY_ORDER – 忽略数组中的顺序
assertJsonEquals("{\"test\":[1,2,3]}", "{\"test\": [3,2,1]}",when(IGNORING_ARRAY_ORDER));
我做的一件事情,它奇迹般地是把这两个对象读入HashMap,然后与一个常规的assertEquals()进行比较。 它将调用hashmaps的equals()方法,它将recursion地比较内部的所有对象(它们将是其他hashmaps或像string或整数一样的单个值对象)。 这是使用Codehaus的Jackson JSONparsing器完成的。
assertEquals(mapper.readValue(expectedJson, new TypeReference<HashMap<String, Object>>(){}), mapper.readValue(actualJson, new TypeReference<HashMap<String, Object>>(){}));
如果JSON对象是一个数组,则可以使用类似的方法。
对于org.json,我已经推出了我自己的解决scheme,一种与JSONObject实例进行比较的方法。 我没有在该项目中使用复杂的JSON对象,所以我不知道这是否适用于所有情况。 另外,考虑到我在unit testing中使用这个,我没有把精力放在优化上。 这里是:
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; }
我将采取在http://json.org/java/的图书馆,并修改JSONObject和JSONArray的;equals
方法做一个深入的平等testing。 为了确保它能正常工作,您只需要用TreeMap
replace内部地图,或者使用类似Collections.sort()
东西。
我正在使用这个,并为我工作正常(与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; } } }
尝试这个:
public static boolean jsonsEqual(Object obj1, Object obj2) throws JSONException { if (!obj1.getClass().equals(obj2.getClass())) { return false; } if (obj1 instanceof JSONObject) { JSONObject jsonObj1 = (JSONObject) obj1; JSONObject jsonObj2 = (JSONObject) obj2; String[] names = JSONObject.getNames(jsonObj1); String[] names2 = JSONObject.getNames(jsonObj1); if (names.length != names2.length) { return false; } for (String fieldName:names) { Object obj1FieldValue = jsonObj1.get(fieldName); Object obj2FieldValue = jsonObj2.get(fieldName); if (!jsonsEqual(obj1FieldValue, obj2FieldValue)) { return false; } } } else if (obj1 instanceof JSONArray) { JSONArray obj1Array = (JSONArray) obj1; JSONArray obj2Array = (JSONArray) obj2; if (obj1Array.length() != obj2Array.length()) { return false; } for (int i = 0; i < obj1Array.length(); i++) { boolean matchFound = false; for (int j = 0; j < obj2Array.length(); j++) { if (jsonsEqual(obj1Array.get(i), obj2Array.get(j))) { matchFound = true; break; } } if (!matchFound) { return false; } } } else { if (!obj1.equals(obj2)) { return false; } } return true; }
您可以使用zjsonpatch库,该库根据RFC 6902(JSON修补程序)提供diff信息。 它非常容易使用。 请访问其说明页面的使用情况
我知道它通常只用于testing,但是可以使用Hamcrest JSON中的Hamcrest JSON 比较器 SameJSONAs。
Hamcrest JSON SameJSONAs
对于像我这样想和jackson做这个的人,可以使用json-unit 。
JsonAssert.assertJsonEquals(jsonNode1, jsonNode2);
错误给出了不匹配types的有用反馈:
java.lang.AssertionError: JSON documents have different values: Different value found in node "heading.content[0].tag[0]". Expected 10209, got 10206.
空手道正是你正在寻找的。 这里是一个例子:
* def myJson = { foo: 'world', hey: 'ho', zee: [5], cat: { name: 'Billie' } } * match myJson = { cat: { name: 'Billie' }, hey: 'ho', foo: 'world', zee: [5] }
(免责声明:dev在这里)
jsonObject实现一个可比的接口,尝试使用collection.sort()。
这个解决scheme对我来说,工作非常好:
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
希望这可以帮助别人。