Java: org.codehaus.jackson: How deserialize from string to Enum? - jackson

I has POJO
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyPojo implements Serializable {
#JsonProperty("id")
private int id;
#JsonProperty("type")
private MyTypeEnum type;
}
I deserialize from string (json) using annotation: org.codehaus.jackson.annotate.JsonProperty.
And it's work fine.
But what about Enum? How I can deserialize from string to enum (e.g. property "type") in uppercase?
Here my enum:
import org.codehaus.jackson.annotate.JsonProperty;
public enum Type {
#JsonProperty("text")
TEXT("text"),
#JsonProperty("external")
EXTERNAL("external"),
#JsonProperty("event")
EVENT("event"),
#JsonProperty("post")
POST("post"),
UNKNOWN("Unknown");
private String name;
private Type(String name) {
this.name = name;
}
private static Type[] genderList = Type.values();
public String getName() {
return name;
}
}
`
Error:
getLastPromotion: errorMessage: Can not construct instance of com.myproject.Type from String value 'external': value not one of declared Enum instance names
org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.bnmt.main.classEnum.PromotionObjectType from String value 'external': value not one of declared Enum instance names
at org.codehaus.jackson.map.deser.StdDeserializationContext.weirdStringException(StdDeserializationContext.java:243)
at org.codehaus.jackson.map.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:80)
at org.codehaus.jackson.map.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:23)
at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:299)
at org.codehaus.jackson.map.deser.SettableBeanProperty$FieldProperty.deserializeAndSet(SettableBeanProperty.java:579)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:697)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
at org.codehaus.jackson.map.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:104)
at org.codehaus.jackson.map.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:18)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1863)

Related

How to receive a JSON File with multiple nodes through a POST Request with JAX-RS and Jackson (Quarkus)

When im tryin got do a Post request through my JAX-RS API it always sends a null value. I dont know if the Jackson annotations are incorrect or if i need to use an ObjectMapper.
These are my classes:
public class ClassA{
private String name;
private ClassB classB;
public ClassA(){}
public ClassA(String name, ClassB classB){
this.name = name;
this.classB = classB;
}
#JsonGetter
public String getName(){ return name; }
#JsonGetter
public ClassB getClassB(){ return classB; }
and this is the classB
public class ClassB{
#JsonProperty("type")
private String type;
#JsonProperty("number")
private int number;
public ClassB(){}
#JsonPropertyOrder({"type, number"})
public ClassB(String type, int number){
this.type= type;
this.number= number;
}
#JsonGetter
public String getType(){ return type; }
#JsonGetter
public int getNumber(){ return number; }
My JSON file:
{
"type": "typeExample;
"classB": {
"type": "classBTypeExample";
"int": 10;
}
}
I want Jackson to read the file and then add an Object type ClassA to a list (the problem is that is not even reading it)
This is the API code:
#Path("/path")
public class Requests {
private Set<ClassA> classesA = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));
#GET
public Set<ClassA> list() {
return classesA;
}
#POST
public Set<ClassA> add(ClassA classA){
classesA.add(classA);
return classesA;
}
}
I already added the quarkus.jackson.fail-on-unknown-properties=true to the application.properies file
It was a simple thing that i didnt notice. I forgot to set the values on the constructer. So i just had to add the #ConstructorProperties({}) to the classes constructors and it worked out.

Cannot deserialize instance of MyEnum out of START_OBJECT token

Stuck with the problem of MyEnum enum deserialization from JSON to POJO and cannot figure out what I do wrong. So basically I try to retrieve some data calling particular microservice endpoint that returns the following json:
{
"id": "9cabf3e9-965d-4407-b62b-c57dd6006419",
"myEnums": [
{
"context": "SOME_FOO_CONTEXT_1",
"feature": "SOME_BAR_FEATURE_1",
"name": "SOME_FOO_BAR_1"
},
{
"context": "SOME_FOO_CONTEXT_2",
"feature": "SOME_BAR_FEATURE_2",
"name": "SOME_FOO_BAR_2"
}
],
"name": "Some name",
"updatedBy": null,
"updated": "2019-05-16T00:11:19.279Z"
}
This is the method that calls another microservice endpoint, deserialize response body to POJO and return result as Set:
private Mono<Set<MyEnum>> fetchMyEnums(UUID someId) {
return webClient.get().uri("/v1/something/{id}", someId)
.retrieve()
.bodyToMono(MyClass.class)
.flatMapIterable(MyClass::getMyEnums)
.collect(toSet());
}
The class that used for JSON deserialization:
#lombok.Value
static class MyClass {
List<MyEnum> myEnums;
}
Enum that I actually cannot deserialize:
#Getter
#RequiredArgsConstructor
#AllArgsConstructor
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MyEnum {
SOME_FOO_BAR_1(SOME_FOO_CONTEXT_1, SOME_BAR_FEATURE_1),
SOME_FOO_BAR_2(SOME_FOO_CONTEXT_2, SOME_BAR_FEATURE_2);
private final FooEnum context;
private final BarEnum feature;
private String name;
#JsonProperty
public String getName() {
return super.name();
}
}
During deserialization I receive the following exception:
org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize instance of `com.learn.common.security.model.MyEnum` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `com.learn.common.security.model.MyEnum` out of START_OBJECT token
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.learn.common.security.service.MyEnumService$MyClass["myEnums"]->java.util.ArrayList[0])
Where I did mistake?
So spending few more hours to clarify what's the problem with deserialization i figure out that there is no automatic deserialization for Enum whose Shape.Object.
But I found workaround how to deserialize MyEnum object from json(you need define static method marked it as JsonCreator and define what input parameter you expect to catch from object defining JsonProperty with fieldName):
#Getter
#RequiredArgsConstructor
#AllArgsConstructor
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MyEnum {
SOME_FOO_BAR_1(SOME_FOO_CONTEXT_1, SOME_BAR_FEATURE_1),
SOME_FOO_BAR_2(SOME_FOO_CONTEXT_2, SOME_BAR_FEATURE_2);
private final FooEnum context;
private final BarEnum feature;
private String name;
#JsonProperty
public String getName() {
return super.name();
}
#JsonCreator
public static MyEnum fromJson(#JsonProperty("name") String name) {
return valueOf(name);
}
}
For sake of completeness: You can add the #JsonCreator annotation either to a constructor or to a factory method.
Constructor:
#JsonCreator
public MyEnum(#JsonProperty("name") String name) {
this.name = name;
}
Factory method:
#JsonCreator
public static MyEnum fromJson(#JsonProperty("name") String name) {
return valueOf(name);
}
Multiple parameters:
If your enum type contains multiple properties, add them to the signature with #JsonProperty annotation.
#JsonCreator
public MyEnum(#JsonProperty("id") String id, #JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
When using a factory method, creation from JSON might fail, if you have defined multiple properties. You may get this error message:
Unsuitable method [...] decorated with #JsonCreator (for Enum type [...])
Some versions of Jackson cannot handle this case. Use constructor method as a workaround, if your enum type contains more than one property.

Jackson deserialization issue with access modifier

Jackson throwing an exception in deserialization with protected access modifier.
I am trying to deserialize one object then accessing protected properties of parent class is throwing exception.
Class com.fasterxml.jackson.databind.deser.impl.FieldProperty can not
access a member "protected"
Make sure that Jackson can see protected fields. E.g.
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC;
#JsonAutoDetect(fieldVisibility = PROTECTED_AND_PUBLIC)
class Person {
protected String name;
}
Or define getters and setters as needed. E.g. :
class Person {
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Morphia Interface for List of enum does not work (unmarshalling)

I have the following interface
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "className")
public interface InfoChartInformation {
public String name();
}
And the following implementation (enum):
public class InfoChartSummary {
public static enum Immobilien implements InfoChartInformation {
CITY, CONSTRUCTION_DATE;
}
public static enum Cars implements InfoChartInformation {
POWER, MILEAGE;
}
}
Then I use all of It in the following entity:
#Entity(noClassnameStored = true)
#Converters(InfoChartInformationMorphiaConverter.class)
public class TestEntity{
#Id
public ObjectId id;
#Embedded
public List<InfoChartInformation> order;
}
Jackson, in order to detect the type on the unmarshalling time, will add to every enum on the list the className.
I thought morphia would do the same, but there's no field className in the List of enum and the unmarshalling cannot be done correctly: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ClassCastException: java.lang.String cannot be cast to com.mongodb
.DBObject
I guess the correct behavior should be to save all the enum route (package+name), not only the enum name. At least in that way the unmarshalling could be performed. There's a way morphia supports that by default or I need to create my own converter (similar to this) ?
I tried creating a Custom Converter:
public class InfoChartInformationMorphiaConverter extends TypeConverter{
public InfoChartInformationMorphiaConverter() {
super(InfoChartInformation.class);
}
#Override
public Object decode(Class targetClass, Object fromDBObject, MappedField optionalExtraInfo) {
if (fromDBObject == null) {
return null;
}
String clazz = fromDBObject.toString().substring(0, fromDBObject.toString().lastIndexOf("."));
String value = fromDBObject.toString().substring(fromDBObject.toString().lastIndexOf(".") + 1);
try {
return Enum.valueOf((Class)Class.forName(clazz), value);
} catch (ClassNotFoundException e) {
return null;
}
}
#Override
public Object encode(final Object value, final MappedField optionalExtraInfo) {
return value.getClass().getName() + "." + ((InfoChartInformation) value).name();
}
}
Then, I added the converter information to morphia morphia.getMapper().getConverters().addConverter(new InfoChartInformationMorphiaConverter());.
However, when serializing (or marshalling) the object to save it into the database, the custom converter is ignored and the Enum is saved using the default Morphia converter (only the enum name).
If I use in the TestEntity class only an attribute InfoChartInformation; instead of the List<>InfoChartInformation>, my customer converter will work. However I need support for List
Use:
public class InfoChartInformationMorphiaConverter extends TypeConverter implements SimpleValueConverter
It is a marker interface required to make your Convertor work.

Dropwizard hiding field in json

What's the best way to hide a field on a model in Dropwizard? (If I want to be able to deserialize the field via Jackson, but hide it when serialization)
For example, if I have the following model:
class User {
private String secret;
private String username;
}
I want to be able to create a User with a secret via calling new ObjectMapper().readValue(), but I want to hide the secret field it when it's serialized into JSON.
Add #JsonIgnore annotation before the property
Or you can add #JsonIgnoreProperties annotation and specify the fields which are to be excluded
public class Foo{
#JsonIgnore
private String bar;
...
}
or
#JsonIgnoreProperties(value = { "bar" })
public class Foo {
private String bar;
...
}
or if you want to ignore this field only on serialization, and not on deserialization then
public class Foo{
private String bar;
...
#JsonIgnore
public String getBar(){
return bar;
}
public void setBar(String bar){
this.bar = bar;
}
}
If you want to hide it only during seriazliation add #JsonIgnore annotation to property getter.
class User {
#JsonProperty
private String secret;
#JsonProperty
private String username;
#JsonIgnore
public String getSecret(){
return secret;
}
public void setSecret(String secret){
this.secret = secret;
}
...
}