-
Notifications
You must be signed in to change notification settings - Fork 20
Jackson interpretation of XMLElements not compatible with JAXB #51
Comments
This is the case of differing defaults; Jackson defaults to using wrapper element, whereas JAXB does not. You can use Jackson annotation from module |
@JacksonXmlElement(useWrapping=false) does exactly the opposite of what I want/need. It removes the outer list wrapper but the individual list elements are still being wrapped. I am not seeing anything that allows me to change the defaults to JAXB, sorry. |
Ok. So this is actually more related to polymorphic type handling, where type identifier is to be used as the element wrapper. Would it be possible to include definitions of value types (or at least one)? |
Sorry, I was bitten by the web rendering of the unescaped <Computer> tag because my code does have the List type information: @XmlRootElement(name="company") @XmlAccessorType(XmlAccessType.FIELD) public class Company { //@XmlElementWrapper(name = "computers") @XmlElements({ @XmlElement(type = DesktopComputer.class, name = "desktop"), @XmlElement(type = LaptopComputer.class, name = "laptop") }) private List<Computer> computers; ... I am not sure exactly what you mean by including the "definitions of value types" (isn't that what the @XmlElements annotation above is doing?). My Computer super class also has the @XmlSeeAlso annotation with the type information: @XmlSeeAlso({ LaptopComputer.class, DesktopComputer.class }) @XmlAccessorType(XmlAccessType.FIELD) public class Computer { @XmlAttribute @XmlID private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } I have full control over the source so I can add additional annotations, if required, to get the proper XML shape. Thanks, |
I guess it would be great to split this problem into two parts, due to 2 possible problem areas:
So, a reproduction that only did one of the two would be most helpful in figuring out the actual issue. |
I will work on this and at a minimum, provide more details and a reproducer of the specific problem(s). |
JAXB annotations with JSON fails in Jackson 2.7.0-rc2: Using Jackson to read the file previously written by Jackson: |
However, the JSON generated does not have the extra layer of list element wrappers (i.e., it is what I would expect). Full reproducer is at https://github.com/rpatrick00/jackson-list-test {
"computers" : [ {
"desktop" : {
"id" : "computer-1",
"location" : "Bangkok"
}
}, {
"desktop" : {
"id" : "computer-2",
"location" : "Pattaya"
}
}, {
"laptop" : {
"id" : "computer-3",
"vendor" : "Apple"
}
} ]
} |
In switching to Jackson annotations (so that I could switch to the XML serialization part of the problem), I am able to generate the proper JSON but Jackson is still failing to read it back with Jackson 2.7.0-rc2. Maybe I omitted something in the conversion from JAXB annotations (since I am less familiar with them than JAXB annotations)? Here are the details (reproducer is the same as above, just changed the POJO annotations as shown below). The reproducer with Jackson annotations is available at https://github.com/rpatrick00/jackson-xml-list-test:. Error: Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (FIELD_NAME), expected START_OBJECT: need JSON Object to contain As.WRAPPER_OBJECT type information for class test.Computer at [Source: company-jackson.json; line: 3, column: 5] (through reference chain: test.Company["computers"]->java.util.ArrayList[0]) at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:216) at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:962) at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer._deserialize(AsWrapperTypeDeserializer.java:91) at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer.deserializeTypedFromObject(AsWrapperTypeDeserializer.java:49) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:992) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:279) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:249) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26) at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:490) at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:95) at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:257) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:125) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3773) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2657) at test.RunThis.readJacksonFile(RunThis.java:61) at test.RunThis.main(RunThis.java:94) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) Company.java @JsonRootName(value = "company") public class Company { private List<Computer> computers; public Company() { computers = new ArrayList<Computer>(); } public List<Computer> getComputers() { return computers; } public void setComputers(List<Computer> computers) { this.computers = computers; } public Company addComputer(Computer computer) { if (computers == null) { computers = new ArrayList<Computer>(); } computers.add(computer); return this; } } Computer.java @JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "id" ) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT, property = "type" ) @JsonSubTypes({ @JsonSubTypes.Type(value = DesktopComputer.class, name = "desktop"), @JsonSubTypes.Type(value = LaptopComputer.class, name = "laptop") }) public class Computer { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } DesktopComputer.java @JsonTypeName("desktop") public class DesktopComputer extends Computer { @JsonProperty("location") private String location; public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } } LaptopComputer.java @JsonTypeName("laptop") public class LaptopComputer extends Computer { @JsonProperty("vendor") private String vendor; public String getVendor() { return vendor; } public void setVendor(String vendor) { this.vendor = vendor; } } JSON file generated { "computers" : [ { "desktop" : { "id" : "computer-1", "location" : "Bangkok" } }, { "desktop" : { "id" : "computer-2", "location" : "Pattaya" } }, { "laptop" : { "id" : "computer-3", "vendor" : "Apple" } } ] } |
Switching the native Jackson annotations to serialize XML instead of JSON does two undesirable things: 1.) It throws the same error as above <?xml version='1.1' encoding='UTF-8'?> <company> <computers> <computers> <desktop> <id>computer-1</id> <location>Bangkok</location> </desktop> </computers> <computers> <desktop> <id>computer-2</id> <location>Pattaya</location> </desktop> </computers> <computers> <laptop> <id>computer-3</id> <vendor>Apple</vendor> </laptop> </computers> </computers> </company> The reproducer with Jackson annotations is available at https://github.com/rpatrick00/jackson-xml-list-test. If you run the RunThis main class without any args, it will serialize XML. Add the -useJson arg to get it to serialize Json instead. |
In Jackson 2.4.3, we were able to strip out these unwanted wrapper objects using a custom serializer/deserializer (even though we would have preferred not to have to write custom code, it was our only option). That custom code no longer works in Jackson 2.6.3 and newer (didn't test the intermediate versions to see where it broke) and, of course, requires the use of Jackson annotations to register them. If I remember correctly, the error when we tried to use the custom serializers/deserializers to strip out the wrappers in 2.6.3 was very similar to, if not exactly, the error above. In my opinion, it would be very desirable to eliminate the need for the custom serializers/deserializers to make it possible to get Jackson's XML output with JAXB annotations to match JAXB exactly. While it would be nice for us if this were the default when using the Jaxb Annotations Module, I would be ok with having to set additional mapper/module properties, if necessary, but would like to not have to add Jackson annotations. |
@rpatrick00 Thank you for the test and investigation! I will need to read this with thought, hoping to start resolving parts of the problem. |
@rpatrick00 Interesting. I can reproduce the basic |
Ok, yes. And another piece of the puzzle is that handling that is needed to allow JSON Object wrapped Object Id (for JSOG) is somehow causing the issue, as an unintended side effect. Change probably went in 2.5 (guessing it might be issue FasterXML/jackson-databind#669) -- and building from tag 2.5.0 does indeed pass, and latest 2.5 from branch fails. So that's what triggers the problem. |
Ok, first things first: the underlying problem with databind is fixed (for 2.6.5 patch, and Now, as to XML/JAXB part, it appears like JAXB annotations are not to blame, as the results are the same with equivalent Jackson I will try to work on this for XML module next. |
Will close this issue now, assuming problem exists within XML module, and is not due to translation of JAXB annotations itself. |
So is there an existing issue on the XML side? |
@rpatrick00 I assumed one of existing issues would cover it. But if not, a new one would be needed over there. If I understand issue correctly it is combination of polymorphic serialization as XML, using Description few comments up should cover it, although due to a fix to I'll file a new issue just in case, linking back to this one -- if there is a duplicate, it can be closed, but it's better to have two than none. |
Re-created remaining problem as: FasterXML/jackson-dataformat-xml#178 |
I have a very simple example of a class with a List member that uses polymorphism. The Java code looks like this:
JAXB serializes the output correctly (or at least in the way that I want it) like this:
Jackson JAXB annotations wraps each resulting element in the list in a redundant <computers> tag like this:
How can I eliminate the extra wrapping Jackson is doing to generate the same shape as JAXB?
The text was updated successfully, but these errors were encountered: