We use Jackson 1.9.1 to serialize and deserialize JSON request response strings to/from Java objects. Primitive Java types, collection types, and custom objects are (de)serialized without issues. However, I have a problem trying to deserialize JSON string into java enum. JSON string is serialized like so:
"wt":{"wt":100.5,"unit":{"LBS":3}}
Java type for wt is like so:
public class Weight {
protected double weight;
protected Unit unit;
}
I referred to this, this, and this on SO and came up with enum for weight units like so:
public enum Unit {
KG("kg"),
GM("gm"),
LBS("lbs"),
OZ("oz");
private String value;
private WeightMeasurementUnit(String value) { this.value = value; }
@JsonValue
public String getValue() { return this.value; }
@JsonCreator
public static Unit create(String val) {
Unit[] units = Unit.values();
for (Unit unit : units) {
if (unit.getValue().equals(val)) {
return unit;
}
}
return LBS;
}
}
The problem is, when ever I try to deserialize above mentioned JSON I get this error saying: "Unrecognized field "LBS" (Class a.b.c.d.Weight), not marked as ignorable" Exception stacktrace is like so:
Caused by: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "LBS" (Class a.b.c.d.Weight), not marked as ignorable
at [Source: java.io.ByteArrayInputStream@20172017; line: 1, column: 464] (through reference chain: a.b.c.d.MyRequest["blah"]->a.b.c.d.AnotherType["wt"]->a.b.c.d.Weight["LBS"])
at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)
at org.codehaus.jackson.map.deser.std.StdDeserializer.reportUnknownProperty(StdDeserializer.java:673)
at org.codehaus.jackson.map.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:659)
at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:1365)
...
My questions are: Is the serialized JSON string for enum seem correct ? What else should I include (or annotate) for the enum to be properly deserialized ?
All the methods annotated by @JsonCreator are invoked by Jackson for getting an instance of the enclosing class. In order to deserialize the JSON String to Enum by using the @JsonCreator, we will define the forValues() factory method with the @JsonCreator annotation. We have the following JSON string to deserialize: {
To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant's name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.
JSON has no enum type. The two ways of modeling an enum would be: An array, as you have currently. The array values are the elements, and the element identifiers would be represented by the array indexes of the values.
Because enums are automatically Serializable (see Javadoc API documentation for Enum), there is no need to explicitly add the "implements Serializable" clause following the enum declaration.
I am assuming that in the public enum Unit
code block, you mean Unit
instead of WeightMeasurementUnit
.
The Weight
class has only a weight
and a unit
, so if you pass {"wt":100.5,"unit":"lbs"}
, it should work, because a unit
is just a unit without value. So there is no way for the deserializer to parse {"LBS":3}
as a Unit
. What is the 3
for?
Another problem is that your value is "lbs" whereas you are passing "LBS". So either you need to standardise or you need to use unit.getValue().equalsIgnoreCase(val)
I would suggest you update your jackson version to 2.7.0-rc2 (and probably also before) and then configure the ObjectMapper as follows:
private ObjectMapper createObjectMapper() {
final ObjectMapper mapper = new ObjectMapper();
// enable toString method of enums to return the value to be mapped
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
return mapper;
}
In your enum you just have to override the toString() method:
public enum Unit {
KG,
GM,
LBS,
OZ;
// UPDATE: implicitly already the default so override not needed in this case
@Override
public String toString() {
return name();
}
}
You don't need any annotations or custom deserializers. This would be the way to map a simple enum to a json and vice-versa.
If your enum should be mapped from a special string you have to add a value field and a Constructor which assigns this field and return the value in the toString method.
public enum Unit {
KG("kilogram"),
GM("gram"),
LBS("blah"),
OZ("anything");
Unit(final String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With