/**
* personium.io
* Copyright 2014 FUJITSU LIMITED
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fujitsu.dc.test;
import java.util.Arrays;
import java.util.HashMap;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
/**
* JSON比較クラス.
*/
public class CompareJSON {
/**
* デフォルトコンストラクタの隠蔽.
*/
private CompareJSON() {
// Nothing todo
}
/**
* 比較結果格納クラス.
*/
public static class Result extends HashMap<Object, Object> {
/**
* .
*/
private static final long serialVersionUID = 4728015078227545856L;
/**
* .
* @param key キー
* @param actualValue 実際の値
* @param message メッセージ
* @return 旧オブジェクト
*/
public Object put(Object key, Object actualValue, String message) {
return super.put(key, new Object[] {message, actualValue });
}
/**
* 差分があった場合の差分情報メッセージを返す。
* @param key キー
* @return 差分があった場合の差分情報メッセージ
*/
@Override
public Object get(Object key) {
return ((Object[]) super.get(key))[0];
}
/**
* JSON内の acutalValueを返す。
* @param key キー
* @return 実際の値
*/
public Object getMismatchValue(Object key) {
return ((Object[]) super.get(key))[1];
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (Object value : this.values()) {
builder.append(((Object[]) value)[0]).append("\n");
}
return builder.toString();
}
}
/**
* 2つのJSONを比較し、差分を報告する.
* @param source 正しい/オリジナルの JSON
* @param toBeCompared 比較対象JSON
* @return 比較結果. 差分がない場合は nullを返す。
*/
public static Result compareJSON(JSONObject source, JSONObject toBeCompared) {
Result result = new Result();
// 比較不要な要素の削除
if (source.containsKey("d")) {
source = (JSONObject) source.get("d");
}
if (source.containsKey("results")) {
source = (JSONObject) source.get("results");
}
if (toBeCompared.containsKey("d")) {
toBeCompared = (JSONObject) toBeCompared.get("d");
}
if (toBeCompared.containsKey("results")) {
toBeCompared = (JSONObject) toBeCompared.get("results");
}
source.remove("__published");
source.remove("__updated");
source.remove("__metadata");
toBeCompared.remove("__published");
toBeCompared.remove("__updated");
toBeCompared.remove("__metadata");
// JSONの比較
compareJSON(result, "", source, toBeCompared);
if (0 < result.size()) {
return result;
} else {
return null;
}
}
/**
* 2つのJSONを比較し、差分を報告する.
* @param source 正しい/オリジナルの JSON
* @param toBeCompared 比較対象JSON
* @return 比較結果. 差分がない場合は nullを返す。
* @throws ParseException 入力が正しい JSONではない場合.
*/
public static Result compareJSON(String source, String toBeCompared) throws ParseException {
JSONParser parser = new JSONParser();
return compareJSON((JSONObject) parser.parse(source), (JSONObject) parser.parse(toBeCompared));
}
private static String toKey(Object parentKey, Object key) {
if (0 < ((String) parentKey).length()) {
return parentKey + "." + key;
} else {
return (String) key;
}
}
/**
* 2つのJSONを比較し、差分を報告する.
* @param result 結果オブジェクト
* @param parentKey jsonの上位キー
* @param source 正しい/オリジナルの JSON
* @param toBeCompared 比較対象JSON
* @return 比較結果
*/
private static Result compareJSON(Result result, Object parentKey, JSONObject source, JSONObject toBeCompared) {
for (Object key : source.keySet()) {
Object originalValue = source.get(key);
Object targetValue = null;
if (toBeCompared.containsKey(key)) {
targetValue = toBeCompared.get(key);
} else {
result.put(toKey(parentKey, key), null,
String.format("key[%s] does not exist in target JSON.", toKey(parentKey, key)));
continue;
}
if (originalValue instanceof JSONArray) {
Object targetVal = toBeCompared.get(key);
if (targetVal instanceof JSONArray) {
compareJSONArray(result, toKey(parentKey, key), (JSONArray) originalValue, (JSONArray) targetValue);
} else {
result.put(toKey(parentKey, key), String.format(
"Value of target of key[%s] has different type (e.g. JSONArray) as source JSON.",
targetVal,
toKey(parentKey, key)));
}
} else if (originalValue instanceof JSONObject) {
if (targetValue instanceof JSONObject) {
compareJSON(result, toKey(parentKey, key), (JSONObject) originalValue, (JSONObject) targetValue);
} else {
result.put(toKey(parentKey, key),
targetValue,
String.format(
"Value of target of key[%s] has different type (e.g. JSONArray) as source JSON.",
toKey(parentKey, key)));
}
} else {
compareValue(result, toKey(parentKey, key), originalValue, targetValue);
}
}
for (Object key : toBeCompared.keySet()) {
if (!source.containsKey(key)) {
result.put(toKey(parentKey, key),
toBeCompared.get(key),
String.format("Excess key[%s] exists in target JSON.", toKey(parentKey, key)));
}
}
return result;
}
private static Result compareValue(Result result, Object key, Object source, Object toBeCompared) {
if (source == toBeCompared) {
// nullチェック
return result;
}
if (!source.equals(toBeCompared)) {
result.put(key,
toBeCompared,
String.format(
"Value of target of key[%s] "
+ "does not have the same value as source JSON. orignal: [%s], target[%s]",
key, source, toBeCompared));
}
return result;
}
private static Result compareJSONArray(Result result, Object key, JSONArray source, JSONArray toBeCompared) {
if (source == toBeCompared) {
// nullチェック
return result;
}
if (null == toBeCompared) {
result.put(key,
toBeCompared,
String.format("Size of JSONArray [%s] is different. expected[%d], target[null]", key,
source.size()));
}
Object[] expectedArray = source.toArray();
Object[] targetArray = toBeCompared.toArray();
Arrays.sort(expectedArray);
Arrays.sort(targetArray);
if (expectedArray.length != targetArray.length) {
result.put(key,
toBeCompared,
String.format("Size of JSONArray [%s] is different. expected[%d], target[%d]", key,
source.size(),
toBeCompared.size()));
return result;
}
for (int i = 0; i < expectedArray.length; i++) {
Object expected2 = expectedArray[i];
Object target2 = targetArray[i];
if (expected2 instanceof JSONObject && target2 instanceof JSONObject) {
compareJSON(result, key, (JSONObject) expected2, (JSONObject) target2);
} else if (!expected2.equals(target2)) {
result.put(key,
toBeCompared,
String.format("Value of target of key[%s] does "
+ "not have the same value as source JSON. orignal: [%s], target[%s]",
key, source, toBeCompared));
}
}
return result;
}
}