I am using Jackson as a mapper for a REST Service. A sample json response is
{
"id": 1,
"name": "One fine name",
"child": 1 //this is a primary key referencing a composite object
}
Now, using Jackson to map this notation into POJOs, the class would probably look like the following
class Parent{
protected Integer id;
protected String name;
protected Child child;
//....
//appropriate Getters and Setters
}
But when Jackson is about to deserialize the object, it complains about converting an integer into the Child object. Any idea how to make Jackson understand that the value for the child field is a reference?
Related
I have come across a problem of parsing json data . I am building project using spring boot based on REST api . When i have to parse data corresponding to domain then it is very easy , i use RequestBody in controller method with domain name but in current scenerio i have a list of domain in json form :
{
"data":[
{
"type":"abc",
"subtypes":[
{
"leftValue":"BEACH",
"rightValue":"MOUNTAIN",
"preferencePoint":60
},
{
"leftValue":"ADVENTURE",
"rightValue":"LEISURE",
"preferencePoint":60
}
]
},
{
"type":"mno",
"subtypes":[
{
"leftValue":"LUXURY",
"rightValue":"FUNCTIONAL",
"preferencePoint":60
},
{
"leftValue":"SENSIBLE",
"rightValue":"AGGRESIVE",
"preferencePoint":0
}
]
}
]
}
I am sending data in list where type is the property of class Type
and class Type has list of Subtypes class and subtype class contains leftValue and rightValue as enums
I am using spring boot which uses jackson liberary by default and i want to parse this data into corresponding Type class using Jackson. Can any one provide me solution.
It wasn't clear to me if you have static or dynamic payload.
Static payload
For static one, I would personally try to simplify your payload structure. But your structure would look like this. (I skipped getters and setters. You can generate them via Lombok library).
public class Subtype{
private String leftValue;
private String rightValue;
private int preferencePoint;
}
public class Type{
private String type;
private List<Subtype> subtypes;
}
public class Data{
private List<Type> data;
}
Then in your controller you inject Data type as #RequestBody.
Dynamic payload
For dynamic payload, there is option to inject LinkedHashMap<String, Object> as #RequestBody. Where value in that map is of type Object, which can be casted into another LinkedHashMap<String, Object> and therefore this approach support also nested objects. This can support infinite nesting this way. The only downside is that you need to cast Objects into correct types based on key from the map.
BTW, with pure Spring or Spring Boot I was always able to avoid explicit call against Jackson API, therefore I don't recommend to go down that path.
I have a Jackson polymorphic question.
I want to deserialize JSON data into polymorphic types. Reading Jackson documentation, I can deserialize JSON data to polymorphic types. However, I have a special case. I have a class structure as follows:
class Supreme {
private String type;
}
class Foo extends Supreme {
public String label;
}
class Bar extends Supreme {
}
Note: Class Bar does not have any other member variable other than the inherited "type" field.
I have transformed that structure to:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.PROPERTY,property ="type")
#JsonSubTypes({#Type(value = Foo.class, name = "Foo"),#Type(value = Bar.class, name = "Bar") })
class Supreme {
}
class Foo extends Supreme {
public String label;
}
class Bar extends Supreme {
}
String data=
"[{
"type": "Foo",
"label": "abc"
},
{
"type": "Bar"
}]"
If I pass in the above json data like:
new ObjectMapper().readValue(data, new TypeReference<List<Supreme>>());
I get something like "Unable to deserialize class Bar out of the END_TOKEN". And I believe that is because the JsonTypeInfo and JsonSubTypes annotations have parsed "type" property and figured out that the 2nd entity in the array should be mapped to Bar class; however it tries to find "something" after the type property in that 2 entity. In other words, Jackson thinks it is an empty JSON object.
(Note: the above data without the 2nd entry in the array works fine. In other words, we can deserialize to a list containing Foo object since it at least has a property other than "type")
Any idea how to get around this?
By mistake, I was using Jackson 1.5
I bumped to Jackson 1.9 and the exception went away. So there was a bug in Jackson 1.5
I am using resttemplate with jackson to marshall/unmarshall java/json objects.
What would be the best strategy to serialize/deserialize
a Map that may contain key value pairs such that keys are strings and values could
be various types for example an ArrayList of custom objects
I did some research on this site and found the use of #JsonAnyGetter #JsonAnySetter
could be used in this situation, but wasnt sure of how to deserialize in the context
of resttemplate getforobject method. Would one have to write a custom httpmessageconverter
to accomplish the deserialization?
Thanks in advance.
We'll assume you have a response like this:
{ key1: "something", key2: 3}
You'll want to have a DTO that has those fields:
class CustomResponse {
private String key1;
private long key2;
}
Make sure you add getters and setters for the above.
Now make your request:
restTemplate.postForObject(url, requestObject, CustomResponse.class);
The request object can be either a DTO like the above or just use Arrays and Maps to construct the requestObject.
You should add this annotation to your response DTOs. This ensures that if there are fields in the response that don't map in your DTO, they will be ignored.
#JsonIgnoreProperties(ignoreUnknown = true)
So I have a class that I was planning on using for simple JSON serialization.
public class Thing {
private int field1;
private String some_other_field;
private List<SubType> subs;
private list<AnotherType> another;
public String toJson() {
Gson g = new Gson();
g.toJson(this);
}
}
So the documentation shows that if you want to serialize generic types, you need to specify a TypeToken:
Type listtype = new TypeToken<List<SubType>>() {}.getType();
gson.toJson(subs, listtype);
But then, how does this work if I have a whole class I want to serialize for? Where do I get to specify the serialization type for those two List<> types so that I can just pass the whole object in and get a serialized output? Is that even possible?
From the doc it seems that if you serialize a complete object with toJson(...), it deals with the generics attributes properly.
toJson(Object)
"Note that this method works fine if the any of the object fields are of generic type, just the object itself should not be of a generic type"
What output did you get with your object ?
I have a JSON object which I don't have control of and want to map it to a Java object which is pre-created.
There is one attribute in the JSON object which can be a URL or it could be a JSONArray.
Class SomeClass {
private URL items;
public URL getURL() {
return items;
}
public void setURL(URL url) {
this.items = url;
}
}
Below is the JSON:
Case A:
{
...
items: http://someurl.abc.com/linktoitems,
...
}
OR
Case B
{
...
items: [
{ "id": id1, "name": name1 },
{ "id": id2, "name": name2 }
]
...
}
If i create the POJO to map for Case A, Case B fails and vice versa. In short, is there a way to map the JSON attribute to the POJO field with different data types? In that case I will create two separate fields in the POJO named,
private URL itemLink;
private Item[] itemList;
It depends on exact details, but if what you are asking is if it is possible to map either JSON String or JSON array into a Java property, yes this can be done.
Obvious way would be to define a custom deserializer which handles both kinds of JSON input.
But it is also possible to define Java type in such a way that it can be constructed both by setting properties (which works from JSON Object) and have a single-String-arg constructor or static single-String-arg factory method marked with #JsonCreator.
Yet another possibility is to use an intermediate type that can deserialized from any JSON: both java.lang.Object and JsonNode ("JSON tree") instances can be created from any JSON. From this value you would need to do manual conversion; most likely in setter, like so:
public void setItems(JsonNode treeRoot) { .... }
What will not work, however, is defining two properties with the same name.
One thing I don't quite follow is how you would convert from List to URL though. So maybe you actually do need two separate internal fields; and setter would just assign to one of those (and getter would return value of just one).