Json string with nested objects to Map<String,String> - java

I have a json string with possible nested object inside like this:
{
"stringTypeCode": "aaaaa",
"choiceTypeCode1": {
"option1": true,
"option2": true
},
"choiceTypeCode2": {
"option3": true,
"option4": true
}
}
I need it to convert to a Map leaving the nested objects as strings:
stringTypeCode - aaaaa
choiceTypeCode1 - {"option1": true,"option2": true}
choiceTypeCode2 - {"option2": true,"option3": true}
Can it be done in a simple way, preferably without any library?
Edit: or with a library if there is no other simple way.
Edit2: I have a variable number of properties with variable names in the objects.

Parse the json to a map or generic json structure, iterate over the key - value pairs, then create a new map from key - toJsonString(value) pairs. value might be a simple string, json object, number etc...
With a simple Jackson ObjectMapper:
String json = "YOUR JSON HERE";
ObjectMapper mapper = new ObjectMapper();
Iterator<Entry<String, JsonNode>> fields = mapper.readTree(json).fields();
Map<String, String> m = new HashMap<>();
while (fields.hasNext()) {
Entry<String, JsonNode> field = fields.next();
m.put(field.getKey(), mapper.writeValueAsString(field.getValue()));
}
m.entrySet().forEach(e -> System.out.println(e.getKey() + " - " + e.getValue()));
Your example produces:
stringTypeCode - "aaaaa"
choiceTypeCode1 - {"option1":true,"option2":true}
choiceTypeCode2 - {"option3":true,"option4":true}

Related

Convert a List of JSON-objects input into a nested Map

I have a String input in the following format:
Input String: [{ "id":"1", "name":"A", "address":"St 1"},{ "id":"2", "name":"B", "address":"St 2"}, ...]
And I want to be able to convert this to a Map<String, Map<String, String>> format.
So, something like:
Required Output Format: {1: {id:1, name:"A", address: "St 1"} 2: {id:2, name:"B", address: "St 2"}}
I created a class to help in parsing the input:
public class Student{
private String id;
private String name;
private String address;
}
Which I am trying to do through Jackson's ObjectMapper to get the data in the format: List<Student> format and then use Collectors.toMap() to convert it to a Map of Maps format.
All the examples I have seen so far suggest an approach like:
List<Student> student = objectMapper.readValue(inputString, new TypeReference<List<Student>>(){});
Map<String, Student> studentTempMap = student.stream()
.collect(Collectors.toMap(
Student::getId,
Function.identity()
));
Which makes the studentTempMap something like:
{ 1: object of Student("id":"1", "name":"A", "address":"St 1"),
2: object of Student("id":"2", "name":"B", "address":"St 2"),
... }
A brute force approach from here:
create a new Map studentMap
Iterate over keys (which are "id") in studentTempMap.
Then create another Map, temp.
Add keys "id", "name", and "address" and values using something like studentTempMap.get(2).get("id"), and something similar for all the other keys (name and address). Where 2 would be the current iterator key over the Map studentTempMap.
Finally add a key say as 2 (current iterator) and value temp in the studentMap.
I do not want to use this brute force approach as I have a large number of Student objects.
Is there a way through ObjectMapper to get the output directly in the form of Map<String, Map<String, String>> format?
Or is there a way through Collectors.toMap to parse it to the above format?
I want to be able to convert this to a Map<String, Map<String, String>>
If you want to obtain a nested map of strings Map<String,Map<String,String>> as a result, you don't need to convert JSON into a list of POJO.
Instead, you can parse JSON into a list of maps List<Map<String,String>> and then generate a nested map.
String inputString = """
[{ "id":"1", "name":"A", "address":"St 1"},
{ "id":"2", "name":"B", "address":"St 2"}]""";
ObjectMapper objectMapper = new ObjectMapper();
List<Map<String, String>> students = objectMapper.readValue(
inputString, new TypeReference<>() {}
);
Map<String, Map<String, String>> studentMapById = students.stream()
.collect(Collectors.toMap(
map -> map.get("id"), // key
Function.identity(), // value
(left, right) -> left // resolving duplicates
));
studentMapById.forEach((k, v) -> System.out.println(k + " : " + v));
Output:
1 : {id=1, name=A, address=St 1}
2 : {id=2, name=B, address=St 2}

Custom Gson deserialization for primitive arrays

Given the following Json snippet:
String json = "{ \"test\" : [1, 2.5, 4, 5.66] }";
and the call to GSON:
Map obj = gson.fromJson(array, Map.class);
I get a properly parsed Java object that is a LinkedHashMap containing an ArrayList of Doubles. For the purposes of my program, I would like a result to contain primitive versions of arrays and numbers. So preferably, I would get a Map containing a double[] rather than ArrayList<Double>.
I have looked into custom deserialization via the JsonDeserializer<?> interface but have not successfully implemented it such that ArrayLists become []
's and Doubles becomes doubles.
What must I understand to be able to tell GSON to choose Array opposed to ArrayList and double opposed to Double when parsing arbitrary JSON?
You can use new TypeToken<LinkedHashMap <String, double[]>>(){}.getType() instead of Map.class
Here is demo:
public static void main(String[] args) throws IOException, JSONException {
String array = "{ \"test\" : [1, 2.5, 4, 5.66] }";
Gson gson = new Gson();
LinkedHashMap <String, double[]> obj
= gson.fromJson(array, new TypeToken<LinkedHashMap<String, double[]>>(){}.getType());
for (Map.Entry<String, double[]> entry : obj.entrySet()) {
System.out.println("Key = " + entry.getKey() +
", Value = " + Arrays.toString(entry.getValue()));
}
}

Convert json to corresponding HashMap

When converting json to Map using gson, we are having instance of LinkedTreeMap with all values String or Boolean... even numbers are converted to String...
Gson gson = new GsonBuilder().create();
Map<String, Object> result = gson.fromJson(EXAMPLE, new TypeToken<Map<String,Object>>() {}.getType());
How do we convert json to simplest HashMap with corresponding primitive wrappers? Also performance is very important in this case... I want to create as less garbage as possible and resuse parsers...
Is there a way to do that using gson? or any other lib?
Please dont suggest to create special java types for each json... I'll rather navigate trough map...
Here is an example
{ // Hashmap (as no ordering or sorting is essential)
"bool": true, // Boolean
"string": "string", // String
"int" : 123, // Long for all non floats are ok but if possible i'd like this to be Integer if it fits, otherwise Long
"long" : 100000000000000, // Long if Integer cant contain the number...
"double" : 123.123435, // all floating point nubmers will just map to Double
"object" : { // another HashMap
...
}
"array" : [ // Array or some collection like ArrayList or LinkedList
...
]
}
The goal is to convert any json as fast as possible to java Map (or Array if root of json is array) and then use some accessor methods to access data... and not invent java type for every single possible json structure...
Works fine with Jackson Databind library:
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(jsonString, Map.class);
The values in the map will be of their corresponding type.
This test passes:
#Test
public void test() throws Exception {
String jsonString = "{\"bool\": true,"
+ "\"str\":\"strv\","
+ "\"long\": 100000000000000}";
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(jsonString , Map.class);
assertEquals(Long.class, map.get("long").getClass());
assertEquals(Boolean.class, map.get("bool").getClass());
}

Comparing two JSONs

What's the best way to compare two JSON strings in Java? I want to be able to print out only the difference in key/values pairs.
I'm using gson library to convert it to a map and do assert but it displays both JSONs:
Type type = new TypeToken<Map<String, String>>() {}.getType();
Gson gson = new Gson();
Map<String, String> mapExpected = gson.fromJson(expectedJson, type);
Map<String, String> mapResponse = gson.fromJson(jsonResponse, type);
Assert.assertEquals(mapExpected, mapResponse);
Is there a better way to do this?
If you just wanted to compare simple equality, Jackson would make it easy; and just in case this might help it'd be:
JsonNode tree1 = objectMapper.readTree(json1);
JsonNode tree2 = objectMapper.readTree(json2);
if (!tree1.equals(tree2)) { // handle diffing
}
Now; since all JsonNode objects properly implement equality checks (so that ordering of keys does not matter etc), you could use this for recursive diffing to see where and how contents differ. For ObjectNodes you could get key names, remove same (tree1.getFieldNames().removeAll(tree2.getFieldNames()) and so on.
It is tricky, but it can be done.
I would implement a Pair class that holds both String (key and value), with its corresponding equals() and hashcode();
Then put all elements from Map A as Pair in a Set (setA) and all elements from Map B in another Set (setB)
Then calculate
Set<Pair> setA_B = setA.removeAll(setB);
Set<Pair> setB_A = setB.removeAll(setA);
Set<Pair> result = setA_B.addAll(setB_A);
In result there are only the elements that do not match. If all elements match (both original maps are equal) then result is empty.
Using SJuan76's solution, I was able to get just the difference in Key value pairs.
Type type = new TypeToken<Map<String, String>>() {}.getType();
Gson gson = new Gson();
Map<String, String> mapExpected = gson.fromJson(expectedJson, type);
Map<String, String> mapResponse = gson.fromJson(jsonResponse, type);
Set<SimpleEntry<String,String>> expectedSet = new HashSet<SimpleEntry<String, String>>();
Set<SimpleEntry<String, String>> tmpExpectedSet = new HashSet<SimpleEntry<String, String>>();
Set<SimpleEntry<String, String>> responseSet = new HashSet<SimpleEntry<String, String>>();
for (String key : mapExpected.keySet()) {
expectedSet.add(new SimpleEntry<String, String>(key, mapExpected.get(key)));
tmpExpectedSet.add(new SimpleEntry<String, String>(key, mapExpected.get(key)));
}
for (String key : mapResponse.keySet())
responseSet.add((new SimpleEntry<String, String>(key, mapResponse.get(key))));
expectedSet.removeAll(responseSet);
responseSet.removeAll(tmpExpectedSet);
expectedSet.addAll(responseSet);
if (!expectedSet.isEmpty()) {
for (SimpleEntry<String, String> diff : expectedSet)
log.error(diff.getKey() + ":" + diff.getValue());
}

Parse JSON string to Dictionary<String, Integer> with Gson

I have a JSON string which looks as following: {"altruism":1,"amazon":6}
What I want to have is a HashMap<String, Integer> with two entries afterwards.
Key: altruism Value: 1
Key: amazon Value:6
I really can't figure out how to do this. Normally there are objects parsed from JSON strings, but that's not the case here.
Gson makes what you're trying to do relatively easy. Following is a working example.
// input: {"altruism":1,"amazon":6}
String jsonInput = "{\"altruism\":1,\"amazon\":6}";
Map<String, Integer> map = new Gson().fromJson(jsonInput, new TypeToken<HashMap<String, Integer>>() {}.getType());
System.out.println(map); // {altruism=1, amazon=6}
System.out.println(map.getClass()); // class java.util.HashMap
System.out.println(map.keySet().iterator().next().getClass()); // class java.lang.String
System.out.println(map.get("altruism").getClass()); // class java.lang.Integer

Categories