I have a json with complex nested. values. But there is one unique key value pair in the json.. without traversing , I need to retrieve the value .
for example:
name : "xyz". In the entire complex nested json , I need to return a json with all the values of name key.
I am currently using org.simple.json. but its very complex as I have to traverse through the entire nested json and build JSONArray etc.
maybe you can use java8 stream,the way is very easy to solve your problem.
example:
[
{
"name":"xyz",
"value":"1"
},
{
"name":"xyz",
"value":"2"
},
{
"name":"xyz",
"value":"3"
}
]
and then,you can create a class:
import lombok.Data;
#Data
public class JsonTest {
private String name;
private String value;
}
finally, like this. btw, I am currently using other json package.
List<JsonTest> jsonArray = JSON.parseArray(json, JsonTest.class);
List<String> valueList = jsonArray.stream()
.filter(e -> "xyz".equals(e.getName()))
.map(JsonTest::getValue)
.collect(Collectors.toList());
Related
I want to map JSON Object to Java class with custom structure.
For example:
public class RestAttributesMappingDTO {
Map<String, Object> details = new LinkedHashMap<>();
#JsonAnySetter
void setDetail(String key, Object value) {
details.put(key, value);
}
}
As you can see, annotation #JsonAnySetter have been used for mapping.
Its ok for any flat structure.
But if I want to map object with unlimited (custom) nested structure?
For example:
{
name: "",
age: "",
job: [{
job1: {
title: "",
ZIP: ""
}
}]
}
Arises recursive algorithm of creating map without #JsonAnySetter.
But is there any way to build this unlimited nested map with Jackson?
Jackson by default deserialises:
JSON Object to Map where keys are String-s.
JSON Array to List of objects or primitives.
In your example, you can keep like it is but it would need casting and checking types in runtime. Other option is to use com.fasterxml.jackson.databind.JsonNode class and method could look like:
void setDetail(String key, JsonNode value)
But you can even skip this method and #JsonAnySetter annotation and use it in class directly:
public class RestAttributesMappingDTO {
private JsonNode details;
//getters, setters
}
JsonNode allows to iterate over key-value pairs.
See some other related questions:
Mapping Json Array to POJO using Jackson
How to modify the value of a JsonNode recursively using Jackson
How to convert a JsonNode instance to an actual pojo
I have the following JSON data being fetched from an external file source.
{
.... More complicated structures within this JSON format above but I only care about the following
"feature": {
"100": {
"DATA1": [
{
"header": "head1",
"someId": "123"
}
]
}
}
}
I am trying to capture the following portion as List < Data >
"DATA1": [
{
"header": "head1",
"someId": "123"
}
]
but unable to do so. Getting following error:
Cannot cast LinkedHashMap to List.
Method which fetched the data and try to assign it to List< Data >
private void getData(){
AllDataFromFile all = someFetchAPI(); // Got every JSON data in the file.
// capturing the portion that I want which I do get.
Map<String, Map<String,Object>> feature = all.getFeature();
Map<String, Object> allData = feature.get("100");
List<Data> dataList = allData.get("DATA1");
}
No compilation nor run time errors from above but dataList is not a List of Data.
Instead it is a List of LinkedHashMap when I see in debug mode. Why?
And how can I turn this into a List of Data instead?
Since it doesn't map as a List< Data >, I am unable to perform operations such as follows.
dataList.get(0).getHeader().
Unable to cast it either and getting same error:
Cannot cast LinkedHashMap to List.
Please advice. Thanks.
AllDataFromFile class
#Getter
#Setter
#JsonInclude(Include.NON_NULL)
public class AllDataFromFile {
private Map<String, Map<String,Object>> feature;
}
Data class
#JsonInclude(Include.NON_NULL)
public class Data implements Comparable<Data>, Cloneable{
private String header;
private String someId;
#Override
public int compareTo(Data o) {
int result = // some logic
return result;
}
}
EDIT:
This is data format inside List
result = {ArrayList#18590} size = 1
0 = {LinkedHashMap#18593} size = 2
"header" -> "header1"
"someId" -> "id1"
Getting following error when I try to match the Object's value to be more specific as follows.
private Map<String, Map<String, List<Data>>> feature;
Able to go back to getting LinkedHashMap (no use, back to Square one) if I go for the following:
private Map<String, Map<String, List>> feature;
Error:
Could not read JSON: Class java.util.LinkedHashMap not subtype of
[simple type, class com.a.a.a.a.a.Data] (through reference chain:
com.b.b.b.b.b.b.AllDataFromFile["feature"]->java.util.LinkedHashMap["100"]->java.util.LinkedHashMap["DATA1"]->java.util.ArrayList[0])
Jackson uses reflection to determine what type it needs to deserialize too.
But starting from AllDataFromFile, it cannot determine that the content is actually a List of Data objects, so it falls back to default Lists (ArrayList) and Maps(LinkedHashMap).
I think if you provide more information in the type declaration, jackson might figure it out:
public class AllDataFromFile {
private Map<String, Map<String, List<Data>>> feature;
}
How do I remove some fields with a specified name from a JSON string recursively ?
For example, I want to remove the field "secondName" from the following JSON:
INPUT:
{
"name" : "abc",
"secondName": "qwe",
"add" : "abcd",
"moreDetails" : {
"secondName": "qwe",
"age" : "099"
}
}
OUTPUT:
{
"name" : "abc",
"add" : "abcd",
"moreDetails" : {
"age" : "099"
}
}
I have to remove some fields from a lot of different JSONs with different structures/schema, so I won't be able to deserialize/serialize to/from a POJO.
Gson deserializes any valid Json to LinkedTreeMap, like:
LinkedTreeMap<?,?> ltm = new Gson().fromJson(YOUR_JSON, LinkedTreeMap.class);
Then it is just about making some recursive methods to do the clean up:
public void alterEntry(Entry<?, ?> e) {
if(e.getValue() instanceof Map) {
alterMap((Map<?, ?>) e.getValue());
} else {
if(e.getKey().equals("secondName")) { // hard coded but you see the point
e.setValue(null); // we could remove the whole entry from the map
// but it makes thing more complicated. Setting null
// achieves the same.
}
}
}
public void alterMap(Map<?,?> map) {
map.entrySet().forEach(this::alterEntry);
}
Usage:
alterMap(ltm);
You could try storing the JSON as a JSONObject, iterate over the keys using jsonObject.names() and remove the entries using jsonObject.remove(key).
You can do like below if you know the schema and heirarchy:
JsonObject jsonObj= gson.fromJson(json, JsonObject.class);
jsonObj.getAsJsonObject("moreDetails").remove("secondName");
System.out.println(jsonObj.getAsString());
refer this for more info Remove key from a Json inside a JsonObject
else you need to write a dynamic function which will check each and every element of JSON object and try to find the secondName element in it and remove it.
So consider here as you have multiple nested objects then you need to write a function which will iterate over each element and check its type if its again a jsonObject call the same method recursively or iteratively to check against current element, in each check you need to also verify that the key, if it matches with the key which has to be removed then you can remove it and continue the same.
for a hint on how to check a value type of JSON see this How to check the type of a value from a JSONObject?
I am trying to serialize a list of JSON blobs and put certain keys into a HashTable during the serialization. Here is an example of my JSON:
[
{
"name": "sally",
"id": 1,
"eye_color": "green"
},
{
"name": "jack",
"id": 2,
"eye_color": "blue"
},
{
"name": "jane",
"id": 3,
"eye_color": "brown"
}
]
What I am looking for specifically is a POJO (or set of POJOs) which can serialize the above JSON like so with Jackson assuming the above JSON is in a file called above_json.json:
MyPOJO pojo = objectMapper.readValue(new File("above_json.json"), MyPOJO.class);
I want the result of the serialization to give me a HashTable (or an Object which encapsulates the HashTable) where the HashTable key is the value of name and the Hashtable value is the value of the corresponding id above.
Assuming we serialized the above JSON in this fashion, I would want to access the HashTable like so:
myTable.get("jane")
result: 3
myTable.get("jack")
result: 2
myTable.get("Jill")
result: null
I know how to serialize basic JSON with Jackson. I have an example like below:
JSON Input:
"Parameter":{
"Name":"Parameter-Name",
"Value":"Parameter-Value"
}
POJO to serialize above simple JSON:
public class Parameter {
#JsonProperty("Name")
public String name;
#JsonProperty("Value")
public String value;
}
But obviously this type of setup does not put the results into a HashTable. I need a POJO like what I have in this example which will serialize JSON directly into a HashTable
I don't think that is possible.
You serialize this json into a list of pojos, and have a utility function to generate the hashtable in the way you desire from the list of pojos.
Create a POJO for holding the properties you are interested in.
#JsonIgnoreProperties(ignoreUnknown = true)
private static class MyPOJO {
#JsonProperty("name")
private String name;
#JsonProperty("id")
private Integer id;
public Integer getId() {
return id;
}
public String getName() {
return name;
}
}
Deserialize the contents of the file into List<MyPOJO>
List<MyPOJO> myPOJO = mapper.readValue(new File(".."), new TypeReference<List<MyPOJO>>(){});
Stream the contents of the map to construct a map whose key is the name and value is the id.
Map<String, Integer> map = myPOJO.stream()
.collect(Collectors.toMap(MyPOJO::getName, MyPOJO::getId));
First of all, you probably don't want to use a HashTable, as it's considered to be an obsolete type (see here).
Either use a HashMap or if you want thread safety, a ConcurrentHashMap or a thread-unsafe Map backed by Collections.synchronized[...] and referenced to within synchronized statements.
Secondly, you can use a TypeReference to de-serialize as your desired type.
Finally, your JSON's syntax is incorrect: it starts with a square bracket ([) and ends with a curly bracket (}), which is technically unparseable.
Assuming you want an array of Maps here (e.g. HashMap<String, String>[]), here is some suitable code, provided you replace the last curly bracket with a square one:
// the map array
Map<String, String>[] map = null;
map = om.readValue(yourFile, new TypeReference<HashMap<String, String>[]>() {});
// prints the de-serialized contents
System.out.println(Arrays.toString(map));
Edit
Since you have now edited your JSON to remove the first square bracket and replace it with a curly bracket, no you can't parse as a Map as is.
Edit 2
Since you have now re-edited your JSON to feature square brackets once again instead of curly brackets in the wrapping object, you can once again de-serialize as a Map[]. Until the next edit, I guess...
So, I've been doing GSON for a while, but I just now ran into the issue of using JSON Maps, which as I understand it are basically just Key Value pairs where he value is a JSON object.
To give you an idea where I'm coming from, here's my JSON
{
"configs":[
{
"com.hp.sdn.adm.alert.impl.AlertManager":{
"trim.alert.age":{
"def_val":"14",
"desc":"Days an alert remains in storage (1 - 31)",
"val":"14"
},
"trim.enabled":{
"def_val":"true",
"desc":"Allow trim operation (true/false)",
"val":"true"
},
"trim.frequency":{
"def_val":"24",
"desc":"Frequency in hours of trim operations (8 - 168)",
"val":"24"
}
}
},
{
"com.hp.sdn.adm.auditlog.impl.AuditLogManager":{
"trim.auditlog.age":{
"def_val":"365",
"desc":"Days an audit log remains in storage (31 - 1870)",
"val":"365"
},
"trim.enabled":{
"def_val":"true",
"desc":"Allow trim operation (true/false)",
"val":"true"
},
"trim.frequency":{
"def_val":"24",
"desc":"Frequency in hours of trim operations (8 - 168)",
"val":"24"
}
}
}
]
}
All of those com.hp.sdn... things are dynamic, as in I won't know the key names until Runtime. I figured I can just use a HashMap for this and GSON would figure it out, but I'm not sure what I would name the field...
Here are my classes that I have so far
package com.wicomb.sdn.models.configs;
import java.util.HashMap;
import com.wicomb.sdn.types.Model;
public class ConfigResponse extends Model {
private ConfigGroup[] configs;
}
package com.wicomb.sdn.models.configs;
import com.wicomb.sdn.types.Model;
public class ConfigGroup extends Model {
private HashMap<String,Config> ????;
}
TL;DR How should I write the Java Class to let Gson know how to handle a Json property that I don't know the name of.. And lots of them.
You can feed Gson with a HashMap (or if children order is important a LinkedHashMap) than you iterate over the entries or the keys normally as you would do to any other map.
In the code below I use the following json as input:
{
"test": 123,
"nested":{
"nested-1": 1,
"nested-2": 2
}
}
And the code looks like these:
public void testGson() {
String input = "{\"test\": 123, \"nested\": {\"nested-1\": 1, \"nested-2\": 2}}";
LinkedHashMap<String, Object> json = new Gson().fromJson(input, LinkedHashMap.class);
// iterating
for(Map.Entry<String, Object> entry : json.entrySet()){
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// testing values
System.out.println(json.get("test")); // should be 123
Map<String, Object> nested = (Map<String, Object>) json.get("nested");
System.out.println(nested.get("nested-1")); // should be 1
System.out.println(nested.get("nested-2")); // should be 2
}