特にWebサービスから返されるJSONのユニットテストのために、子の順序を無視して2つのJSONオブジェクトの比較をサポートするJSON解析ライブラリを探しています。
主要なJSONライブラリのいずれかがこれをサポートしていますか?org.jsonライブラリは単に参照比較を行います。
特にWebサービスから返されるJSONのユニットテストのために、子の順序を無視して2つのJSONオブジェクトの比較をサポートするJSON解析ライブラリを探しています。
主要なJSONライブラリのいずれかがこれをサポートしていますか?org.jsonライブラリは単に参照比較を行います。
回答:
一般的なアーキテクチャのポイントとして、私は通常、特定のシリアル化フォーマットへの依存をストレージ/ネットワーク層を超えて流出させないことをお勧めします。したがって、最初に、JSONマニフェストではなく、独自のアプリケーションオブジェクト間の同等性をテストすることを検討することをお勧めします。
そうは言っても、私は現在、Jacksonの大ファンです。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;
}
SkyscreamerのJSONAssertをお試しください。
その非厳密モードでは、あまり脆く二つの主要な利点があります。
strictモードでは、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);
}
JSONAssert.assertEquals()呼び出しのパラメーターは、expectedJSONString、actualDataString、およびisStrictです。
結果メッセージはかなり明確です。これは、非常に大きなJSONオブジェクトを比較するときに重要です。
JSONAssert.assertEquals(expected, data, JSONCompareMode.NON_EXTENSIBLE);
なります。NON_EXTENSIBLEモードでは、新しいフィールドや欠落しているフィールドが原因でエラーが発生しますが、順序は原因ではありません。falseを使用すると、余分な要素や欠落している子要素を報告しない寛大なモードが開始されます。
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);
編集: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);
私は次のようにします
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);
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]>
JSONAssert.assertJsonEquals( "{foo: 'bar', list: [{test: '1'}, {rest: '2'}] }", "{ foo: 'bar', list: [{rest: '2'}, {test: '1'}] }");
します:メッセージ:junit.framework.AssertionFailedError: : : objects differed at key [list];: arrays first differed at element [0];: objects differed at key [test];
このライブラリを使用: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)
);
すでにJUnitを使用している場合、最新バージョンではHamcrestが採用されています。これは、新しいマッチャーを構築するために拡張できる汎用マッチングフレームワーク(特にユニットテストに役立ちます)です。
hamcrest-json
JSON対応の一致で呼び出される小さなオープンソースライブラリがあります。十分に文書化され、テストされ、サポートされています。以下はいくつかの便利なリンクです:
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
)、ダグラス・クロックフォードのJSONライブラリからだけでなく、ネイティブにサポートするオブジェクトorg.json
。
最後に、JUnitを使用していない場合は、アサーションに直接Hamcrestを使用できます。(私はそれについてここに書いた。)
両方のオブジェクトをHashMapに読み込んでから、通常のassertEquals()と比較することで、うまくいきました。ハッシュマップのequals()メソッドを呼び出し、内部のすべてのオブジェクトを再帰的に比較します(他のハッシュマップか、文字列や整数などの単一の値のオブジェクトのいずれかになります)。これは、CodehausのJackson JSONパーサーを使用して行われました。
assertEquals(mapper.readValue(expectedJson, new TypeReference<HashMap<String, Object>>(){}), mapper.readValue(actualJson, new TypeReference<HashMap<String, Object>>(){}));
JSONオブジェクトが配列の場合も、同様のアプローチを使用できます。
私はこれを使用しており、(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;
}
}
}
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;
}
val1
nullの場合、このコードからNullPointerExceptionが発生しますif (!val1.equals(val2)) {
js2
ですnull
か、ないときjs1
ではありませんnull
RFC 6902(JSONパッチ)に準拠した差分情報を表示するzjsonpatchライブラリを使用できます。とても使いやすいです。使い方については説明ページをご覧ください
ライブラリをhttp://json.org/java/でequals
取得し、JSONObjectとJSONArrayのメソッドを変更して詳細な等価テストを実行します。子の順序に関係なく動作することを確認するには、内部マップをに置き換えるTreeMap
か、などを使用するだけですCollections.sort()
。
これを試して:
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;
}
if (obj1Array.length() != obj2Array.length())
することを保証しませんか?
私は、それが通常はテストのために考えられている知っていますが、Hamcrest JSON使用することができcomparitorSameJSONAsを Hamcrest JSONで。
JSONの比較には、JSONCompareの使用をお勧めします:https : //github.com/fslev/json-compare
// Compare by regex
String expected = "{\"a\":\".*me.*\"}";
String actual = "{\"a\":\"some text\"}";
JSONCompare.assertEquals(expected, actual); // True
// Check expected array has no extra elements
String expected = "[1,\"test\",4,\"!.*\"]";
String actual = "[4,1,\"test\"]";
JSONCompare.assertEquals(expected, actual); // True
// Check expected array has no numbers
String expected = "[\"\\\\\\d+\"]";
String actual = "[\"text\",\"test\"]";
JSONCompare.assertEquals(expected, actual); // True
// Check expected array has no numbers
String expected = "[\"\\\\\\d+\"]";
String actual = "[2018]";
JSONCompare.assertNotEquals(expected, actual); // True
私のようなジャクソンでこれをしたい人のために、あなたはjson-unitを使うことができます。
JsonAssert.assertJsonEquals(jsonNode1, jsonNode2);
エラーは、不一致のタイプに関する有用なフィードバックを提供します。
java.lang.AssertionError: JSON documents have different values:
Different value found in node "heading.content[0].tag[0]". Expected 10209, got 10206.
他には何も問題なく動作するように思われたので、私はこれを書きました:
private boolean jsonEquals(JsonNode actualJson, JsonNode expectJson) {
if(actualJson.getNodeType() != expectJson.getNodeType()) return false;
switch(expectJson.getNodeType()) {
case NUMBER:
return actualJson.asDouble() == expectJson.asDouble();
case STRING:
case BOOLEAN:
return actualJson.asText().equals(expectJson.asText());
case OBJECT:
if(actualJson.size() != expectJson.size()) return false;
Iterator<String> fieldIterator = actualJson.fieldNames();
while(fieldIterator.hasNext()) {
String fieldName = fieldIterator.next();
if(!jsonEquals(actualJson.get(fieldName), expectJson.get(fieldName))) {
return false;
}
}
break;
case ARRAY:
if(actualJson.size() != expectJson.size()) return false;
List<JsonNode> remaining = new ArrayList<>();
expectJson.forEach(remaining::add);
// O(N^2)
for(int i=0; i < actualJson.size(); ++i) {
boolean oneEquals = false;
for(int j=0; j < remaining.size(); ++j) {
if(jsonEquals(actualJson.get(i), remaining.get(j))) {
oneEquals = true;
remaining.remove(j);
break;
}
}
if(!oneEquals) return false;
}
break;
default:
throw new IllegalStateException();
}
return true;
}
次のコードは、2つのJsonObject、JsonArray、JsonPrimitive、およびJasonElementsを比較するのに役立ちます。
private boolean compareJson(JsonElement json1, JsonElement json2) {
boolean isEqual = true;
// Check whether both jsonElement are not null
if (json1 != null && json2 != null) {
// Check whether both jsonElement are objects
if (json1.isJsonObject() && json2.isJsonObject()) {
Set<Entry<String, JsonElement>> ens1 = ((JsonObject) json1).entrySet();
Set<Entry<String, JsonElement>> ens2 = ((JsonObject) json2).entrySet();
JsonObject json2obj = (JsonObject) json2;
if (ens1 != null && ens2 != null) {
// (ens2.size() == ens1.size())
// Iterate JSON Elements with Key values
for (Entry<String, JsonElement> en : ens1) {
isEqual = isEqual && compareJson(en.getValue(), json2obj.get(en.getKey()));
}
} else {
return false;
}
}
// Check whether both jsonElement are arrays
else if (json1.isJsonArray() && json2.isJsonArray()) {
JsonArray jarr1 = json1.getAsJsonArray();
JsonArray jarr2 = json2.getAsJsonArray();
if (jarr1.size() != jarr2.size()) {
return false;
} else {
int i = 0;
// Iterate JSON Array to JSON Elements
for (JsonElement je : jarr1) {
isEqual = isEqual && compareJson(je, jarr2.get(i));
i++;
}
}
}
// Check whether both jsonElement are null
else if (json1.isJsonNull() && json2.isJsonNull()) {
return true;
}
// Check whether both jsonElement are primitives
else if (json1.isJsonPrimitive() && json2.isJsonPrimitive()) {
if (json1.equals(json2)) {
return true;
} else {
return false;
}
} else {
return false;
}
} else if (json1 == null && json2 == null) {
return true;
} else {
return false;
}
return isEqual;
}
JSON.areEqual(json1, json2); //using BlobCity Java Commons
https://tech.blobcity.com/2018/09/02/json-equals-in-java-to-compare-two-jsons
このソリューションは私にとって、仕事はとても良いです:
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
これが他の人を助けることを願っています。
toString()
がオブジェクトをJSON
文字列に変換することをサポートしていると思います。