diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index db0ce088d..bd4f9aafa 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -12,6 +12,8 @@ Project: jackson-dataformat-xml #294: XML parser error with nested same element names (reported by Alexei V) #301: Problem deserializing POJO with unwrapped `List`, ignorable attribute value +#389: Exception when serializing with type via mapper.writerFor(type).write(...) + (reported by texhnolyzze@github) #393: `MismatchedInputException` for nested repeating element name in `List` (reported by kaizenHorse@github) #399: Can not deserialize unwrapped list when `@JacksonXmlProperty` localName matches diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/ser/XmlSerializerProvider.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/ser/XmlSerializerProvider.java index c0688f5ed..01349a103 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/ser/XmlSerializerProvider.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/ser/XmlSerializerProvider.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.PropertyName; import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.SerializerFactory; import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; import com.fasterxml.jackson.databind.util.TokenBuffer; @@ -80,6 +81,7 @@ public DefaultSerializerProvider createInstance(SerializationConfig config, @Override public void serializeValue(JsonGenerator gen, Object value) throws IOException { + _generator = gen; if (value == null) { _serializeXmlNull(gen); return; @@ -115,16 +117,27 @@ public void serializeValue(JsonGenerator gen, Object value) throws IOException } } + @Override // since 2.11.1, was missing before + public void serializeValue(JsonGenerator gen, Object value, JavaType rootType) throws IOException + { + serializeValue(gen, value, rootType, null); + } + // @since 2.1 @SuppressWarnings("resource") @Override public void serializeValue(JsonGenerator gen, Object value, JavaType rootType, JsonSerializer ser) throws IOException { + _generator = gen; if (value == null) { _serializeXmlNull(gen); return; } + // Let's ensure types are compatible at this point + if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) { + _reportIncompatibleRootType(value, rootType); + } final boolean asArray; final ToXmlGenerator xgen = _asXmlGenerator(gen); if (xgen == null) { // called by convertValue() @@ -155,6 +168,56 @@ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType, } } + @SuppressWarnings("resource") + @Override // since 2.11.1, was missing before + public void serializePolymorphic(JsonGenerator gen, Object value, JavaType rootType, + JsonSerializer valueSer, TypeSerializer typeSer) + throws IOException + { + _generator = gen; + if (value == null) { + _serializeXmlNull(gen); + return; + } + // Let's ensure types are compatible at this point + if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) { + _reportIncompatibleRootType(value, rootType); + } + final boolean asArray; + final ToXmlGenerator xgen = _asXmlGenerator(gen); + if (xgen == null) { // called by convertValue() + asArray = false; + } else { + QName rootName = _rootNameFromConfig(); + if (rootName == null) { + rootName = _rootNameLookup.findRootName(rootType, _config); + } + _initWithRootName(xgen, rootName); + asArray = TypeUtil.isIndexedType(rootType); + if (asArray) { + _startRootArray(xgen, rootName); + } + } + // 21-May-2020: See comments in `jackson-databind/DefaultSerializerProvider` + if (valueSer == null) { + if ((rootType != null) && rootType.isContainerType()) { + valueSer = findValueSerializer(rootType, null); + } else { + valueSer = findValueSerializer(value.getClass(), null); + } + } + // From super-class implementation + try { + valueSer.serializeWithType(value, gen, this, typeSer); + } catch (Exception e) { // but others do need to be, to get path etc + throw _wrapAsIOE(gen, e); + } + // end of super-class implementation + if (asArray) { + gen.writeEndObject(); + } + } + protected void _serializeXmlNull(JsonGenerator jgen) throws IOException { // 14-Nov-2016, tatu: As per [dataformat-xml#213], we may have explicitly diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/lists/ListWithAttributes.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/lists/ListWithAttributes.java index 52db39141..398821119 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/lists/ListWithAttributes.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/lists/ListWithAttributes.java @@ -10,7 +10,7 @@ public class ListWithAttributes extends XmlTestBase { - // [Issue#43] + // [dataformat-xml#43] static class Name { @JacksonXmlProperty(isAttribute=true) public String language; @@ -29,7 +29,7 @@ static class RoomName { public List names; } - // [Issue#99]: allow skipping unknown properties + // [dataformat-xml#99]: allow skipping unknown properties static class Root { @JacksonXmlElementWrapper(useWrapping = false) @JacksonXmlProperty(localName = "value") @@ -42,7 +42,6 @@ public static class Value { } // [dataformat-xml#108]: unwrapped lists, more than one entry, id attributes - static class Foo { @JacksonXmlElementWrapper(useWrapping = false) public List firstBar = new ArrayList(); @@ -79,7 +78,7 @@ static class ChildB301 { @JacksonXmlProperty(localName = "MY_PROPERTY") public Double value; } - + /* /********************************************************** /* Test methods @@ -88,7 +87,7 @@ static class ChildB301 { private final ObjectMapper MAPPER = newMapper(); - // [Issue#43] + // [dataformat-xml#43] public void testIssue43() throws Exception { String xmlData = "" @@ -100,7 +99,7 @@ public void testIssue43() throws Exception assertEquals("SPECIAL", roomName.names.get(0).text); } - // [Issue#99]: allow skipping unknown properties + // [dataformat-xml#99]: allow skipping unknown properties public void testListWithAttributes() throws Exception { String source = "" @@ -114,7 +113,7 @@ public void testListWithAttributes() throws Exception assertEquals(1, root.values.size()); } - // [Issue#108]: unwrapped lists, more than one entry, id attributes + // [dataformat-xml#108]: unwrapped lists, more than one entry, id attributes public void testIdsFromAttributes() throws Exception { Foo foo = new Foo(); Bar bar1 = new Bar(); diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/misc/EmptyPolymorphicTest.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/ser/EmptyPolymorphicTest.java similarity index 96% rename from src/test/java/com/fasterxml/jackson/dataformat/xml/misc/EmptyPolymorphicTest.java rename to src/test/java/com/fasterxml/jackson/dataformat/xml/ser/EmptyPolymorphicTest.java index 86df3421c..44723f1cd 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/misc/EmptyPolymorphicTest.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/ser/EmptyPolymorphicTest.java @@ -1,4 +1,4 @@ -package com.fasterxml.jackson.dataformat.xml.misc; +package com.fasterxml.jackson.dataformat.xml.ser; import com.fasterxml.jackson.annotation.*; diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/ser/PolymorphicSerialization389Test.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/ser/PolymorphicSerialization389Test.java new file mode 100644 index 000000000..9e80b10de --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/ser/PolymorphicSerialization389Test.java @@ -0,0 +1,53 @@ +package com.fasterxml.jackson.dataformat.xml.ser; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.XmlTestBase; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +public class PolymorphicSerialization389Test extends XmlTestBase +{ + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") + @JsonSubTypes({ + @JsonSubTypes.Type(value = ConcreteModel.class, name = "ConcreteModel") + }) + public abstract class AbstractModel { + + @JacksonXmlProperty(isAttribute = true) + public Long id; + + @JacksonXmlProperty(isAttribute = true) + public String name; + } + + @JacksonXmlRootElement(localName = "Concrete") + public class ConcreteModel extends AbstractModel { + @JacksonXmlProperty(isAttribute = true) + public String someAdditionalField; + } + + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + private final ObjectMapper MAPPER = newMapper(); + + // [dataformat-xml#389] + public void testIssue389() throws Exception + { + ConcreteModel concreteModel = new ConcreteModel(); + concreteModel.id = 1L; + concreteModel.name = "Bob"; + concreteModel.someAdditionalField = "..."; + + String xml1 = MAPPER.writeValueAsString(concreteModel); + String xml2 = MAPPER.writerFor(ConcreteModel.class) + .writeValueAsString(concreteModel); + assertEquals(xml1, xml2); + } +}