Jackson deserialisation mappings to Java Generics - java

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).

Related

How to deserialize empty strings with jackson?

I want to deserialize a json with Jackson and I want to map empty strings to STANDARD enum type.
When I try to use JsonProperty with empty string, It ignores empty value and throws exception;
value not one of declared Enum instance names:......,STANDARD,...
Is there any way to handle this?
public enum Type{
#JsonProperty("")
STANDARD,
#JsonProperty("complex")
COMPLEX,
....
}
My json;
....
"type": "",
....
#JsonValue will do the trick:
public enum Type {
STANDARD(""),
COMPLEX("complex");
private String value;
StatusType(String value) {
this.value = value;
}
#JsonValue
public String getValue() {
return value;
}
}
Quoting the relevant parts from the #JsonValue documentation:
Marker annotation that indicates that the value of annotated accessor (either field or "getter" method [a method with non-void return type, no args]) is to be used as the single value to serialize for the instance, instead of the usual method of collecting properties of value. [...]
At most one accessor of a Class can be annotated with this annotation; if more than one is found, an exception may be thrown. [...]
NOTE: when use for Java enums, one additional feature is that value returned by annotated method is also considered to be the value to deserialize from, not just JSON String to serialize as. This is possible since set of Enum values is constant and it is possible to define mapping, but can not be done in general for POJO types; as such, this is not used for POJO deserialization.
You could try using #JsonInclude annotation to ignore empty values and use JsonInclude.Include.NON_NULL or JsonInclude.Include.NON_EMPTY as desired
for example:-
#JsonInclude(JsonInclude.Include.NON_NULL)
STANDARD

Accessing Android SharedPreferences using generic method

I'm trying to define a generic wrapper method for Android's SharedPreferences, for getting\setting in a more convenient way a list of predefined parameters.
Initially I defined an enum of all supported types (String, Int & Bool in my case), each one of them is associated with its corresponding class:
public enum ParamType {
String(String.class),
Int(Intent.class),
Bool(Boolean.class);
private final Class paramClass;
ParamType(Class paramClass) {
this.paramClass = paramClass;
}
}
Then I defined the enum of the known parameters I use, with the associated type of each one:
public enum Param {
FirstParam(ParamType.Bool),
SecondParam(ParamType.String),
ThirdParam(ParamType.Int);
Param(ParamType paramType) {
this.paramType = paramType;
}
private final ParamType paramType;
}
Then I want to have a generic method, that by the given parameter, will know to return the appropriate type:
public static <T> T getParamValue(Param param) {
}
However, I'm not sure how to implement this method and if it's actually possible, since the generic T param is not passed in the signature above.
It is a OVERENGINEERING, don't do that. You can create interface for saving your customobject and loading it and implement it for SharedPreferences and database. But it is not neccessary to create generic for every integral type, if there is three - five types.

gson exclusion strategy apply only to fields of target object

I want to prevent Gson from serializing fields of a specific type. For this, I have created an exclusion strategy. The exclusion strategy does successfully recognize when the class in question is being processed and it does successfully exclude it. Unfortunately, it prevents me from serializing objects of that class even when they are the root. By that I mean they are the argument passed to the gson.toJson() method.
To be more clear, I have a class of type Person with class fields that themselves involve the Person type. I do not want to serialize class fields of the type Person.
public class Person{
private Person child;
private String name;
}
So, in the above example, I want a json object containing the name field but not the child field. I want the solution to be sensitive the the type of the field, not the field name.
An ExclusionStrategy defines two methods, one to exclude types and one to exclude fields. Just use the field method to skip any fields of type Person.
class PersonExcluder implements ExclusionStrategy {
#Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaredType().equals(Person.class);
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
then use register it
Gson gson = new GsonBuilder().setExclusionStrategies(new PersonExcluder()).create();

How do I pass varied parameters when constructing objects via reflection?

I have a situation where I use reflection to create objects at run-time based on their fully qualified class names. All the objects extend the same abstract class, so their general type is known at compile time but the concrete implementation is determined by the class name supplied at run-time.
The abstract class provides one static method named create, that takes the class name and other parameters with which to construct the concrete object. Each Response has an actual type A and a storage type S. The actual type is the "proper" representation of the data in Java and the storage type is the thing that gets stored in the database. E.g. A might be a Date object and S might be the same Date objected converted to a Long for storage in SQLite.
Here's a simplified representation:
public abstract class Response<A, S> {
public Response(String clazz, Object value, boolean actual) {
this.clazz = clazz;
if (actual) {
actualValue = (A) value;
} else {
actualValue = toActualValue((S) value);
}
}
public static Response create(String clazz, Object value) {
//create response by reflection
return response;
}
}
This was working okay until I now when I have to deal with the fact that in addition to the two parameters that each Response implementation takes, some Response implementations now need to take additional parameters.
These additional parameters cannot be passed via setter methods as they are typically used in the package private method toActualValue() that is called from within the abstract Response constructor.
I've considered using the Builder pattern to handler the optional parameters, but then I would need a way to determine which Response implementations take which parameters - and I can't think of a clean way to provide that information. Maybe I am thinking about this entirely wrong. Any helpful insights or suggestions will be appreciated.
Have you considered using the arbitrary number of arguments?
public TestClass(String clazz, Object value, boolean actual, Object... parms) {
this.clazz = clazz;
if (actual) {
actualValue = (A) value;
} else {
//actualValue = toActualValue((S) value, parms);
}
}
https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html

Jackson deserialize generic with unknown property name

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");

Categories