我试图比较2个JSON文件,它们具有重复值的数组。
我的第一个JSON对象有一个这样的数组:
"categories": [
"May",
"Apr",
"Mar"
]
我的第二个JSON对象有一个这样的数组:
"categories": [
"May",
"May",
"Apr",
"Apr",
"Mar",
"Mar"
]
我正在使用平面地图比较JSON,可在此链接comparing JSONs using guava中找到
这是我的代码的一部分:
private String smartJSONsCompare(JSONObject leftJson, JSONObject rightJson) {
String result = "</br>";
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> leftMap = gson.fromJson(leftJson.toString(), type);
Map<String, Object> rightMap = gson.fromJson(rightJson.toString(), type);
Map<String, Object> leftFlatMap = FlatMapUtil.flatten(leftMap);
Map<String, Object> rightFlatMap = FlatMapUtil.flatten(rightMap);
MapDifference<String, Object> difference = Maps.difference(leftFlatMap, rightFlatMap);
StringBuilder SB = new StringBuilder("</br>");
SB.append("Entries only on LEFT: </br>");
difference.entriesOnlyOnLeft().forEach((key, value) -> SB.append(key + ": " + value + "</br>"));
SB.append("Entries only on RIGHT: </br>");
difference.entriesOnlyOnRight().forEach((key, value) -> SB.append(key + ": " + value + "</br>"));
SB.append("Entries full difference : </br>");
difference.entriesDiffering().forEach((key, value) -> SB.append(key + ": " + value + "</br>"));
result = SB.toString();
return result;
}
我希望能够以更“聪明”的方式呈现差异。换句话说:显示JONS中不匹配的所有对象/数组。丢失的内容或比较的JSON添加了什么。
对于“categories”数组,我的代码返回一个消息,表明它们不匹配,但没有以优雅的方式表示差异。
我能做什么?
我在您的解决方案中进行了一些更改以获得所需结果。
我会在List中执行差异检查,因此我将创建基于您的代码将JSON更改为字符串列表的方法:
private static List<String> jsonToList(String json){
List<String> list = new ArrayList<>();
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> jsonMap = gson.fromJson(json, type);
Map<String, Object> flatten = FlatMapUtil.flatten(jsonMap);
flatten.forEach((k, v) -> list.add(v.toString()));
return list;
}
更新 当我回答问题时,我做的事情有点快,jsonToList基于你的代码。就像现在一样,你要求的东西过于复杂。因此我使用以下方法制作了更轻的版本:
private static List<String> jsonToList(String json) {
JSONObject response = new JSONObject(json);
List<String> list = new ArrayList<>();
JSONArray jsonArray = response.getJSONArray("categories");
if (jsonArray != null) {
for (int i = 0; i < jsonArray.length(); i++) {
list.add(jsonArray.get(i).toString());
}
}
return list;
}
也就是说,现在您有两个选择,您可以自行决定哪个最适合您的需求并从中获取。
更新结束
在这个例子中,我做了3个测试例子
String main = "{\"categories\":[\"May\",\"Apr\",\"Mar\"]}";
String json1 = "{\"categories\":[\"May\",\"May\",\"Apr\",\"Apr\",\"Mar\",\"Mar\"]}";
String json2 = "{\"categories\":[\"May\",\"Apr\",\"Apr\",\"Mar\",\"Mar\",\"Mar\"]}";
String json3 = "{\"categories\":[\"May\",\"Apr\",\"Mar\",\"Mar\"]}";
在我的第二步,我将创建一个
List<String> mainList = jsonToList(main);
List<String> list1 = jsonToList(json1);
到现在为止还挺好。现在我创建一个方法来获取2列表的额外差异,这意味着您在评论中请求的,我们只采用多次重复的值并将其返回列表中。在这个方法中,我使用hashmap只计算重复数,而不是重复超过1次的所有重复:
private static List<String> diffList(List<String> mainList, List<String> secondList){
List<String> list = new ArrayList<String>();
Map<String, Integer> wordCount = new HashMap<>();
for(String word: secondList) {
if(mainList.contains(word)) {
Integer count = wordCount.get(word);
wordCount.put(word, (count == null) ? 1 : count + 1);
if(wordCount.get(word) > 1){
list.add(word);
}
}
}
return list;
}
最后我会测试所有情况,例如list1:
List<String> diff1 = diffList(mainList, list1);
for (String s : diff1) {
System.out.println(s);
}
输出将是
May
Apr
Mar
对于list2
Apr
Mar
Mar
对于list3
Mar
现在我将视图方法与您的方法分开并创建一些类似的东西,只是为了让我的代码更清晰,更易于使用:
private static String viewResult(List<String> list1, List<String> list2, List<String> duplicate){
String result;
StringBuilder SB = new StringBuilder("</br>");
SB.append("Entries only on LEFT: </br>");
list1.forEach(e -> SB.append(e + "</br>"));
SB.append("Entries only on RIGHT: </br>");
list2.forEach(e -> SB.append(e + "</br>"));
SB.append("Entries full difference : </br>");
duplicate.forEach(e -> SB.append(e + "</br>"));
result = SB.toString();
return result;
}
因此,如果我们将所有这些代码放在一起,我将会是这样的,下面的代码将演示如何工作,但是从这里开始,您可以将它带到代码的下一个级别:
public static void main(String[] args) {
String main = "{\"categories\":[\"May\",\"Apr\",\"Mar\"]}";
String json1 = "{\"categories\":[\"May\",\"May\",\"Apr\",\"Apr\",\"Mar\",\"Mar\"]}";
String json2 = "{\"categories\":[\"May\",\"Apr\",\"Apr\",\"Mar\",\"Mar\",\"Mar\"]}";
String json3 = "{\"categories\":[\"May\",\"Apr\",\"Mar\",\"Mar\"]}";
List<String> mainList = jsonToList(main);
List<String> list1 = jsonToList(json1);
List<String> diff1 = diffList(mainList, list1);
for (String s : diff1) {
System.out.println(s);
}
String view = viewResult(mainList, list1, diff1);
}
private static List<String> jsonToList(String json){
List<String> list = new ArrayList<String>();
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> jsonMap = gson.fromJson(json, type);
Map<String, Object> flatten = FlatMapUtil.flatten(jsonMap);
flatten.forEach((k, v) -> list.add(v.toString()));
return list;
}
private static List<String> diffList(List<String> mainList, List<String> secondList){
List<String> list = new ArrayList<String>();
Map<String, Integer> wordCount = new HashMap<>();
for(String word: secondList) {
if(mainList.contains(word)) {
Integer count = wordCount.get(word);
wordCount.put(word, (count == null) ? 1 : count + 1);
if(wordCount.get(word) > 1){
list.add(word);
}
}
}
return list;
}
private static String viewResult(List<String> list1, List<String> list2, List<String> duplicate){
String result;
StringBuilder SB = new StringBuilder("</br>");
SB.append("Entries only on LEFT: </br>");
list1.forEach(e -> SB.append(e + "</br>"));
SB.append("Entries only on RIGHT: </br>");
list2.forEach(e -> SB.append(e + "</br>"));
SB.append("Entries full difference : </br>");
duplicate.forEach(e -> SB.append(e + "</br>"));
result = SB.toString();
return result;
}
如果你想要一个更好的差异和一个好的差异你可以在这里使用AssertJ。它通常用于测试,但差异看起来非常好,你也可以在普通代码中使用它。
例:
Expecting:
<["Mai", "Apr", "Mar"]>
to contain exactly in any order:
<["May", "Apr", "Mar", "Mar"]>
elements not found:
<["May", "Mar"]>
and elements not expected:
<["Mai"]>
可以创建:
[...]
import org.assertj.core.api.Assertions;
public class JsonTest {
final static String arr = " [\n"+
" \"Mai\",\n"+
" \"Apr\",\n"+
" \"Mar\"\n"+
" ]";
final static String arr2 = " [\n"+
" \"May\",\n"+
" \"Apr\",\n"+
" \"Mar\",\n"+
" \"Mar\"\n"+
" ]";
public static void main(String[] args){
System.out.println(smartJSONsCompare(arr,arr2));
}
private static String smartJSONsCompare(String leftJson, String rightJson) {
Gson gson = new Gson();
Type type = new TypeToken<List<String>>(){}.getType();
List<String> left = gson.fromJson(leftJson, type);
List<String> right = gson.fromJson(rightJson, type);
try{
Assertions.assertThat(left).containsExactlyInAnyOrderElementsOf(right);
}catch(AssertionError ae){
return ae.getMessage();
}
return "Matched";
}
}
我在gradle中添加了依赖项:
dependencies {
compile("org.assertj:assertj-core:3.11.1")
}
如果要在两个JSON对象之间创建补丁,请查看json-patch。
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.fge.jsonpatch.diff.JsonDiff;
import java.io.IOException;
public class JsonPatchTest {
public static void main(String[] args) throws IOException {
String jsonFirst = "{\"categories\":[\"May\",\"Apr\",\"Mar\"]}";
String jsonSecond = "{\"categories\":[\"May\",\"May\",\"Apr\",\"Apr\",\"Mar\",\"Mar\"]}";
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNodeFirst = mapper.readTree(jsonFirst);
JsonNode jsonNodeSecond = mapper.readTree(jsonSecond);
JsonNode patchNode = JsonDiff.asJson(jsonNodeFirst, jsonNodeSecond);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(patchNode));
}
}
将为您的方案生成以下输出:
[ {
"op" : "replace",
"path" : "/categories/1",
"value" : "May"
}, {
"op" : "replace",
"path" : "/categories/2",
"value" : "Apr"
}, {
"op" : "add",
"path" : "/categories/-",
"value" : "Apr"
}, {
"op" : "add",
"path" : "/categories/-",
"value" : "Mar"
}, {
"op" : "add",
"path" : "/categories/-",
"value" : "Mar"
} ]
我相信你应该自己处理json数组,以便以更“智能”的方式呈现它们的差异。 Here是一个包含CollectionUtils
类和disjunction
方法的库。
MapDifference<String, Object> difference = Maps.difference(leftMap, rightMap);
difference.entriesDiffering().forEach((key, value) -> {
Object left = value.leftValue();
Object right = value.rightValue();
if (left instanceof Iterable && right instanceof Iterable) {
Collection<?> diff = CollectionUtils.disjunction((Iterable<?>) right, (Iterable<?>) left);
System.out.println(key + " -> " + diff);
}
...
});
此代码适用于我(2年前)的生产。
public class App {
private final Gson GSON = new GsonBuilder().create();
public boolean isDifference(final String path, Map<String, Object> oldData, Map<String, Object> newData) {
MapDifference<String, Object> difference = Maps.difference(oldData, newData);
difference.entriesOnlyOnLeft().forEach((key, value) -> {
publishChange(Action.REMOVE, path, key, value);
});
difference.entriesOnlyOnRight().forEach((key, value) -> {
publishChange(Action.ADD, path, key, value);
});
difference.entriesDiffering().forEach((key, value) -> {
if (value.rightValue() instanceof Map && value.leftValue() instanceof Map) {
if (!path.isEmpty()) {
key = path.concat("-").concat(key);
}
isDifference(key, (Map) value.leftValue(), (Map) value.rightValue());
} else {
publishChange(Action.MODIFY, path, key, value);
}
});
return !difference.areEqual();
}
public void publishChange(Action action, String path, String key, Object value) {
if (value instanceof MapDifference.ValueDifference) {
value = ((MapDifference.ValueDifference) value).rightValue();
}
JsonElement jsonValue = GSON.toJsonTree(value);
String event = createEvent(action, path, key, jsonValue);
System.out.println("Differrence: " + event);
}
public String createEvent(Action action, String paths, String key, JsonElement value) {
JsonObject root = new JsonObject();
JsonArray arrPaths = new JsonArray();
for (String path : paths.split("-")) {
arrPaths.add(path);
}
root.addProperty("action", action.toString());
root.add("paths", arrPaths);
JsonObject data = new JsonObject();
data.addProperty("key", key);
data.add("value", value);
root.add("data", data);
return root.toString();
}
public static enum Action {
ADD, REMOVE, MODIFY
}}
测试/示例:
public class AppTest {
@Test
public void testAppHasAGreeting() {
App classUnderTest = new App();
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
// JsonOld: {"a":1,"b":1,"c":true,"array":[1,2,3],"object":{"arrayKey":["a","b","c","d"]}}
String jsonOld = "{\"a\":1,\"b\":1,\"c\":true,\"array\":[1,2,3],\"object\":{\"arrayKey\":[\"a\",\"b\",\"c\",\"d\"]}}";
// JsonNew: {"a":2,"b":1,"array":[1,2,3,2],"another":{"d":false,"e":["a","b","c"]},"object":{"booleanKey":true,"arrayKey":["a","b","c"]}}
String jsonNew = "{\"a\":2,\"b\":1,\"array\":[1,2,3,2],\"another\":{\"d\":false,\"e\":[\"a\",\"b\",\"c\"]},\"object\":{\"booleanKey\":true,\"arrayKey\":[\"a\",\"b\",\"c\"]}}";
Type mapType = new TypeToken<Map<String, Object>>() {
}.getType();
Map<String, Object> jsonOldAsMap = gson.fromJson(jsonOld, mapType);
Map<String, Object> jsonNewAsMap = gson.fromJson(jsonNew, mapType);
System.out.println("Old Json: " + gson.toJson(jsonOldAsMap));
System.out.println("New Json: " + gson.toJson(jsonNewAsMap));
System.out.println("========== Result ==========");
// When
boolean diff = classUnderTest.isDifference("", jsonOldAsMap, jsonNewAsMap);
// Then
assertTrue(diff);
}}
结果将打印如下:
区别:{“action”:“REMOVE”,“paths”:[“”],“data”:{“key”:“c”,“value”:true}}
区别:{“action”:“ADD”,“paths”:[“”],“data”:{“key”:“another”,“value”:{“d”:false,“e”:[“一”, “b”, “C”]}}}
区别:{“action”:“MODIFY”,“paths”:[“”],“data”:{“key”:“a”,“value”:2.0}}
区别:{“action”:“MODIFY”,“paths”:[“”],“data”:{“key”:“array”,“value”:[1.0,2.0,3.0,2.0]}}
差异:{“action”:“ADD”,“paths”:[“object”],“data”:{“key”:“boolean Key”,“value”:true}}
区别:{“action”:“MODIFY”,“paths”:[“object”],“data”:{“key”:“array Key”,“value”:[“a”,“b”,“c “]}}