diff --git a/src/main/java/tools/jackson/dataformat/xml/ser/XmlBeanSerializerBase.java b/src/main/java/tools/jackson/dataformat/xml/ser/XmlBeanSerializerBase.java index 29db12cb..6c63e839 100644 --- a/src/main/java/tools/jackson/dataformat/xml/ser/XmlBeanSerializerBase.java +++ b/src/main/java/tools/jackson/dataformat/xml/ser/XmlBeanSerializerBase.java @@ -315,9 +315,11 @@ public void serializeWithType(Object bean, JsonGenerator gen, SerializationConte return; } // Ok: let's serialize type id as attribute, but if (and only if!) - // we are using AS_PROPERTY - if (typeSer.getTypeInclusion() == JsonTypeInfo.As.PROPERTY) { - ToXmlGenerator xgen = (ToXmlGenerator)gen; + // we are using AS_PROPERTY -- and only when writing real XML. During + // convertValue()/valueToTree() `gen` is a TokenBuffer/tree generator, + // so skip the attribute handling (as `_serializeProperties` does). + if (typeSer.getTypeInclusion() == JsonTypeInfo.As.PROPERTY + && gen instanceof ToXmlGenerator xgen) { xgen.setNextIsAttribute(true); super.serializeWithType(bean, gen, ctxt, typeSer); if (_attributeCount == 0) { // if no attributes, need to reset @@ -333,9 +335,11 @@ protected void _serializeObjectId(Object bean, JsonGenerator gen, SerializationC TypeSerializer typeSer, WritableObjectId objectId) throws JacksonException { - // Ok: let's serialize type id as attribute, but if (and only if!) we are using AS_PROPERTY - if (typeSer.getTypeInclusion() == JsonTypeInfo.As.PROPERTY) { - ToXmlGenerator xgen = (ToXmlGenerator)gen; + // Ok: let's serialize type id as attribute, but if (and only if!) we are + // using AS_PROPERTY -- and only when writing real XML (not a TokenBuffer + // from convertValue()/valueToTree()). + if (typeSer.getTypeInclusion() == JsonTypeInfo.As.PROPERTY + && gen instanceof ToXmlGenerator xgen) { xgen.setNextIsAttribute(true); super._serializeObjectId(bean, gen, ctxt, typeSer, objectId); if (_attributeCount == 0) { // if no attributes, need to reset diff --git a/src/test/java/tools/jackson/dataformat/xml/ser/PolymorphicConvertValueTest.java b/src/test/java/tools/jackson/dataformat/xml/ser/PolymorphicConvertValueTest.java new file mode 100644 index 00000000..ec996777 --- /dev/null +++ b/src/test/java/tools/jackson/dataformat/xml/ser/PolymorphicConvertValueTest.java @@ -0,0 +1,65 @@ +package tools.jackson.dataformat.xml.ser; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.*; + +import tools.jackson.databind.JsonNode; +import tools.jackson.dataformat.xml.XmlMapper; +import tools.jackson.dataformat.xml.XmlTestUtil; + +import static org.junit.jupiter.api.Assertions.*; + +// Polymorphic beans (default `As.PROPERTY` inclusion) failed with a +// `ClassCastException` when run through `convertValue()`/`valueToTree()`, +// because those serialize into a `TokenBuffer`/tree generator rather than a +// `ToXmlGenerator`, which the type-id handling cast to unconditionally. +public class PolymorphicConvertValueTest extends XmlTestUtil +{ + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) + @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog") }) + static abstract class Animal { + public String name; + } + + static class Dog extends Animal { + public int barks; + + public Dog() { } + public Dog(String n, int b) { name = n; barks = b; } + } + + private final XmlMapper MAPPER = newMapper(); + + @Test + public void testValueToTree() throws Exception + { + Animal input = new Dog("Rex", 3); + JsonNode tree = MAPPER.valueToTree(input); + assertEquals("dog", tree.path("_type").asString()); + assertEquals("Rex", tree.path("name").asString()); + assertEquals(3, tree.path("barks").asInt()); + } + + @Test + public void testConvertValueToMap() throws Exception + { + Animal input = new Dog("Rex", 3); + @SuppressWarnings("unchecked") + Map map = MAPPER.convertValue(input, Map.class); + assertEquals("dog", map.get("_type")); + assertEquals("Rex", map.get("name")); + assertEquals(3, map.get("barks")); + } + + // Real XML serialization must be unchanged: type id still written as attribute + @Test + public void testXmlSerializationUnchanged() throws Exception + { + Animal input = new Dog("Rex", 3); + String xml = MAPPER.writeValueAsString(input); + assertTrue(xml.contains("_type=\"dog\""), "should write type id as attribute: " + xml); + } +}