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/
Related
I have problems with automatically generated model schema by Swagger. I have a collection field of type List<String> which I want to serialize as plain string and I have separate serializer for that. Jackson is doing its job well, but not schema generator. It resolves type of the field as object despite of explictly set attribute dataType:
public class FilialDetails {
#ApiModelProperty(dataType = "string", example = "10000101010")
#JsonInclude(JsonInclude.Include.NON_NULL)
// custom serializer treats collection as subset of known, pre-populated list
// and serializes it as series of 0 and 1
#JsonSerialize(using = ServicesSerializer.class)
private List<String> services;
}
Generated properties definition (excerpt):
"properties": {
"services": {
"type": "object",
"example": 10000101010
}
}
I need string in schema definition, not object, how it's possible? I'm using io.springfox:springfox-boot-starter:3.0.0 in my project.
I had to specify fully qualified type name for string
#ApiModelProperty(dataType = "java.lang.String", example = "10000101010")
private List<String> services;
I think perhaps I am misunderstanding how some of the Jackson libraries work. I am trying to deserialize a JSON into an object without including type information on the object I am deserializing into.
For example, I have the Correspondence parent-child relationship for contact information shown below:
#JsonRootName("correspondence")
public class Correspondence {
private Contact contact;
// getter/setter not shown for brevity
}
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "concreteType")
#JsonSubTypes({#JsonSubTypes.Type(value = Person.class, name = "person")})
public interface Contact {
// just a wrapper interface
}
#JsonRootName("person")
public class Person implements Contact {
private String firstName;
private String lastName;
// getter/setter not shown for brevity
}
Sending the following JSON allows me to deserialize this object with JsonTypeInfo and JsonSubTypes when I have the inclusion property PROPERTY. The JSON would be:
{
"correspondence": {
"contact": {
"type": "person",
"firstName": "Johnny",
"lastName": "Bravo"
}
}
}
The above format works, but the format I want to send in my JSON in is this:
{
"correspondence": {
"person": {
"firstName": "Johnny",
"lastName": "Bravo"
}
}
}
As you see, the child class of Contact, Person, is now the rootname of the inner object. I thought this could be accomplished using the inclusion property WRAPPER_OBJECT, but for the life of me I cannot seem to get this to work. I have read multiple resources which are all well documented which is why I think I might be misunderstanding how to use the Jackson library to accomplish my goal.
References:
Jackson Polymorphic Deserialization
Polymorphic JSON serialization tutorial
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).
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.
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");