J'essaie de comparer 2 fichiers JSON, ils ont des tableaux avec des valeurs dupliquées.
Mon premier objet JSON a un tableau comme celui-ci:
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;
}
Mon deuxième JSON l'objet a un tableau comme celui-ci:
"categories": [
"May",
"May",
"Apr",
"Apr",
"Mar",
"Mar"
]
Je compare le JSON en utilisant des cartes plates que l'on peut trouver dans ce lien comparaison de JSON avec goyave
Voici une partie de mon code:
"categories": [
"May",
"Apr",
"Mar"
]
Je souhaite pouvoir présenter la différence d'une manière plus «intelligente». En d'autres termes: afficher tous les objets / tableaux dans les JONS qui ne correspondent pas. Ce qui manque ou ce qui a été ajouté au JSON comparé.
Pour le tableau "categories", mon code renvoie un message indiquant qu'il ne correspond pas, mais n'énonce pas la différence de manière élégante. p>
Que puis-je faire?
5 Réponses :
Je pense que vous devriez gérer vous-même les tableaux json afin de présenter leur différence d'une manière plus "intelligente". Voici une bibliothèque qui contient la classe CollectionUtils avec la méthode disjonction .
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);
}
...
});
J'ai un peu changé votre solution pour obtenir le résultat souhaité.
Je ferais mon contrôle des différences dans List, donc je vais créer une méthode pour changer JSON en liste de chaînes en fonction de votre code:
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;
}
Mettre à jour
Quand j'ai répondu à la question, j'ai fait les choses un peu vite, la jsonToList était basée sur votre code. Comme c'est le cas actuellement, c'est trop compliqué par rapport à ce que vous demandez. J'ai donc fait une version beaucoup plus légère en utilisant à la place la méthode suivante:
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;
}
Ceci dit, maintenant vous avez deux choix et c'est à vous de savoir lequel vous convient le mieux à vos besoins et prenez-le d'ici.
Fin de la mise à jour
pour cet exemple, j'ai fait 3 exemples de test p>
Mar
dans ma deuxième étape, je vais créer un
Apr Mar Mar
pour l'instant tout va bien. Maintenant, je fais une méthode pour prendre la différence supplémentaire de la liste 2, cela signifie que comme vous l'avez demandé dans vos commentaires, nous ne prenons que toutes les valeurs qui sont dupliquées plus d'une fois et les renvoyons dans la liste. Dans cette méthode, j'ai utilisé hashmap pour ne compter que les doublons et ensuite prendre tout ce qui est répété plus d'une fois:
May Apr Mar
Enfin, je testerais tous les cas, par exemple pour list1:
List<String> diff1 = diffList(mainList, list1);
for (String s : diff1) {
System.out.println(s);
}
La sortie sera
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;
}
pour la liste2
List<String> mainList = jsonToList(main); List<String> list1 = jsonToList(json1);
Et pour list3
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\"]}";
Je vais maintenant séparer la méthode view de votre méthode et créer quelque chose comme, juste pour rendre mon code plus clair et plus facile à utiliser: p >
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;
}
Donc, si nous mettons tout ce code ensemble, je serai quelque chose comme ça, et le code suivant est pour montrer comment les choses fonctionnent, mais à partir de là, vous pouvez passer au niveau suivant dans votre code:
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;
}
Si vous voulez quelque chose de plus générique avec une bonne différence, vous pouvez utiliser AssertJ ici. Il est généralement utilisé pour les tests, mais le diff semble vraiment bon et vous pouvez également l'utiliser dans du code normal.
Exemple:
dependencies {
compile("org.assertj:assertj-core:3.11.1")
}
Peut être créé par: p >
[...]
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";
}
}
J'ai ajouté les dépendances dans gradle avec:
Expecting: <["Mai", "Apr", "Mar"]> to contain exactly in any order: <["May", "Apr", "Mar", "Mar"]> elements not found: <["May", "Mar"]> and elements not expected: <["Mai"]>
Si vous souhaitez créer un patch entre vos deux objets JSON, jetez un œil à json -patch .
[ {
"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"
} ]
Produirait la sortie suivante pour votre scénario:
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));
}
}
Ce code fonctionne pour moi (il y a 2 ans) en production.
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);
}}
Test / Exemple:
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
}}
Le résultat sera imprimé comme ceci:
Différence: {"action": "REMOVE", "chemins": [""], "data": {"key": "c", "value": true}}
Différence: {"action": "ADD", "chemins": [""], "data": {"key": "another", "value": {"d": false , "e": ["a", "b", "c"]}}}
Différence: {"action": "MODIFY", "path": [""], "data ": {" key ":" a "," value ": 2.0}}
Différence: {" action ":" MODIFY "," path ": [" "]," data ": { "clé": "tableau", "valeur": [1.0,2.0,3.0,2.0]}}
Différence: {"action": "AJOUTER", "chemins": ["objet"] , "data": {"key": "booleanKey", "value": true}}
Différence: {"action": "MODIFY", "path": ["object"], " data ": {" key ":" arrayKey "," value ": [" a "," b "," c "]}}
Le code disponible ici: https://github.com/liemle3893/compare-json
Pourquoi un objet et non une liste dans la carte?
Je lance votre code semble fonctionner, qu'entendez-vous par incompatibilité de message. Veuillez suivre. ajoutez exactement une copie du message que vous recevez dans votre console, et ce qui ne va pas. ET comment pensez-vous que votre sortie sera.
Le code ne peut afficher que 2 différences dans le tableau des mois. Je veux qu'il soit affiché de manière "intelligente". Dire à l'utilisateur que l'un JSON n'a que 3 valeurs tandis que l'autre en a 6 + montrant la différence exacte.
Le deuxième json a des valeurs répétées dans un tableau comme Mars et Mars et dans la première liste Mars, la différence est-elle donc ici Mars?
Mars, avril et mai - sont tous dupliqués dans le 2ème JSON.
il s'agit donc de montrer les doublons dans le second json par rapport au premier json
Oui, mais en montrant la différence de manière intelligente.
ok chose de plus si mar répété 3 fois alors est-il en double devrait renvoyer le résultat être Mar ou Mar, Mar? car si cela ne se soucie pas du nombre de fois qu'il est répété, je peux vous donner une solution sous peu
Il devrait indiquer Mar, Mar si Mar est dupliqué 3 fois.
Spring sync pourrait vous sauver
Pourquoi n'utilisez-vous pas quelque chose comme ça? stackoverflow.com/a/24012023/8588359 Vous devrez ajouter plus de code mais je pense que ce ne serait pas un problème .