Jackson deserialize generic with unknown property name - java

I have following JSON. And I am parsing it using Jackson Parser
{
"code": 0,
"response": {
"pagination": {
"page": 1,
"limit": 20,
"count": 5,
"pageCount": 1
},
"random": [
....
]
}
}
Now I have simple POJO classes created for various random object. I expect 3-4 different types of random object. So instead of creating different wrapper classes for different types of 'random' object I created a generic one
EDITED CLASS:
public class PaginatedResponse<E> {
private Pagination pagination;
private List<E> responseList;
public Pagination getPagination() {
return pagination;
}
public void setPagination(Pagination pagination) {
this.pagination = pagination;
}
public List<E> getResponseList() {
return responseList;
}
public void setResponseList(List<E> responseList) {
this.responseList = responseList;
}
}
Now For mapping it I used,
JsonNode tree = mapper.readTree(response);
TypeReference<PaginatedResponse<LocationParent>> ref = new TypeReference<PaginatedResponse<LocationParent>>() { };
PaginatedResponse<LocationParent> resp = mapper.convertValue(tree.get("response"), ref);
But i am not able to map responseList. I get the pagination object but the responseList is always null. Now how to dynamically provide property name for responseList.
Please help

What you need for variable value type is handling for polymorphic types. Generic types alone won't help, since deserialization side would not know what type to use.
You can enable polymorphic type handling with annotation #JsonTypeInfo; but a problem in this particular case is that you want a List of things of arbitrary type -- due to type-erasure, all Java Lists are really just List<Object>; there is no typing for elements.
If it was me, I would probably sub-class PaginatedResponse and just add #JsonTypeInfo in base class, like:
#JsonTypeInfo(...) // see javadocs for properties needed
public abstract class PaginatedResponse<T> {
public Pagination pagination;
// .. and so on
}
public class PaginatedFooResponse<Foo> { }
The reason to use sub-classing here is simply make it possible for deserializer to figure out element type, given type of response object. Response object will have type (PaginatedFooResposne), and from that type of elements is available.

Try this::
JSONObject objJSON = JSONObject.fromString("urString");
String code = objJSON.get("code");

Related

Dynamic object mapping using Jackson

I have a JSON request of the form:
{
"type": "Car"
"data": {
"object": {
"id": "1"
"color": "Red"
"plate": "J124D"
},
"owner": {
"name": "John"
}
}
}
Now, the type field dictates the content of the object field. In the example above, the type = Car means the object container will have id, color and plate fields. But, if the type = Plane, the Object field will have id, wingspan, manufacturer etc. Basically, the type field will dictate dynamically what Java object corresponds to "object". I am fairly new to Jackson, so I was looking for some pointers on how to achieve this type of dynamic mapping in code. So far I have this,
public class Request {
#JsonProperty
String type;
#JsonProperty
Data data; // Probably need a custom deserializer here?
}
The other difficulty is the usage of Request. Data would probably have to be cast into something like CarData or PlaneData so that callers could easily do request.getType() and then extract data from the request casted to the appropriate type of data (Car or Plane).
A simple way to do it is using a Map<String, Object> to map the data property:
public class Request {
private String type;
private Map<String, Object> data;
// Getters and setters
}
You may find this answer useful.
Create a POJO for object tag. Using JSONObject class you can map the value to java POJO accordingly.
Ref: https://www.mkyong.com/java/how-to-convert-java-object-to-from-json-jackson/

Jackson deserialisation mappings to Java Generics

What I am trying to do is the following: Given a JSON document, map it to a POJO using Jackson, but define the type of the Generic class member based on a field in the JSON document.
My JSON looks as follows
{
"name": "Name",
"parameters": [
{"name": "paramName","value": "Value1", "#type": "string"},
{"name": "size","value": 5,"#type": "double"}
]
}
The class that maps to this JSON doc is
public class Strategy {
public String name;
public List<Parameter<?>> parameters;
}
Then I have a Generic class for this as follows
public class Parameter<T> {
public String name;
public T value;
#Override
public String toString() {
return this.getClass().getName();
}
}
So the idea is to tell Jackson when you deserialize the JSON document into the Strategy class and get to the parameters field, use the following classes as the Generic data type for the value member, i.e. I want to select it to be String or Double or Integer but I want that to be my decision so that it's generic and can be extended to any data type I want.
I realise I can use the annotation JsonTypeInfo which I added as well like this
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="#type")
But using these classes as is actually works but Jackson decides itself what the type should be based on its value and my size parameter is set to an Integer. If I set its value to 5.0 then its set to a Double which works, but what if I want one of the parameters to be a custom object?
The only way I could get this to work (and am not 100% happy with the solution) is to make the Parameter class abstract and then create concrete classes for each type that I want, i.e. ParameterString, ParameterDouble, ParameterCustomClass and then use the #JsonSubTypes annotations to set the correct class to use based on the type field in the JSON document.
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="#type")
#JsonSubTypes({
#JsonSubTypes.Type(value=ParameterString.class, name="string"),
#JsonSubTypes.Type(value=ParameterDouble.class, name="double"),
#JsonSubTypes.Type(value=ParameterInstrument.class, name="instrument")
})
With the following class as an example
public class StrategyParameterString extends StrategyParameter<String> {
}
This isn't very extendable, I guess it will just need a new subtype annotation and concrete class added for every type that I need, but just doesn't feel as elegant as it could be.
Does anyone know of a better way of handling this ?
Thanks
Andrew
As I understand it, the types you want to represent in your Parameter list are reifiable, eg. String, Double, Instrument. You can take advantage of the fact that reifiable types have a runtime type token in the form of their class literal. This can be exploited to form the basis of a heterogenous type safe collection.
Instead of defining your Parameter class this way:
public class Parameter<T> {
public String name;
public T value;
:
:
}
}
You can define it as a concrete class that associates the object's value with its run time type token.
public class Parameter() {
private final Object m_value;
private final Class<?> m_cls;
private Parameter(Class<?> token, Object val) {
m_value = val;
m_cls = token;
}
public static <T> Parameter newInstance(Class<T> token, T value) {
return new Parameter(token, value);
}
:
:
public <T> T getValue(Class<T> token) {
if (token != m_cls) throw new ClassCastException("Type error");
return token.cast(m_value);
}
}
In this setting, type tokens and generic methods (rather than a generic type) are used to set and reestablish the type linkage for the desired value. The value you set can be any type and is guaranteed to be returned as the same type that you stored as long as the type tokens are consistent.
Note that constructors can not be generic. To address this, the constructor for Parameter has been made private and Parameter instances are formed by invoking the newInstance() static factory method (which can be generic).

Gson - deserialization to specific object type based on field value

I want to deserialize json objects to specific types of objects (using Gson library) based on type field value, eg.:
[
{
"type": "type1",
"id": "131481204101",
"url": "http://something.com",
"name": "BLAH BLAH",
"icon": "SOME_STRING",
"price": "FREE",
"backgroundUrl": "SOME_STRING"
},
{
....
}
]
So type field will have different (but known) values. Based on that value I need to deserialize that json object to appropriate model object, eg.: Type1Model, Type2Model etc.
I know I can easily do that before deserialization by converting it to JSONArray, iterate through it and resolve which type it should be deserialized to. But I think it's ugly approach and I'm looking for better way. Any suggestions?
You may implement a JsonDeserializer and use it while parsing your Json value to a Java instance. I'll try to show it with a code which is going to give you the idea:
1) Define your custom JsonDeserializer class which creates different instance of classes by incoming json value's id property:
class MyTypeModelDeserializer implements JsonDeserializer<MyBaseTypeModel> {
#Override
public MyBaseTypeModel deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
JsonElement jsonType = jsonObject.get("type");
String type = jsonType.getAsString();
MyBaseTypeModel typeModel = null;
if("type1".equals(type)) {
typeModel = new Type1Model();
} else if("type2".equals(type)) {
typeModel = new Type2Model();
}
// TODO : set properties of type model
return typeModel;
}
}
2) Define a base class for your different instance of java objects:
class MyBaseTypeModel {
private String type;
// TODO : add other shared fields here
}
3) Define your different instance of java objects' classes which extend your base class:
class Type1Model extends MyBaseTypeModel {
// TODO: add specific fields for this class
}
class Type2Model extends MyBaseTypeModel {
// TODO: add specific fields for this class
}
4) Use these classes while parsing your json value to a bean:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyBaseTypeModel.class, new MyTypeModelDeserializer());
Gson gson = gsonBuilder.create();
MyBaseTypeModel myTypeModel = gson.fromJson(myJsonString, MyBaseTypeModel.class);
I can not test it right now but I hope you get the idea. Also this link would be very helpful.
#stephane-k 's answer works, but it is a bit confusing and could be improved upon (see comments to his answer)
Copy https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java into your project. (It's ok; these classes are designed to be copy/pasted https://github.com/google/gson/issues/845#issuecomment-217231315)
Setup model inheritance:
// abstract is optional
abstract class BaseClass {
}
class Type1Model extends BaseClass {
}
class Type2Model extends BaseClass {
}
Setup GSON or update existing GSON:
RuntimeTypeAdapterFactory<BaseClass> typeAdapterFactory = RuntimeTypeAdapterFactory
.of(BaseClass.class, "type")
.registerSubtype(Type1Model.class, "type1")
.registerSubtype(Type2Model.class, "type2");
Gson gson = new GsonBuilder().registerTypeAdapterFactory(typeAdapterFactory)
.create();
Deserialize your JSON into base class:
String jsonString = ...
BaseClass baseInstance = gson.fromJson(jsonString, BaseClass.class);
baseInstance will be instanceof either Type1Model or Type2Model.
From here you can either code to an interface or check instanceof and cast.
use https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
then configure it with
public static final class JsonAdapterFactory extends
RuntimeTypeAdapterFactory<MediumSummaryInfo> {
public JsonAdapterFactory() {
super(MyBaseType.class, "type");
registerSubtype(MySubtype1.class, "type1");
registerSubtype(MySubtype2.class, "type2");
}
}
and add the annotation:
#JsonAdapter(MyBaseType.JsonAdapterFactory.class)
to MyBaseType
Much better.
If you have a lot of sub types and you do not want to or cannot maintain a list of them, you can also use an annotation based approach.
Here is the required code and also some usage examples:
https://gist.github.com/LostMekka/d90ade1fe051732d6b4ac60deea4f9c2
(it is Kotlin, but can easily be ported to Java)
For me, this approach is especially appealing, since I write a small library that does not know all possible sub types at compile time.

Group list of objects in a list attribute during deserialization using Jackson

I have a JSON Structure looking like this:
[
{
"id": 0,
"name": "Foo"
},
{
"id": 1,
"name": "Bar"
}
]
and a corresponding Java Object for Data binding:
public class Thing {
public int id;
public String name;
}
I know how I could deserialize the JSON list into a list of Thing.
Now here comes the tricky part: What I want to do is deserializing the JSON into a class looking like the following snippet by only doing changes to this class:
public class Things {
private List<Thing> things;
public void setThings(List<Thing> things) {
this.things = things;
}
public List<Thing> getThings() {
return this.things;
}
}
This is because the JSON deserialization is build in deep in our application by using an ObjectMapper like this:
private static <T> T parseJson(Object source, Class<T> t) {
TypeReference<T> ref = new TypeReference<T>() {
};
TypeFactory tf = TypeFactory.defaultInstance();
//[...]
obj = mapper.readValue((String) source, tf.constructType(ref));
//[...]
return obj;
}
Are there any annotations with which I can achieve what I want or do I have to make changes to the mapper-code?
Much thanks in advance, McFarlane
The whole point of TypeReference, as described in this link, is to use the generic type argument to retrieve type information.
Internally it does the following
Type superClass = getClass().getGenericSuperclass();
...
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
where getActualTypeArguments()[0] will give you the actual type argument. In this case, that will be the type variable T, regardless of what you pass in for the Class<T> t parameter of your method.
The proper usage is
TypeReference<List<Thing>> ref = new TypeReference<List<Thing>>() {};
...
List<Thing> thingsList = ...;
Things things = new Things();
things.setThings(thingsList);
In other words, no, you'll need to change your mapper code to achieve what you want.
As far as I know, you won't be able to map a root JSON array as a property of a class. The alternatives is the TypeReference example above or some other ones found here.

Jackson JSON-RPC deserilisation to generic Object

I am trying to deserialise a JSON-RPC object with Jackson. The format of JSON-RPC is :
{ "result": "something", "error": null, "id": 1}
In my case the result property is an generic Object.
I have a class for deserilising the response:
public class JsonRpcResponse {
private Object result;
private JsonRpcError error;
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public JsonRpcError getError() {
return error;
}
public void setError(JsonRpcError error) {
this.error = error;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
}
I can get the response object with:
JsonRpcResponse jsonResp = mapper.readValue(response, JsonRpcResponse.class);
I want to have a generic method that deserializes this result object by passing to the method the type of the object (or the class if you want) that is going to be deserialized to. This way I can pass any type of object depending of the response I expect.
For example, I have this class with two properties:
public class JsonEventProperties {
private String conditon;
private String usage;
public JsonEventProperties(String condition, String usage) {
this.conditon = condition;
this.usage = usage;
}
public JsonEventProperties() {
throw new UnsupportedOperationException("Not yet implemented");
}
public String getConditon() {
return conditon;
}
public void setConditon(String conditon) {
this.conditon = conditon;
}
public String getUsage() {
return usage;
}
public void setUsage(String usage) {
this.usage = usage;
}
}
The result object inside the response for the above case will be:
"result": {"condition":"test","usage":"optional"}
I tried:
mapper.readValue(result,objectClass)
where result is a JsonNode intance of the result (Which for some reason is a LinkedHashMap) and objectClass the class I want it to deserialize to. But this is not working.
I busted my head all day with different ways of doing this but I probably do not understand who Jackson works.
Can anyone help me with this?
Thank you in advance.
I understand the original question to be asking about polymorphic deserialization of the "result" object.
Jackson now has a built-in mechanism available for this, using the #JsonTypeInfo and #JsonSubTypes annotations. (ObjectMapper has methods available as alternatives to using the annotations.) Further information is available in the official docs at http://wiki.fasterxml.com/JacksonPolymorphicDeserialization. Also, I posted a few use examples of this at http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html.
However, if you're stuck deserializing JSON that, in the target object, does not have an element that identifies the type by some name, then you're stuck with custom deserialization, where you'll have to determine based on some object content what the type should be. One of the last examples in the same blog posted I linked above demonstrates this approach, using the existence of particular JSON elements in the target object to determine the type.
Check out jsonrpc4j on github:
https://github.com/briandilley/jsonrpc4j
I had the same issue, this was my solution.
I added one more field to the object, so when building the object, i am setting the field value with class name, when deserializing it i am using
mapper.convertvalue(object, Class.forName(field value)
In your case
private Object result;
In the result object add one more field "className", while serializing the class set the value "className" with the name of the class you are treating as result object.
while deserializing the object
JsonRpcResponse jsonResp = mapper.readValue(response, JsonRpcResponse.class);
in jsonResp you will have Object result, String className, here the object is of type linkedhashmap
Now to convert to your object
objectmapper.convertValue(result, Class.forName(className))
The above code will get you the generic object which you want .
Hope this helps

Categories