I am trying to parse a JSON string with jackson that looks like:
{
"name":"John",
"Wrapper":{
"id":0
}
}
I am trying to prevent having to make another Java class for Wrapper and simply map it to an integer instead. I tried using @XmlElementWrapper even though the documentation states:
This is primarily intended to be used to produce a wrapper XML element
around collections.
But that does not work. I get the following exception:
Exception in thread "main" org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.lang.Integer out of START_OBJECT token
at [Source: java.io.StringReader@44eefb4; line: 1, column: 15] (through reference chain: Test["Wrapper"])
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
at org.codehaus.jackson.map.deser.std.StdDeserializer._parseInteger(StdDeserializer.java:305)
at org.codehaus.jackson.map.deser.std.StdDeserializer$IntegerDeserializer.deserialize(StdDeserializer.java:795)
at org.codehaus.jackson.map.deser.std.StdDeserializer$IntegerDeserializer.deserialize(StdDeserializer.java:782)
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.ObjectMapper._readMapAndClose(ObjectMapper.java:2723)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1854)
at Test.main(Test.java:37)
Here is a runnable example:
@XmlAccessorType(XmlAccessType.FIELD)
public class Test {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
String json =
"{" +
"\"name\":\"John\","+
"\"Wrapper\":{"+
" \"id\":0}"+
"}";
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector());
mapper.setSerializationInclusion(Inclusion.NON_NULL);
Test test = mapper.readValue(json, Test.class);
System.out.println(test.toString());
}
@XmlElement(name="name")
private String name;
@XmlElementWrapper(name="Wrapper")
@XmlElement(name="id")
private Integer wrapperId;
@Override
public String toString() {
return "Test [name=" + name + ", wrapperId=" + wrapperId + "]";
}
}
解决方案
Jackson provides limited number of structure transform annotations (@JsonUnwrapped, root value wrapping), but not something for this use case. I think there is actually a feature request for this particulal use case (it'd be @JsonWrapped I think).
For what it is worth, Jackson JAXB annotation module does recognize the annotation, but it is not used for JSON (it is used for XML backend, but just for Collection and array valued properties).
I would just add a simple static class Wrapper; or, if it is a common idiom, shared generic class Wrapper, to use for all kinds of wrapped values. Amount of code would be simple, and Object structure would then match 1-to-1 with JSON data structure.