How to deserialize empty strings with jackson? - java

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

Related

Using the instantiated collection type for Jackson deserialization instead of initializing a new one?

If I have a class
class DTO {
final MySet<Types> values = MySetWrapper(EnumSet.of(Types.class));
public MySet getValues() {
return values;
}
}
where MySet extends Set. Jackson complains that
Cannot find a deserializer for non-concrete Collection type MySet
which I understand, but I already instantiated the collection. What I want is for jackson to just call add for each value after it created an instance, something like:
DTO o = new DTO();
MySet<Types> values = o.getValues();
for (Types type : jsonArray) {
values.add(type );
}
I don't want it to try to create a new collection itself.
That error message means that the DTO class is configured (by default or explicitly) to deserialize the values part of the JSON input into the DTO values field of DTO :
Cannot find a deserializer for non-concrete Collection type MySet
If you consider that Jackson should not perform the deserialization directly on this field, you could define a constructor to set values and also make sure that Jackson will not perform automatically the deserialization work : to achieve it, remove setter for that field (or add #JsonIgnore on it) and any jackson module that will use reflection to deserialize to fields.
It would give :
final MySet<Types> values = MySetWrapper(EnumSet.of(Types.class));
#JsonCreator
public MyFoo(Set<Types> values) {
this.values.addAll(values);
}
Note that I specified in the constructor Set and not MySet (should not be an issue as interface doesn't declare fields), otherwise you would get the same issue since you didn't define a deserializer for MySet.
But if you implement a deserializer for that you could of course do :
public MyFoo(MySet<Types> values) {
this.values.addAll(values);
}
Found an answer using #JsonProperty:
#JsonProperty
private void setValues(Set<Types> types) {
values.addAll(types);
}
Pretty short and simple thankfully.
Edit: seems like you don't even need the annotation.

Java8 Bean Validation for Enum type using Hibernate

Here's my situation,
I have a class with Enum type fields. I want to execute annotated validation for enum types, similar to annotations for strings, example: #Size, #NotNull etc.
Problem is, json deserializer fails on enum type before validation occurs.
public class myClass {
#JsonProperty
//#SomeCustomValidator -- ??
private EnumType enumValue;
}
public enum EnumType {
A,
B,
C
}
Few things:
I do not want to change the data type to String.
Tried steps in following threads, didn't fix my problem.
Tried this link, but get an error in deserialization before validation hits
Tried this link, but it works only when data Type is String
Validation works after the type of the argument is resolved. So I don't see a way how to use String validating annotations on enums. As workaround you can use #JsonCreator and do some validation before object creation.
public enum EnumType {
A,
B,
C;
#JsonCreator
public static EnumType from(String s) {
// add your logic here, for example
/*
if (Stream.of(values()).map(Enum::name).noneMatch(name -> name.equals(s))) {
throw new MyServiceException("Bad value " + s);
}
*/
return valueOf(s);
}
}

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

Serialize class with generic to JSON using Jackson

I have a structure of objects representing a Questionnaire and I need to serialize to JSON.
One class of the structure is a OpenQuestion and this class use generics with two parameters.
The problem starts when one of types used was Date, the date is serialized wrong, like a long.
Class code:
public class OpenQuestion <valueType,validationType> extends AbstractQuestion implements Serializable {
private valueType value;
private validationType minValue;
private validationType maxValue;
...
}
I saw how to serialize a date in a hash map if the hash map always uses a Date, but in this case I use the class with String, Integer or Date.
Any idea to solve it?
Thanks
You can add a JsonTypeInfo annotation for this. There's two ways of using this:
Get it to automatically add a type annotation to your object, so it knows what to deserialize it as.
Add a custom type resolver, to handle this for you.
The first will make your JSON ugly, but requires very little extra code and doesn't force you to make custom serializers. The latter is more difficult, but will result in cleaner JSON. Overall the problem is partly that one of your types isn't modelled in JSON (Date) so you'll probably need it to be serialised as an integer or String type in your JSON file.
The former option looks a bit like this:
#JsonTypeInfo( use = Id.CLASS, include = As.WRAPPER_PROPERTY )
private valiationType minValue;
This should encode say, a String value, as something like:
{ __type = "java.lang.String", value = "Hello, World" }
No promises on that being accurate as this is mostly from memory!
It depends. If you do know expected type, you just pass generic type reference:
OpenQuestion<Value,Validation> v = objectMapper.readValue(json,
new TypeReference<OpenQuestion<Value,Validation>>() { });
as that clues Jackson in as to expected type.
If you do not know it, then the other answer shows how to use #JsonTypeInfo.
As pointed out by #MiserableVariable, Jackson serializes (most) date fields as (numeric long) timestamps by default. You can override this behavior in a number of ways.
If using your own instance of ObjectMapper, override a property to write dates as ISO-8601:
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
If using your own instance of ObjectMapper, to have dates written in your own custom format:
objectMapper.setDateFormat(myDateFormat); // 1.8 and above
objectMapper.getSerializationConfig().setDateFormat(myDateFormat); // for earlier versions (deprecated for 1.8+)
To leave the default serialization behavior for most fields, but override it for certain fields on certain objects, use a custom serializer:
public class MyBean implements Serializable {
private Date postDate;
// ... constructors, etc
#JsonSerialize(using = MyCustomDateSerializer.class)
public Date getPostDate() {
return postDate;
}
}
public class MyCustomDateSerializer extends JsonSerializer<Date> {
#Override
public void serialize(final Date date, final JsonGeneraror generator,
final SerializerProvider provider) throws IOException,
JSONProcessingException {
generator.writeString(yourRepresentationHere);
}
}
All of this information is available in the Jackson Documentation, with the bulk of it in the section dealing with date handling.

Jackson Data-Binding with Heterogeneous Json Object

I'm calling a rest service that returns a json object. I'm trying to deserialize the responses to my Java Beans using Jackson and data-binding.
The example Json is something like this:
{
detail1: { property1:value1, property2:value2},
detail2: { property1:value1, property2:value2},
otherObject: {prop3:value1, prop4:[val1, val2, val3]}
}
Essentially, detail1 and detail2 are of the same structure, and thus can be represented by a single class type, whereas OtherObject is of another type.
Currently, I've set up my classes as follows (this is the structure I would prefer):
class ServiceResponse {
private Map<String, Detail> detailMap;
private OtherObject otherObject;
// getters and setters
}
class Detail {
private String property1;
private String property2;
// getters and setters
}
class OtherObject {
private String prop3;
private List<String> prop4;
// getters and setters
}
Then, just do:
String response = <call service and get json response>
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(response, ServiceResponse.class)
The problem is I'm getting lost reading through the documentation about how to configure the mappings and annotations correctly to get the structure that I want. I'd like detail1, detail2 to create Detail classes, and otherObject to create an OtherObject class.
However, I also want the detail classes to be stored in a map, so that they can be easily distinguished and retrieved, and also the fact that the service in in the future will return detail3, detail4, etc. (i.e., the Map in ServiceResponse would look like
"{detail1:Detail object, detail2:Detail object, ...}).
How should these classes be annotated? Or, perhaps there's a better way to structure my classes to fit this JSON model? Appreciate any help.
Simply use #JsonAnySetter on a 2-args method in ServiceResponse, like so:
#JsonAnySetter
public void anySet(String key, Detail value) {
detailMap.put(key, value);
}
Mind you that you can only have one "property" with #JsonAnySetter as it's a fallback for unknown properties. Note that the javadocs of JsonAnySetter is incorrect, as it states that it should be applied to 1-arg methods; you can always open a minor bug in Jackson ;)

Categories