jackson-dataformat-xml
jackson-dataformat-xml copied to clipboard
Can't deserialize list in JsonSubtype when type property is visible
Using the latest 2.12.2 version.
Getting com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No fallback setter/field defined for creator property 'ListItem' (through reference chain: cz.smarteon.loxone.system.status.Child["ListItem"]) when parent's @JsonTypeInfo(visible = true).
So it seems it's not possible to have the type property visible, while using collections in subtypes.
I also have more complex scenario, where the deserialization doesn't fail, but the list in subtype is simply not filled (which is even worse). However I wasn't able to make it short enough to include it in report, instead I found this. This could be also related to #426 ? (just a wild guess)
package test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class VisibleTypePropertyTest {
public static void main(String[] args) throws Exception {
final ObjectMapper mapper = XmlMapper.builder().defaultUseWrapper(false).build();
final String parentXml = "<Item type=\"parent\" someProperty=\"someValue\" />";
final String childXml = "<Item type=\"child\" someProperty=\"someValue2\">" +
"<ListItem name=\"a\"/><ListItem name=\"b\" />" +
"</Item>";
mapper.readValue(parentXml.getBytes(StandardCharsets.UTF_8), Parent.class);
try {
mapper.readValue(childXml.getBytes(StandardCharsets.UTF_8), Parent.class);
} catch (InvalidDefinitionException ex) {
System.out.println("This is the BUG");
ex.printStackTrace();
}
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type", defaultImpl = Parent.class
, visible = true // this triggers the BUG
)
@JsonSubTypes({
@JsonSubTypes.Type(name = "child", value = Child.class)
})
class Parent {
final String visibleType;
final String someProperty;
@JsonCreator
Parent(@JsonProperty("type") final String visibleType, @JsonProperty("someProperty") final String someProperty) {
this.visibleType = visibleType;
this.someProperty = someProperty;
}
}
class Child extends Parent {
final List<ChildItem> list;
@JsonCreator
Child(@JsonProperty("type") final String visibleType, @JsonProperty("someProperty") final String someProperty,
@JsonProperty("ListItem") final List<ChildItem> list) {
super(visibleType, someProperty);
this.list = list;
}
}
class ChildItem {
@JsonCreator
ChildItem(@JsonProperty("name") final String name) {}
}
Update, the problem only occurs with defaultUseWrapper(false) and the most probably is caused by
https://github.com/FasterXML/jackson-dataformat-xml/blob/5e928b2c8bce48dcb66be25c29bf35686b490b4f/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/WrapperHandlingDeserializer.java#L150
Here it is expected the JsonParser to be configured is either one passed as argument or the delegate one, however, in this scenario JsonParserSequence is passed which is changing the delegate when iterating through nextToken.
I guess other place for this configuration must be found, however it's beyond my knowledge
And moreover this BUG deosn't happen, when the type determining property is the first one (therefore JsonParserSequence is not used).
The problem is almost certainly due to buffering, then, necessary often when order of properties does not match what @JsonCreator needs: parser sequence is used, but mostly the issue is that TokenBuffer usage: XML deserialization code requires XML-specific parser and TokenBuffer is not one.
I hope this can be addressed in 2.13 as there is a plan to allow format-specific TokenBuffer sub-classes. That alone is not enough, but might allow a fix with xml-specific variant.