I have this object structure which I'm trying to annotate with Jackson to marshal/unmarshal to a JSON file.
public class A {
List<B> bList;
}
public class B {
String attr;
Map<String, C> map;
}
public class C {
#JsonIgnore
String name;
String value;
}
A has a list of B's and B has a map of C's where the key of the map is the name attribute of C. I want the JSON to look like this if possible:
{
"bList" : [
{
"attr":"itsValue"
"KEY_IN_MAP":"VALUE_IN_C",
"KEY_2_IN_MAP":"VALUE_2_IN_C"
}
]
}
Where KEY_IN_MAP is the name of C as the key in B's map and VALUE_IN_C is the value of the value object in the map. I've tried annotating a put method for the map:
#JsonAnySetter
private void put(String name, C value) {
map.put(name, c);
}
But marshaling this gives me:
{
"bList" : [
{
"attr":"itsValue"
"KEY_IN_MAP": {
"value":"VALUE_IN_C",
},
"KEY_2_IN_MAP": {
"value":"VALUE_2_IN_C"
}
}
]
}
Is there any way to get the above mapping with Jackson or any other JSON serializing library? My goal is to get rid of the redundancy of writing "value" every time and compress the file as much as possible.
The map in B could be turned into a list of C but I still need the mapping to be
{"name" : "value"} for each object C
Here are few suggestions that I have:
Make your Map<String,C> to be a Map<String,String>.
You can also make your Map<String,C> to be List<C>.
Please have a look at the other thread which discusses the same.
Related
I have a pair of objects like
public class Obj1 {
public int id;
public String name;
public Obj2 obj2;
}
public class Obj2 {
public int id;
public String name;
public List<Obj1> obj1list;
}
I want to be able to convert this to Json via Jackson. I found the JsonManagedReference and JsonBackReference and annotated them but when you do that, the serialization only works in one way. It will only show when the class with the JsonManagedReference side is serialized.
If I serialize an "Obj1" I want to see the "Obj2" that is attached to it. And if I serialize the "Obj2" I want to see the list of "Obj1"s that is attached to it.
I also tried using JsonIdentityInfo annotation like so
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
and this seems to work except that it adds the "id" value of the parent object into the subobject (or every subobject in the list case) which is a bit odd. Though I guess I can live with it.
Is there a way to get this to behave as I expect?
You are looking at #JsonIgnoreProperties, it will give what is needed and avoid json recursion.
public class Obj1 {
public int id;
public String name;
#JsonIgnoreProperties("obj1list")
public Obj2 obj2;
}
public class Obj2 {
public int id;
public String name;
#JsonIgnoreProperties("obj2")
public List<Obj1> obj1list;
}
UPDATE
I always perfers #JsonIgnoreProperties over JsonBackReference and JsonManagedReference. As it not only does the trick, but also not escapes any data serialization (which is the case required here).
I also have a sample project on github deals with this situation. Look for entity class codes School.java & District.java. Its an mvn spring-boot executable code, if you wanna run a test.
From Javadoc, starting with Jackson 2.0, #JsonIgnoreProperties annotation can be applied both to classes and to properties. If used for both, actual set will be union of all ignorals: that is, you can only add properties to ignore, not remove or override. So you can not remove properties to ignore using per-property annotation.
HOW IT WORKS
When you define #JsonIgnoreProperties at propety level, while serialization/deserization it will work on the refered property object.
So here, when Obj1 is being parsed, you asked parser to ignore obj1list property of obj2. And similary, while parsing Obj2 it will ignore contained obj2 references in the Obj collection. So your parsing will look like below:
Obj1 : {
id : int,
name : string,
Obj2 : {
id : int,
name : string
obj1list : //ignored avoids recursion
}
}
Obj2 : {
id : int,
name : string,
obj1list : [{
id : int,
name : string,
obj2 : //ignored avoids recursion
},
{
id : int,
name : string
obj2 : //ignored avoids recursion
}
]
}
I'm working on a Spring-boot project where I receive different format of Json String. My goal is to convert these Json string into an Unified Java class.
I can receive many variations of this Json:
{ "id" : "someId", "type" : "temperature", "value" : 21.0 }
For example, one variation might look like :
{ "id" : "someId", "data" : { "type": "temp", "val" : 21.0 }, "location": "here" }
So these 2 Json must be mapped into the same Java class.
I already have 2 solutions in mind :
First solution
1) Create a Specific Java Class for each Json that I may receive
2) Create a function that takes this specific object and return the Unified Java Class
Second solution
1) Create a JsonNode with the Json String
2) For each key try to match it with a field of the Unified Java Class.
But we have to take into consideration every key possible of a node like "value" or "val".
What is the best approach to solve this problem ?
I'm looking for a solution that could be easy to maintain.
Edit : I'm already using Jackson, but my problem is to map this Json object into an universal Java Class independently of the Json
Edit 2 : The Unified Java Class is a class model that already exist and it's used to store information in our database. So to push information inside our database, I have to convert each json I receive into this unified format
I can see following solutions. E.g. you use Jackson for parse JSON you could declare you custom ObjectMapper:
ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
This mapper contains additional options to ignore unknow properties.
Do you Map<String, Object> as destination class. This is magic key and it works always. Contra: you do not have json validation and have to add many constant keys to read this.
Example:
public static <T> Map<String, T> readMap(String json) throws NGPException {
if (json == null) {
return null;
}
ObjectReader reader = JSON_MAPPER.readerFor(Map.class);
MappingIterator<Map<String, T>> it = reader.readValues(json);
if (it.hasNextValue()) {
Map<String, T> res = it.next();
return res.isEmpty() ? Collections.emptyMap() : res;
}
return Collections.emptyMap();
}
Client:
Map<String, Object> map = readMap("json string");
String id = (String)map.getOrDefault("id", null);
Second way is to build one general class that contain all posiible variables. Additionnaly you have to set option to Jackson ignore unknown fields. In this case, existed fields will be used by Jackson.
Example:
public static <T> T read(String json, Class<T> clazz) throws NGPException {
return mapper.readerFor(clazz).readValue(json);
}
class Response {
private String id;
private String type;
private Double value;
private String location;
private Data data;
public class Data {
private String type;
private String temp;
private Double value;
}
}
Client:
Response response = read("json string", Response.class);
I usually use GSon from Google. It is really usefull. Check gson.fromJson(yourJsonString) in your case.
You can easy use
Gson gson = new Gson();
Data data = gson.fromJson(jsonString, Data.class);
I am trying to create Dto class in Java for a json which is similar to this.
{
"abcList":[
{
"keyA":"valA",
"lstB":[{
"keyC":"valC"
},{
"keyC":"valC1"
}]
},{
"keyA":"valA",
"lstB":{
"keyC":"valC2"
}
}
]
}
Is the Json even valid? Inside the list abcList, the first object has a list "lstB" and in the second object, the field "lstB" is not a list.
If the json is valid, how would I create the corresponding Java Classes?
This does not work.
class TopObject{
List<ABC> abcList;
}
class ABC{
public String keyA;
public List<B> lstB;
}
class B{
public String keyC;
}
I mean I can parse it successfully with gson, iterating through every element of the json with a code like below,
if(lstB.isJsonArray()){
System.out.println("lstb is array");
List<B> arLstB = parseBArray(lstB);
abc.setLstB(arLstB);
}else{
System.out.println("lstb is not array");
B b = parseB(lstB);
abc.addB(b);
}
but the original json is very large and i will end up writing the iterating logic for days. I want a solution where I can annotate it properly and do the mapping in one shot like this.
gson.fromJson(jsonInString, TopObject.class);
I am trying to remove all the null values from my json.
{
"key" : null
}
I have used:
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
Here "key" is a list and so when I use the above serialization option, the json gets converted to:
{
"key" : []
}
I want the json to be:
{
}
I don't want to use
Include.NON_EMPTY
as I have other json in my project where I need to show the empty list and 0 valued keys. Is there any way to remove the null valued keys when it is a list, the same way it does for a string value?
I cannot use annotations as the class files are being generated from xml using jaxb. Class Structure:
public class C1 {
protected List<C2> key;
public List<C2> getKey() {
if (key == null) {
key = new ArrayList<C2>();
}
return this.key;
}
}
I have been stuck for a while now. Any help is highly appreciated. Thanks in advance.
To suppress serializing properties with null values,
you can configure the ObjectMapper directly using this.
mapper.setSerializationInclusion(Include.NON_NULL);
This is what my class looks like -
public class A {
private Map<String, Object> objects = null;
....
}
My json would be like -
{
"f1" : {
"name" : "some name",
"val" : 3
},
"f2" : {
"arg": {
some field/value pairs
}
}
}
What I want is to specify in the JSON itself the type to which it can be deserialized to. So the value for f1 would be converted to an object of class B and f2 would get converted to object of C.
My code will look like this -
Object o = objects.get("f1");
if (o instanceof B) {
...
} else if (o instanceof C) {
...
}
Is there a way to do this? I want the json to control the deserialization.
Yes, Jackson can use a type identifier if JSON document has it. This is usually done by using annotation #JsonTypeInfo.
There are multiple ways to add/use type identifier, both regarding how it is included in JSON document, and as to what kind of id is being used (type name or Java class name?).
The easiest way to see how things match is to actually start with a POJO, add #JsonTypeInfo annotation, and serialize it to see kind of JSON produced. And once you understood how inclusion works you can modify, if necessary, structure of JSON and/or Java class definition.