How to deserialize JSON into flat, Map-like structure? - java

Have in mind that the JSON structure is not known before hand i.e. it is completely arbitrary, we only know that it is JSON format.
For example,
The following JSON
{
"Port":
{
"#alias": "defaultHttp",
"Enabled": "true",
"Number": "10092",
"Protocol": "http",
"KeepAliveTimeout": "20000",
"ThreadPool":
{
"#enabled": "false",
"Max": "150",
"ThreadPriority": "5"
},
"ExtendedProperties":
{
"Property":
[
{
"#name": "connectionTimeout",
"$": "20000"
}
]
}
}
}
Should be deserialized into Map-like structure having keys like (not all of the above included for brevity):
port[0].alias
port[0].enabled
port[0].extendedProperties.connectionTimeout
port[0].threadPool.max
I am looking into Jackson currently, so there we have:
TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {};
Map<String, String> o = objectMapper.readValue(jsonString, typeRef);
However, the resulting Map instance is basically a Map of nested Maps:
{Port={#alias=diagnostics, Enabled=false, Type=DIAGNOSTIC, Number=10033, Protocol=JDWP, ExtendedProperties={Property={#name=suspend, $=n}}}}
While I need flat Map with flatten keys using "dot notation", like the above.
I would rather not implement this myself, although at the moment I don't see any other way...

You can do this to traverse the tree and keep track of how deep you are to figure out dot notation property names:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ValueNode;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.junit.Test;
public class FlattenJson {
String json = "{\n" +
" \"Port\":\n" +
" {\n" +
" \"#alias\": \"defaultHttp\",\n" +
" \"Enabled\": \"true\",\n" +
" \"Number\": \"10092\",\n" +
" \"Protocol\": \"http\",\n" +
" \"KeepAliveTimeout\": \"20000\",\n" +
" \"ThreadPool\":\n" +
" {\n" +
" \"#enabled\": \"false\",\n" +
" \"Max\": \"150\",\n" +
" \"ThreadPriority\": \"5\"\n" +
" },\n" +
" \"ExtendedProperties\":\n" +
" {\n" +
" \"Property\":\n" +
" [ \n" +
" {\n" +
" \"#name\": \"connectionTimeout\",\n" +
" \"$\": \"20000\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
#Test
public void testCreatingKeyValues() {
Map<String, String> map = new HashMap<String, String>();
try {
addKeys("", new ObjectMapper().readTree(json), map);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(map);
}
private void addKeys(String currentPath, JsonNode jsonNode, Map<String, String> map) {
if (jsonNode.isObject()) {
ObjectNode objectNode = (ObjectNode) jsonNode;
Iterator<Map.Entry<String, JsonNode>> iter = objectNode.fields();
String pathPrefix = currentPath.isEmpty() ? "" : currentPath + ".";
while (iter.hasNext()) {
Map.Entry<String, JsonNode> entry = iter.next();
addKeys(pathPrefix + entry.getKey(), entry.getValue(), map);
}
} else if (jsonNode.isArray()) {
ArrayNode arrayNode = (ArrayNode) jsonNode;
for (int i = 0; i < arrayNode.size(); i++) {
addKeys(currentPath + "[" + i + "]", arrayNode.get(i), map);
}
} else if (jsonNode.isValueNode()) {
ValueNode valueNode = (ValueNode) jsonNode;
map.put(currentPath, valueNode.asText());
}
}
}
It produces the following map:
Port.ThreadPool.Max=150,
Port.ThreadPool.#enabled=false,
Port.Number=10092,
Port.ExtendedProperties.Property[0].#name=connectionTimeout,
Port.ThreadPool.ThreadPriority=5,
Port.Protocol=http,
Port.KeepAliveTimeout=20000,
Port.ExtendedProperties.Property[0].$=20000,
Port.#alias=defaultHttp,
Port.Enabled=true
It should be easy enough to strip out # and $ in the property names, although you could end up with collisions in key names since you said the JSON was arbitrary.

How about using the json-flattener. https://github.com/wnameless/json-flattener
BTW, I am the author of this lib.
String flattenedJson = JsonFlattener.flatten(yourJson);
Map<String, Object> flattenedJsonMap = JsonFlattener.flattenAsMap(yourJson);
// Result:
{
"Port.#alias":"defaultHttp",
"Port.Enabled":"true",
"Port.Number":"10092",
"Port.Protocol":"http",
"Port.KeepAliveTimeout":"20000",
"Port.ThreadPool.#enabled":"false",
"Port.ThreadPool.Max":"150",
"Port.ThreadPool.ThreadPriority":"5",
"Port.ExtendedProperties.Property[0].#name":"connectionTimeout",
"Port.ExtendedProperties.Property[0].$":"20000"
}

how about that:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gson.Gson;
/**
* NOT FOR CONCURENT USE
*/
#SuppressWarnings("unchecked")
public class JsonParser{
Gson gson=new Gson();
Map<String, String> flatmap = new HashMap<String, String>();
public Map<String, String> parse(String value) {
iterableCrawl("", null, (gson.fromJson(value, flatmap.getClass())).entrySet());
return flatmap;
}
private <T> void iterableCrawl(String prefix, String suffix, Iterable<T> iterable) {
int key = 0;
for (T t : iterable) {
if (suffix!=null)
crawl(t, prefix+(key++)+suffix);
else
crawl(((Entry<String, Object>) t).getValue(), prefix+((Entry<String, Object>) t).getKey());
}
}
private void crawl(Object object, String key) {
if (object instanceof ArrayList)
iterableCrawl(key+"[", "]", (ArrayList<Object>)object);
else if (object instanceof Map)
iterableCrawl(key+".", null, ((Map<String, Object>)object).entrySet());
else
flatmap.put(key, object.toString());
}
}

org.springframework.integration.transformer.ObjectToMapTransformer from Spring Integration produces desired result.
By default it has shouldFlattenKeys property set to true and produces flat maps (no nesting, value is always simple type). When shouldFlattenKeys=false it produces nested maps
ObjectToMapTransformer is meant to be used as part of integration flow, but it is perfectly fine to use it in stand-alone way. You need to construct org.springframework.messaging.Message with payload of transformation input. transform method returns org.springframework.messaging.Message object with payload that is Map
import org.springframework.integration.transformer.ObjectToMapTransformer;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
Message message = new GenericMessage(value);
ObjectToMapTransformer transformer = new ObjectToMapTransformer();
transformer.setShouldFlattenKeys(true);
Map<String,Object> payload = (Map<String, Object>) transformer
.transform(message)
.getPayload();
Side note: It is probably overkill to add Spring Integration to the classpath just to use single class, but you may check implementation of this class and write similar solution on your own. Nested map is produced by Jackson (org.springframework.integration.support.json.JsonObjectMapper#fromJson(payload, Map.class)), then mapis travered recursively, flattening all values that are collections.

I also had to solve a similar problem in my project and found out that springframework.vault has a method flatten() to do the same. Below is a sample code.
//Json string to Map<String, Object>
String data = "Your json as string"
final ObjectMapper mapper = new ObjectMapper();
final MapType type = mapper.getTypeFactory().constructMapType(
Map.class, String.class, Object.class);
final Map<String, Object> map = mapper.readValue(data, type);
//Using springframework.vault flatten method
Map<String, String> keyMap = JsonMapFlattener.flattenToStringMap(map);
//Input
{"key": {"nested": 1}, "another.key": ["one", "two"] }
//Output
key.nested=1
another.key[0]=one
another.key[1]=two
Remember to add the dependency
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
For more info, refer https://docs.spring.io/spring-vault/docs/current/api/org/springframework/vault/support/JsonMapFlattener.html

You can achieve something like that using the Typesafe Config Library as in the following example:
import com.typesafe.config.*;
import java.util.Map;
public class TypesafeConfigExample {
public static void main(String[] args) {
Config cfg = ConfigFactory.parseString(
" \"Port\":\n" +
" {\n" +
" \"#alias\": \"defaultHttp\",\n" +
" \"Enabled\": \"true\",\n" +
" \"Number\": \"10092\",\n" +
" \"Protocol\": \"http\",\n" +
" \"KeepAliveTimeout\": \"20000\",\n" +
" \"ThreadPool\":\n" +
" {\n" +
" \"#enabled\": \"false\",\n" +
" \"Max\": \"150\",\n" +
" \"ThreadPriority\": \"5\"\n" +
" },\n" +
" \"ExtendedProperties\":\n" +
" {\n" +
" \"Property\":\n" +
" [ \n" +
" {\n" +
" \"#name\": \"connectionTimeout\",\n" +
" \"$\": \"20000\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}");
// each key has a similar form to what you need
for (Map.Entry<String, ConfigValue> e : cfg.entrySet()) {
System.out.println(e);
}
}
}

If you know the structure beforehand, you can define a Java class and use gson to parse JSON into an instance of that class:
YourClass obj = gson.fromJson(json, YourClass.class);
If not, then I'm not sure what you're trying to do. You obviously can't define a class on-the-fly so accessing the parsed JSON using dot-notation is out of the question.
Unless you want something like:
Map<String, String> parsed = magicParse(json);
parsed["Port.ThreadPool.max"]; // returns 150
If so, then traversing your map of maps and building a "flattened" map doesn't seem too much of a problem.
Or is it something else?

Related

how to take json data in java map or array

I have the following data as json and I want to take it in java
"endPoints": {
"northAmerica": "https://ad-api.com",
"europe": "https://ad-api-eu.com",
"farEast": "https://ad-api-fe.com"
}
I have tried the below code but not working.
Map<String, Object> endPoints = objectMapper.readValue(JsonParser
.parseString(additionalInfo().get("endPoints").toString())
.getAsJsonObject(), new TypeReference<Map<String, Object>>() {
});
anyone can help me how to do it?
First you need to make your data is a valid json format,then you can use ObjectMapper to do it
public static void testJsonConvert() throws JsonProcessingException {
String data = "{\n" +
" \"endPoints\":{\n" +
" \"northAmerica\":\"https://ad-api.com\",\n" +
" \"europe\":\"https://ad-api-eu.com\",\n" +
" \"farEast\":\"https://ad-api-fe.com\"\n" +
" }\n" +
"}";
Map<String, Object> map = new ObjectMapper().readValue(data, HashMap.class);
System.out.println(map);
}
Test result:
Any JSON string can be mapped to HashMap data structure as Key and Value pair.
There is an answer already in the thread.
But If you want a map of endpoints, like North America & Europe, you need to go a level deeper.
ObjectMapper from Jackson Core library will help.
First, get a HashMap of the data, then again get the endpoint from the HashMap.
String data = "{\n" +
" \"endPoints\":{\n" +
" \"northAmerica\":\"https://ad-api.com\",\n" +
" \"europe\":\"https://ad-api-eu.com\",\n" +
" \"farEast\":\"https://ad-api-fe.com\"\n" +
" }\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(data, HashMap.class);
Map<String, Object> endPointMap = (Map<String, Object>) map.get("endPoints");
System.out.println(endPointMap);
result:
{northAmerica=https://ad-api.com, europe=https://ad-api-eu.com, farEast=https://ad-api-fe.com}

Get list of keys in Complex JSON Object (Java 8)

I am dealing with a JSON that looks like this :-
{
"key1": {
"key1.1": {
"nestedkey1": "something",
"nestedkey2": "something",
"nestedkey3": "Something"
},
"key1.2": {
"nestedkey1": "something",
"nestedkey2": "something",
"nestedkey3": "Something"
}
},
"key2": {
"key2.1": {
"nestedkey1": "something",
"nestedkey2": "something",
"nestedkey3": "Something"
},
"key2.2": {
"nestedkey1": "something",
"nestedkey2": "something",
"nestedkey3": "Something"
}
}...
And I don't know all the keys. I wish to obtain all the keys so that I can create a Map<String, Object> out of this. That map should look something like ("key1" -> Corresponding object)...
Is there a simple way to do this in Java?
String filePath ="src/main/resources/json/1.json";
FileReader reader = new FileReader(filePath);
JSONParser parser = new JSONParser();
JSONObject jsonObject = (JSONObject) parser.parse(reader);
Set<String> setKeys= jsonObject.keySet();
Map<String,Object> yourMap= new HashMap<>();
for (String key:setKeys) {
yourMap.put(key,jsonObject.get(key));
}
yourMap is ready!
Using Jackson JSON library, this json may be parsed as a Map<String, Object> using TypeReference:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
public class JsonTest {
public static void main(String[] args) throws JsonProcessingException {
String json = "{\n"
+ "\"key1\": {\n"
+ " \"key1.1\": {\n"
+ " \"nestedkey1\": \"something\",\n"
+ " \"nestedkey2\": \"something\",\n"
+ " \"nestedkey3\": \"Something\"\n"
+ " },\n"
+ " \"key1.2\": {\n"
+ " \"nestedkey1\": \"something\",\n"
+ " \"nestedkey2\": \"something\",\n"
+ " \"nestedkey3\": \"Something\"\n"
+ " }\n"
+ "},\n"
+ "\"key2\": {\n"
+ " \"key2.1\": {\n"
+ " \"nestedkey1\": \"something\",\n"
+ " \"nestedkey2\": \"something\",\n"
+ " \"nestedkey3\": \"Something\"\n"
+ " },\n"
+ " \"key2.2\": {\n"
+ " \"nestedkey1\": \"something\",\n"
+ " \"nestedkey2\": \"something\",\n"
+ " \"nestedkey3\": \"Something\"\n"
+ " }\n"
+ "}}"; // make sure the json is valid and closing } is available
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(json, new TypeReference<>() {});
System.out.println(map);
}
}
To get the list of all keys, a recursive method needs to be implemented to iterate over the entries of the top-level map and add keys:
public static List<String> getKeys(Map<String, Object> map) {
List<String> keys = new ArrayList<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
keys.add(entry.getKey());
if (entry.getValue() instanceof Map) {
Map<String, Object> nested = (Map<String, Object>) entry.getValue();
keys.addAll(getKeys(nested));
}
}
return keys;
}
Similarly, a list of "prefixed" keys may be created:
public static List<String> getPrefixedKeys(String prefix, Map<String, Object> map) {
List<String> keys = new ArrayList<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = prefix + entry.getKey();
keys.add(key);
if (entry.getValue() instanceof Map) {
Map<String, Object> nested = (Map<String, Object>) entry.getValue();
keys.addAll(getPrefixedKeys(key + "/", nested));
}
}
return keys;
}
// test
System.out.println(getPrefixedKeys("/", map));
Output:
[/key1, /key1/key1.1, /key1/key1.1/nestedkey1, /key1/key1.1/nestedkey2, /key1/key1.1/nestedkey3,
/key1/key1.2, /key1/key1.2/nestedkey1, /key1/key1.2/nestedkey2, /key1/key1.2/nestedkey3,
/key2, /key2/key2.1, /key2/key2.1/nestedkey1, /key2/key2.1/nestedkey2, /key2/key2.1/nestedkey3,
/key2/key2.2, /key2/key2.2/nestedkey1, /key2/key2.2/nestedkey2, /key2/key2.2/nestedkey3]
The computing task is to output field names of all levels in JSON records of indefinite number of levels. The code will be lengthy if you try to handle such a scenario in Java.
It is convenient to do this in SPL, the open-source Java package. Three lines of code are enough:
A
B
1
=i=0,json(file("records.json").read())
2
func recurse(r)
>i+=1,r.fno().run(tmp=eval("r.#"/~),B1=B1.to(:i-1)|r.fname(~),output(B1.concat("->")),if(ifr(tmp),func(recurse,tmp),(B1=B1|tmp)))
3
=func(recurse,A1)
SPL offers JDBC driver to be invoked by Java. Just store the above SPL script as jsonkeys.splx and invoke it in Java as you call a stored procedure:
…
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
st = con.prepareCall("call jsonkeys()");
st.execute();
…

How to merge two json Strings into one in java

If we have given 2 Strings of type json, how can we merge them into single json String in java?
e.g.
String json1 = {
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S"
}
}
}
String json2 = {
"glossary": {
"title": "person name",
"age": "25"
}
}
Should produce
String mergedJson = {
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S"
},
"age": "25"
}
}
Below code should do it, with a couple of assumptions:
You are using ObjectMapper of Jackson library (com.fasterxml.jackson.databind.ObjectMapper) to serialise/deserialise json
fields of json1 will always overwrite json2 while merging
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map1 = mapper.readValue("json1", Map.class);
Map<String, Object> map2 = mapper.readValue("json2", Map.class);
Map<String, Object> merged = new HashMap<String, Object>(map2);
merged.putAll(map1);
System.out.println(mapper.writeValueAsString(merged));
Here is the code which recursively merges two jsons. This outputs as excepted:
NOTE: This is deep merge, not shallow merge ( similar concept used for shall vs deep copy)
private static JsonObject merge(JsonObject json1Obj, JsonObject json2Obj) {
Set<Entry<String, JsonElement>> entrySet1 = json1Obj.entrySet();
for (Entry<String, JsonElement> entry : entrySet1) {
String key1 = entry.getKey();
if (json2Obj.get(key1) != null) {
JsonElement tempEle2 = json2Obj.get(key1);
JsonElement tempEle1 = entry.getValue();
if (tempEle2.isJsonObject() && tempEle1.isJsonObject()) {
JsonObject mergedObj = merge(tempEle1.getAsJsonObject(),
tempEle2.getAsJsonObject());
entry.setValue(mergedObj);
}
}
}
Set<Entry<String, JsonElement>> entrySet2 = json2Obj.entrySet();
for (Entry<String, JsonElement> entry : entrySet2) {
String key2 = entry.getKey();
if (json1Obj.get(key2) == null) {
json1Obj.add(key2, entry.getValue());
}
}
return json1Obj;
}
Consider using a library that does this job for you, like JSON Merge, available at Maven Central.
You will get the desired result with a single line of code (you may ignore the String declarations if you already have the JSONObjects previously loaded):
String json1 = "{\n"
+ " \"glossary\": {\n"
+ " \"title\": \"example glossary\",\n"
+ " \"GlossDiv\": {\n"
+ " \"title\": \"S\"\n"
+ " }\n"
+ " }\n"
+ " }";
String json2 = "{\n"
+ " \"glossary\": {\n"
+ " \"title\": \"person name\",\n"
+ " \"age\": \"25\"\n"
+ " }\n"
+ " }";
JSONObject result = new JsonMerger<>(JSONObject.class).merge(json2, json1);
Note: the first JSON parameter passed to the merge method will always have more precedence/importance than the second one in case of key collisions.
This library works with Jackson, Gson, and other JSON providers as well.
So I'm quite late to the party but I wanted to share my solution if anybody stumbles across this.
You can deeply merge two json strings with com.fasterxml.jackson.core:jackson-databind ObjectMapper.readerForUpdating().
In this scenario you pass in two Json as String and merge them via readerForUpdating (untested code):
public String mergeJsonStrings(String json1, String json2) {
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.readerForUpdating(json1);
String result = reader.readValue(json2);
return result;
}
I used similar code to merge a property into an existing dataset. In this example the SomeProperties class contains a hashmap which holds the properties for a specific user. The passed in propertiesString is a single dot separated property e.g. some.random.property=value. The property will be transformed into a JsonNode with com.fasterxml.jackson.dataformat:jackson-dataformat-properties.
public SomeProperties mergeProperties(SomeProperties someProperties, String propertiesString) {
JavaPropsMapper javaPropsMapper = new JavaPropsMapper();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = javaPropsMapper.readTree(propertiesString);
ObjectReader objectReader = mapper.readerForUpdating(someProperties.getProperties());
HashMap<String, Object> mergedProperties = objectReader.readValue(jsonNode);
someProperties.setProperties(mergedProperties);
return someProperties;
}
In both cases everything passed into objectReader.readValue() will override existing keys.

How to get all the keys of json in java using Gson or org.json library? [duplicate]

This question already has answers here:
How can I access each key and value in JSONArray
(2 answers)
Closed 7 years ago.
For example from the following json, id, items, fromNumber should be retrieved.
The json can be having n number of nesting.
{
"items": [{
"id": 633706061003,
"fromNumber": "16572307534",
"contact": {
"id": 499354453003,
"homePhone": "16572307534"
},
"records": [{
"id": 353389055003,
"result": "LA",
"recordings": [{
"id": 16427622003,
}]
}]
}],
"limit": 100,
"offset": 0,
"totalCount": 5949
}
I have implemented the below code, but in this code I have to tell the level of nesting
String prefix = "";
/*
* Root Array
*/
JsonArray rootArray = new JsonParser().parse(json).getAsJsonArray();
for (int i = 0; i < rootArray.size(); i++) {
/*
* Single object in root array while iterations. for id, properties, tags etc.
*/
JsonObject rootArrayObject = rootArray.get(i).getAsJsonObject();
Set<Map.Entry<String, JsonElement>> rootArrayObjectEntrySet = rootArrayObject.entrySet();
/*
* Getting the keys and values of RootArray Single Object
*/
for (Map.Entry<String, JsonElement> entryChild : rootArrayObjectEntrySet) {
prefix = entryChild.getKey();
/*
* Getting each object, key or array as an element
*/
JsonElement rootArrayObjElement = rootArrayObject.get(entryChild.getKey());
if(rootArrayObjElement.isJsonArray()){
/*
* Getting array's object in single object of root array. Example: tags
*/
JsonArray rootArrayObjArray = rootArrayObjElement.getAsJsonArray();
for (int j = 0; j < rootArrayObjArray.size(); j++) {
}
}else if(rootArrayObjElement.isJsonObject()){
/*
* Single object in root array
*/
JsonObject rootArrayObjObj = rootArrayObjElement.getAsJsonObject();
Set<Map.Entry<String, JsonElement>> rootArrayObjObjEntrySet = rootArrayObjObj.entrySet();
for (Map.Entry<String, JsonElement> rootArrayObjObjChild : rootArrayObjObjEntrySet) {
/*
* Getting each object, key or array as an element
*/
JsonElement rootArrayObjObjElement = rootArrayObjObj.get(rootArrayObjObjChild.getKey());
if(rootArrayObjObjElement.isJsonPrimitive()){
}else if(rootArrayObjObjElement.isJsonArray()){
JsonArray rootArrayObjArray = rootArrayObjObjElement.getAsJsonArray();
for (int j = 0; j < rootArrayObjArray.size(); j++) {
}
}
}
}else if(rootArrayObjElement.isJsonPrimitive()){
}
}
}
You can try something like below :
import com.google.gson.Gson;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class GSonWay {
public static void main(String[] args) throws Exception {
String jsonString = "{\n"
+ "\"items\": [{\n"
+ " \"id\": 633706061003,\n"
+ " \"fromNumber\": \"16572307534\",\n"
+ "\n"
+ " \"contact\": {\n"
+ " \"id\": 499354453003,\n"
+ " \"homePhone\": \"16572307534\"\n"
+ " },\n"
+ "\n"
+ " \"records\": [{\n"
+ " \"id\": 353389055003,\n"
+ " \"result\": \"LA\",\n"
+ " \"recordings\": [{\n"
+ " \"id\": 16427622003\n"
+ " }]\n"
+ " }]\n"
+ "}],\n"
+ "\"limit\": 100,\n"
+ "\"offset\": 0,\n"
+ "\"totalCount\": 5949\n"
+ "\n"
+ "}";
List keys1 = getKeysFromJson(jsonString);
System.out.println(keys1.size());
System.out.println(keys1);
}
static List getKeysFromJson(String jsoString) throws Exception {
Object things = new Gson().fromJson(jsoString, Object.class);
List keys = new ArrayList();
collectAllTheKeys(keys, things);
return keys;
}
static void collectAllTheKeys(List keys, Object o) {
Collection values = null;
if (o instanceof Map) {
Map map = (Map) o;
keys.addAll(map.keySet()); // collect keys at current level in hierarchy
values = map.values();
} else if (o instanceof Collection) {
values = (Collection) o;
} else{return;}
for (Object value : values) {
collectAllTheKeys(keys, value);
}
}
}
Output :
[items, limit, offset, totalCount, id, fromNumber, contact, records, id, homePhone, id, result, recordings, id]
Gson is one of the best ways to decode JSON file. Gson requires POJO classes, which can be manually generated but is tiresome. The best way to develop POJO classes is to visit jsonschema2pojo.org. They will generate the required POJO classes for you.
Let's say the class is JsonData.java
So in your code, you have to create a Gson object as well as an object of JsonData class.
String jsonFile = "____let this be your json data___"
Gson gson = new Gson();
JsonData jsonData = new JsonData();
jsonData = gson.fromJson(jsonFile, JsonData.class);
now jsonData will have all the data's retrieved from the json file. If you want to get fromNumber you can just call getFromNumber() which will be a method inside JsonData class. Similarly you can call other values too.
Try using third party services like jsonschema2pojo.org for improving your productivity.

Convert JSON to hash map with Jackson- Java

Before this is marked as a duplicate please read the question (I did look at similar ones). Thank you.
For simplicity, assume I have JSON like this:
{
"clients" : [
{
"name" : "client 1",
"id" : 1
},
{
"name" : "client 2",
"id" : 2
}
],
"other" : {
"something" : ""
}
...
}
So I want to create a hash map of only the clients and their fields. The basic question is how would I go about doing this using Jackson methods for a single JSON array like clients? I've tried to look online but all of the examples that I have seen either don't use Jackson or only are for a single JSON object like so:
HashMap<String, String>[] values = new ObjectMapper().readValue(jsonString, new TypeReference<HashMap<String, String>[]>() {});
I've also seen Gson examples and I know I can do some string parsing magic:
jsonSting = jsonString.substring(jsonString.indexOf("["), (jsonString.indexOf("]")+1))
to get it in a format that I can use, but I want to try it with Jackson to avoid importing another library. Any ideas?
Rephrasing the question:
So if I only had a list of clients like so:
jsonString = [{"name" : "client 1","id" : 1},{"name" : "client 2","id" : 2}]
then I could just do:
HashMap[] values = new ObjectMapper().readValue(jsonString, new TypeReference[]>() {});
to get what I want. I am basically asking if there is a way using Jackson methods to get the jsonString above from the large JSON section on top. I know I can easily do it with this example with string parsing but there will be more complex situations in the future and string parsing is not really considered best practice
You can extract a part of the JSON tree using the Jackson tree model API and then convert it to an array of maps.
Here is an example:
public class JacksonReadPart {
public static final String JSON = "{\n" +
" \"clients\" : [\n" +
" {\n" +
" \"name\" : \"client 1\",\n" +
" \"id\" : 1\n" +
" },\n" +
" {\n" +
" \"name\" : \"client 2\",\n" +
" \"id\" : 2\n" +
" }\n" +
"],\n" +
" \"other\" : {\n" +
" \"something\" : \"\"\n" +
" }\n" +
"\n" +
"}";
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(JSON).path("clients");
// non type safe
Map<String, Object>[] clients = mapper.treeToValue(node, Map[].class);
System.out.println(Arrays.toString(clients));
// type safe
JsonParser parser = mapper.treeAsTokens(node);
clients = parser.readValueAs(new TypeReference<Map<String, Object>[]>() {});
System.out.println(Arrays.toString(clients));
}
}
Output:
[{name=client 1, id=1}, {name=client 2, id=2}]
[{name=client 1, id=1}, {name=client 2, id=2}]

Categories

Resources