将JSON与数组进行比较

问题描述 投票:4回答:5

我试图比较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”数组,我的代码返回一个消息,表明它们不匹配,但没有以优雅的方式表示差异。

我能做什么?

java json
5个回答
6
投票

我在您的解决方案中进行了一些更改以获得所需结果。

我会在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;
}

1
投票

如果你想要一个更好的差异和一个好的差异你可以在这里使用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")
}

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"
} ]

0
投票

我相信你应该自己处理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);
  }
  ...
});

0
投票

此代码适用于我(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 “]}}

这里提供的代码:https://github.com/liemle3893/compare-json

© www.soinside.com 2019 - 2024. All rights reserved.