I am trying to compare 2 JSON files, they have arrays with duplicated values.
My first JSON Object has an array like this:
"categories": [
"May",
"Apr",
"Mar"
]
My second JSON object has an array like this:
"categories": [
"May",
"May",
"Apr",
"Apr",
"Mar",
"Mar"
]
I am comparing the JSON using flat maps that can be found in this link comparing JSONs using guava
Here is part of my code:
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;
}
I wish to be able to present the difference in a more "smart" way. In other words: showing all the objects / arrays in the JONSs that don't match. What is missing or what was added to the compared JSON.
For the "categories" array my code returns a message that their is a mismatch, but doesn't state the difference in an elegant way.
What can I do?
I have change a bit in your solution to get the wanted result.
I would do my difference check in List, therefore I will create method to change JSON to list of strings based on your 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;
}
Update
When I answered the question I did things a bit fast, the jsonToList was based on your code. As it is right now it is over complicated to what you are asking for. I have therefore made much lighter version using the following method in stead:
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;
}
That said, now you have two choices and it is up to you to find out which one fits best to your needs and take it from here.
End of Update
for this example I have made 3 test examples
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\"]}";
in my second step I will create a
List<String> mainList = jsonToList(main);
List<String> list1 = jsonToList(json1);
so far so good. Now I make a method to take the extra difference of the 2 list, that mean as you requested in your comments, we take only all values that are duplicated more than once and return them in list. In this method I used hashmap only count duplicates and than take the all that is repeated more than 1 time:
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;
}
Finally I would test all cases, for instance for list1:
List<String> diff1 = diffList(mainList, list1);
for (String s : diff1) {
System.out.println(s);
}
The output will be
May
Apr
Mar
for list2
Apr
Mar
Mar
And for list3
Mar
Now I will separate view method from the your method and create some thing like, just to make my code more clear and easy to work with:
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;
}
So if we put all this code together I will be some thing like this, and the following code is to demonstrate how things works, but from here you can take it to the next level in your 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;
}
If you want something more generic with a good diff you could utilize AssertJ here.
Its usually used for Testing, but the diff looks really good and you can also use it in normal code.
Example:
Expecting:
<["Mai", "Apr", "Mar"]>
to contain exactly in any order:
<["May", "Apr", "Mar", "Mar"]>
elements not found:
<["May", "Mar"]>
and elements not expected:
<["Mai"]>
Can be created by:
[...]
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";
}
}
I added the dependencies in gradle with:
dependencies {
compile("org.assertj:assertj-core:3.11.1")
}
If you want to create a patch between your two JSON Objects have a look at 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));
}
}
Would produce the following output for your scenario:
[ {
"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"
} ]
I believe you should handle json arrays on your own in order to present their difference in a more "smart" way. Here is a library which contains CollectionUtils class with disjunction method.
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);
}
...
});
This code work for me (2 years ago) on production.
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
}}
Test/ Example:
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);
}}
The result will print like this:
Differrence: {"action":"REMOVE","paths":[""],"data":{"key":"c","value":true}}
Differrence: {"action":"ADD","paths":[""],"data":{"key":"another","value":{"d":false,"e":["a","b","c"]}}}
Differrence: {"action":"MODIFY","paths":[""],"data":{"key":"a","value":2.0}}
Differrence: {"action":"MODIFY","paths":[""],"data":{"key":"array","value":[1.0,2.0,3.0,2.0]}}
Differrence: {"action":"ADD","paths":["object"],"data":{"key":"booleanKey","value":true}}
Differrence: {"action":"MODIFY","paths":["object"],"data":{"key":"arrayKey","value":["a","b","c"]}}
The code available here: https://github.com/liemle3893/compare-json
Related
I have a hashmap which has key value pair of String and object. It is the conversion of something like below json.
{
"test1": {
"test2": {
"test3": {
"key": "value"
},
"somefields12": "some value2"
},
"somefields": "some value"
}
}
But, I am not converting to map. I have just that map. If this may has key and value , I have to do write some logic based on that value. I implemented as below:
if (map.containsKey("test1") ) {
final HashMap<String, Object> test1 = (HashMap<String, Object>) map.get("test1");
if (test1.containsKey("test2")) {
final List<Object> test2 = (List<Object>) test1.get("test2");
if (!test2.isEmpty()) {
final HashMap<String, Object> test3 = (HashMap<String, Object>) test2.get(0);
if (test3.containsKey("key")) {
final String value = String.valueOf(test2.get("key"));
if (!StringUtils.isBlank(value)) {
//do some work based on value
}
}
}
}
}
Now, I wanted to avoid the nested if (multiple ifs) from my code. What would be the best way to do so?
I'm not familiar with the fancy new Java 8 features, so I'd do it the old fashioned way with a function that takes a path to look up, and walks the list with a loop:
import java.util.*;
class Test {
static String getByPath(HashMap<String, Object> map, String... path) {
for(int i=0; i<path.length-1; i++) {
map = (HashMap<String, Object>) map.get(path[i]);
if (map == null) return null;
}
Object value = map.get(path[path.length-1]);
return value == null ? null : String.valueOf(value);
}
public static void main(String[] args) {
HashMap<String, Object> map = new HashMap<>();
HashMap<String, Object> tmp1 = new HashMap<>();
HashMap<String, Object> tmp2 = new HashMap<>();
map.put("test1", tmp1);
tmp1.put("test2", tmp2);
tmp2.put("key1", "My Value");
System.out.println("With valid path: " + getByPath(map, "test1", "test2", "key1"));
System.out.println("With invalid path: " + getByPath(map, "test1", "BANANA", "key1"));
}
}
This results in:
With valid path: My Value
With invalid path: null
This can optionally be extended to:
Check that nodes are in fact maps before casting
Use Optional or a helpful exception instead of returning null
Using Gson library combined with java 8 Optional you can do something like this:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.Optional;
public class FindJsonKey {
public static final String JSON = "{\n" +
" \"test1\": {\n" +
" \"test2\": {\n" +
" \"test3\": {\n" +
" \"key\": \"value\"\n" +
" },\n" +
" \"somefields12\": \"some value2\"\n" +
" },\n" +
" \"somefields\": \"some value\"\n" +
" }\n" +
"}";
public static void main(String[] args) {
Gson gson = new GsonBuilder().create();
JsonObject jsonObject = gson.fromJson(JSON, JsonObject.class);
Optional
.ofNullable(jsonObject.getAsJsonObject("test1"))
.map(test1 -> test1.getAsJsonObject("test2"))
.map(test2 -> test2.getAsJsonObject("test3"))
.map(test3 -> test3.get("key"))
.map(JsonElement::getAsString)
.ifPresent(key -> {
// Do something with key..
});
}
}
A recursive solution that assumes the JSON structure is as given -- and ignores the names of the nested parent maps -- could look like the following:
/**
* Traverses the map from {#code nextEntry} until a key matching {#code keyTofind} is found
* or the bottom of the map is reached.
* The method assumes that any nested maps are contained in the first
* element of the parent map.
*
* #param nextEntry the next entry to search from
* #param keyToFind the stopping condition for the recursion
* #return the value matching {#code keyToFind} (may be empty)
*/
private static Optional<Object> find(Map.Entry<String,Object> nextEntry, String keyToFind) {
Object value = nextEntry.getValue();
if (nextEntry.getKey().equals(keyToFind)) {
return Optional.of(value);
} else if (nextEntry.getValue() instanceof Map) {
return find(((Map<String, Object>)nextEntry.getValue())
.entrySet().iterator().next(), keyToFind);
} else {
return Optional.empty();
}
}
Testing with the provided data (with help of Jackson for JSON parsing)
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Test {
private static final String JSON = "{ "
+ "\"test1\": {"
+ " \"test2\": {"
+ " \"test3\": {"
+ " \"key\": \"value\""
+ "},"
+ " \"somefields12\": \"some value2\""
+ "},"
+ " \"somefields\": \"some value\""
+ "}"
+ "}";
//...
public static void main(String[] args) throws Exception {
Map<String, Object> json = new ObjectMapper()
.readValue(JSON, new TypeReference<Map<String, Object>>() { });
final String keyToFind = "key";
Optional<Object> value = find(json.entrySet().iterator().next(), keyToFind);
System.out.println(value.isPresent() ? value.get() : "null");
}
}
gives
value
I have a json string which I need to validate and find any other keys other than in a list is there in the json string. The sample json string is
{
"required" : true,
"requiredMsg" : "Title needed",
"choices" : [ "a", "b", "c", "d" ],
"choiceSettings" : {
"a" : {
"exc" : true
},
"b" : { },
"c" : { },
"d" : {
"textbox" : {
"required" : true
}
}
},
"Settings" : {
"type" : "none"
}
}
To allow only predifined keys is exsist in the json string I want to get all the keys in the json string. How can I obtain all the keys in the json string. I am using jsonNode. My code till now is
JsonNode rootNode = mapper.readTree(option);
JsonNode reqiredMessage = rootNode.path("reqiredMessage");
System.out.println("msg : "+ reqiredMessage.asText());
JsonNode drNode = rootNode.path("choices");
Iterator<JsonNode> itr = drNode.iterator();
System.out.println("\nchoices:");
while (itr.hasNext()) {
JsonNode temp = itr.next();
System.out.println(temp.asText());
}
How to get all the keys from the json string using JsonNode
forEach will iterate over children of a JsonNode (converted to String when printed) and fieldNames() gets an Iterator<String> over keys. Here are some examples for printing elements of the example JSON:
JsonNode rootNode = mapper.readTree(option);
System.out.println("\nchoices:");
rootNode.path("choices").forEach(System.out::println);
System.out.println("\nAllKeys:");
rootNode.fieldNames().forEachRemaining(System.out::println);
System.out.println("\nChoiceSettings:");
rootNode.path("choiceSettings").fieldNames().forEachRemaining(System.out::println);
You'll probably need fields() at some point that returns an Iterator<Entry<String, JsonNode>> so you can iterate over key, value pairs.
This should do it.
Map<String, Object> treeMap = mapper.readValue(json, Map.class);
List<String> keys = Lists.newArrayList();
List<String> result = findKeys(treeMap, keys);
System.out.println(result);
private List<String> findKeys(Map<String, Object> treeMap , List<String> keys) {
treeMap.forEach((key, value) -> {
if (value instanceof LinkedHashMap) {
Map<String, Object> map = (LinkedHashMap) value;
findKeys(map, keys);
}
keys.add(key);
});
return keys;
}
This will print out result as
[required, requiredMsg, choices, exc, a, b, c, required, textbox, d, choiceSettings, type, Settings]
The accepted answer works out great but issues a warning, "Type safety: The expression of type Map needs unchecked conversion to conform to Map <String, Object>"
This answer led me to change that line to the following to eliminate the warning:
Map<String, Object> treeMap = mapper.readValue(json, new TypeReference<Map<String, Object>>() {});
List in the json was not supported in the accepted solution. Here is what I propose:
public List<String> getAllNodeKeys(String json) throws JsonProcessingException {
Map<String, Object> treeMap = objectMapper.readValue(json, new TypeReference<>() {
});
return findKeys(treeMap, new ArrayList<>());
}
private List<String> findKeys(Map<String, Object> treeMap, List<String> keys) {
treeMap.forEach((key, value) -> {
if (value instanceof LinkedHashMap) {
LinkedHashMap map = (LinkedHashMap) value;
findKeys(map, keys);
} else if (value instanceof List) {
ArrayList list = (ArrayList) value;
list.forEach(map -> findKeys((LinkedHashMap) map, keys));
}
keys.add(key);
});
return keys;
}
I have a JsonElement:
JsonElement testCaseParametersJson = batchItem.getTestCase().getToolParameters().get("testCaseParameters");
of which assigns the following:
["dessert", "place", "tvShow"]
And I have a JsonArray:
JsonObject testParametersJson = batchItem.getTestParameters().getAsJsonObject();
of which assigns the following:
{"dessert": "cookies", "tvShow": "survivor", "color" : "blue"}
I'd appreciate some advice on how to check if the key in the JsonArray exists as an item in the JsonElement.
Using Gson library, you can get the String values from your JSONElement / JSONObject and do the following:
String jsonObject = "{\"dessert\": \"cookies\", \"tvShow\": \"survivor\", \"color\" : \"blue\"}";
String jsonArray = "[\"dessert\", \"place\", \"tvShow\"]";
Map<String, String> objMap = new Gson().fromJson(jsonObject, new TypeToken<HashMap<String, String>>() {}.getType());
List<String> arrayVals = new Gson().fromJson(jsonArray, new TypeToken<List<String>>(){}.getType());
for(Entry<String, String> entry : objMap.entrySet()) {
for (String val : arrayVals) {
if (entry.getKey().equals(val)) {
System.out.println("Found value in key set: " + val);
}
}
}
Code can be summarized, and if using Java 8, you can try "forEach" loop according to your needs.
I have a set of strings like this
A_2007-04, A_2007-09, A_Agent, A_Daily, A_Execute, A_Exec, B_Action, B_HealthCheck
I want output as:
Key = A, Value = [2007-04,2007-09,Agent,Execute,Exec]
Key = B, Value = [Action,HealthCheck]
I'm using HashMap to do this
pckg:{A,B}
count:total no of strings
reports:set of strings
Logic I used is nested loop:
for (String l : reports[i]) {
for (String r : pckg) {
String[] g = l.split("_");
if (g[0].equalsIgnoreCase(r)) {
report.add(g[1]);
dirFiles.put(g[0], report);
} else {
break;
}
}
}
I'm getting output as
Key = A, Value = [2007-04,2007-09,Agent,Execute,Exec]
How to get second key?
Can someone suggest logic for this?
Assuming that you use Java 8, it can be done using computeIfAbsent to initialize the List of values when it is a new key as next:
List<String> tokens = Arrays.asList(
"A_2007-04", "A_2007-09", "A_Agent", "A_Daily", "A_Execute",
"A_Exec", "P_Action", "P_HealthCheck"
);
Map<String, List<String>> map = new HashMap<>();
for (String token : tokens) {
String[] g = token.split("_");
map.computeIfAbsent(g[0], key -> new ArrayList<>()).add(g[1]);
}
In terms of raw code this should do what I think you are trying to achieve:
// Create a collection of String any way you like, but for testing
// I've simply split a flat string into an array.
String flatString = "A_2007-04,A_2007-09,A_Agent,A_Daily,A_Execute,A_Exec,"
+ "P_Action,P_HealthCheck";
String[] reports = flatString.split(",");
Map<String, List<String>> mapFromReportKeyToValues = new HashMap<>();
for (String report : reports) {
int underscoreIndex = report.indexOf("_");
String key = report.substring(0, underscoreIndex);
String newValue = report.substring(underscoreIndex + 1);
List<String> existingValues = mapFromReportKeyToValues.get(key);
if (existingValues == null) {
// This key hasn't been seen before, so create a new list
// to contain values which belong under this key.
existingValues = new ArrayList<>();
mapFromReportKeyToValues.put(key, existingValues);
}
existingValues.add(newValue);
}
System.out.println("Generated map:\n" + mapFromReportKeyToValues);
Though I recommend tidying it up and organising it into a method or methods as fits your project code.
Doing this with Map<String, ArrayList<String>> will be another good approach I think:
String reports[] = {"A_2007-04", "A_2007-09", "A_Agent", "A_Daily",
"A_Execute", "A_Exec", "P_Action", "P_HealthCheck"};
Map<String, ArrayList<String>> map = new HashMap<>();
for (String rep : reports) {
String s[] = rep.split("_");
String prefix = s[0], suffix = s[1];
ArrayList<String> list = new ArrayList<>();
if (map.containsKey(prefix)) {
list = map.get(prefix);
}
list.add(suffix);
map.put(prefix, list);
}
// Print
for (Map.Entry<String, ArrayList<String>> entry : map.entrySet()) {
String key = entry.getKey();
ArrayList<String> valueList = entry.getValue();
System.out.println(key + " " + valueList);
}
for (String l : reports[i]) {
String[] g = l.split("_");
for (String r : pckg) {
if (g[0].equalsIgnoreCase(r)) {
report = dirFiles.get(g[0]);
if(report == null){ report = new ArrayList<String>(); } //create new report
report.add(g[1]);
dirFiles.put(g[0], report);
}
}
}
Removed the else part of the if condition. You are using break there which exits the inner loop and you never get to evaluate the keys beyond first key.
Added checking for existing values. As suggested by Orin2005.
Also I have moved the statement String[] g = l.split("_"); outside inner loop so that it doesn't get executed multiple times.
I have json structure like this,how I parse the json with help of Gson.I need to store the key values in json object,I tried many example related to dynamic array but dynamic json array without square brace I cannot parse using Gson.Any Solutions or need to do Manual Json Parse?If any one feel duplicate comment the answer below.
{ "Result":[[
"Title",
{
"0": 1323,
"1": 3358,
"2": 2123,
"3": 8536,
"4": 1399,
"5": 9303,
"7": 9732,
"8": 3433,
"9": 1383
}
],[
"Title",
{
"0": 1323,
"1": 3358,
"2": 2123,
"3": 8536,
}
]]}
To start, your JSON causes an exception to be thrown because it is invalid - You have an extra comma at the end of the last value in the second example. "3": 8536, should be "3": 8536.
After fixing that, this should be a simple task provided you define your objects correctly. Here is what I came up with:
public class Results {
#SerializedName("Result")
List<Result> results;
}
public class Result {
String title;
Map<String, Integer> results;
}
From there, the Result class needs to be deserialized in a special fashion, since the fields in the Result class do not directly map to entries in the JSON. Instead, we need to pull off the first and second elements of the JsonArray that is contained within each Result, and parse it accordingly. That looks like this:
public class ResultDeserializer implements JsonDeserializer<Result> {
#Override
public Result deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonArray array = json.getAsJsonArray();
Result result = new Result();
result.title = array.get(0).getAsString();
result.results = new LinkedHashMap<String, Integer>();
for(Entry<String, JsonElement> entry : array.get(1).getAsJsonObject().entrySet()) {
result.results.put(entry.getKey(), entry.getValue().getAsInt());
}
return result;
}
}
Note that my example omits error checking. Finally, register this deserializer, and you should be all set:
Gson gson = new GsonBuilder().registerTypeAdapter(Result.class, new ResultDeserializer()).create();
Results results = gson.fromJson(json, Results.class);
for(Result r : results.results) {
System.out.println("Title = " + r.title);
for(Entry<String, Integer> entry : r.results.entrySet()) {
System.out.println("\t " + entry.getKey() + " -> " + entry.getValue());
}
}
This prints:
Title = Title
0 -> 1323
1 -> 3358
2 -> 2123
3 -> 8536
4 -> 1399
5 -> 9303
7 -> 9732
8 -> 3433
9 -> 1383
Title = Title
0 -> 1323
1 -> 3358
2 -> 2123
3 -> 8536
I'll leave it to the OP to implement the reverse, that is a serializer for Result to produce the same results.
Found simple way to parse above json using Nested Map in Gson
Result.java
public class Result {
#SerializedName("formatted_facet_fields")
Map<String,Map<String,String>> formatted_facet_fields;
}
in response receiving side
Gson gson=new Gson();
Result result = gson.fromJson(json_response, Result.class);
Yes, You can parse nested json object.
if (!exists) {
keys = json.keys();
while (keys.hasNext()) {
nextKeys = (String) keys.next();
try {
if (json.get(nextKeys) instanceof JSONObject) {
if (exists == false) {
getKey(json.getJSONObject(nextKeys), key);
}
} else if (json.get(nextKeys) instanceof JSONArray) {
JSONArray jsonarray = json.getJSONArray(nextKeys);
for (int i = 0; i < jsonarray.length(); i++) {
String jsonarrayString = jsonarray.get(i).toString();
JSONObject innerJSOn = new JSONObject(jsonarrayString);
if (exists == false) {
getKey(innerJSOn, key);
}
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
For complete understanding checkout below: Code Explanation : https://www.youtube.com/watch?v=ZjZqLUGCWxo