From b5ae1e8068ca0e305ba057e5338b9e9c0e583dab Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 23 Sep 2019 22:11:15 -0700 Subject: [PATCH] Fix #325 --- release-notes/VERSION-2.x | 2 + .../xml/DefaultingXmlTypeResolverBuilder.java | 83 +++++++++++++++++++ .../jackson/dataformat/xml/XmlMapper.java | 4 +- .../xml/XmlTypeResolverBuilder.java | 3 + .../xml/failing/DefaultTyping325Test.java | 23 +++-- .../xml/testutil/NoCheckSubTypeValidator.java | 19 ++++- 6 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/fasterxml/jackson/dataformat/xml/DefaultingXmlTypeResolverBuilder.java diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 2a2d3cefb..07ec43080 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -8,6 +8,8 @@ Project: jackson-dataformat-xml #242: Deserialization of class inheritance depends on attributes order (reported by Victor K) +#325: Problem with '$' in polymorphic type id names when "as class", + "wrapper object", inner class #326: Force namespace-repairing on `XMLOutputFactory` instances #354: Support mapping `xsi:nul` marked elements as `null`s (`JsonToken.VALUE_NULL`) diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/DefaultingXmlTypeResolverBuilder.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/DefaultingXmlTypeResolverBuilder.java new file mode 100644 index 000000000..a545728e7 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/DefaultingXmlTypeResolverBuilder.java @@ -0,0 +1,83 @@ +package com.fasterxml.jackson.dataformat.xml; + +import java.util.Collection; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; + +import com.fasterxml.jackson.dataformat.xml.util.StaxUtil; + +/** + * Sub-class of {@code StdTypeResolverBuilder} specifically used with + * Default Typing. + *

+ * Composition/sub-classing gets quite tricky here: ideally we would just + * extend {@link XmlTypeResolverBuilder} but unfortunately inheritance hierarchy + * does not allow this. + * + * @since 2.10 + */ +public class DefaultingXmlTypeResolverBuilder + extends ObjectMapper.DefaultTypeResolverBuilder + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + public DefaultingXmlTypeResolverBuilder(DefaultTyping t, PolymorphicTypeValidator ptv) { + super(t, ptv); + } + + /* + /********************************************************************** + /* Methods copied from `XmlTypeResolverBuilder` + /********************************************************************** + */ + + @Override + public StdTypeResolverBuilder init(JsonTypeInfo.Id idType, TypeIdResolver idRes) + { + super.init(idType, idRes); + if (_typeProperty != null) { + _typeProperty = StaxUtil.sanitizeXmlTypeName(_typeProperty); + } + return this; + } + + @Override + public StdTypeResolverBuilder typeProperty(String typeIdPropName) + { + // ok to have null/empty; will restore to use defaults + if (typeIdPropName == null || typeIdPropName.length() == 0) { + typeIdPropName = _idType.getDefaultPropertyName(); + } + _typeProperty = StaxUtil.sanitizeXmlTypeName(typeIdPropName); + return this; + } + + @Override + protected TypeIdResolver idResolver(MapperConfig config, + JavaType baseType, PolymorphicTypeValidator subtypeValidator, + Collection subtypes, boolean forSer, boolean forDeser) + { + if (_customIdResolver != null) { + return _customIdResolver; + } + // Only override handlers of class, minimal class; name is good as is + switch (_idType) { + case CLASS: + return new XmlTypeResolverBuilder.XmlClassNameIdResolver(baseType, config.getTypeFactory(), + subTypeValidator(config)); + case MINIMAL_CLASS: + return new XmlTypeResolverBuilder.XmlMinimalClassNameIdResolver(baseType, config.getTypeFactory(), + subTypeValidator(config)); + default: + } + return super.idResolver(config, baseType, subtypeValidator, subtypes, forSer, forDeser); + } +} diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java index a5f42a93b..6e1a58a47 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java @@ -212,9 +212,7 @@ public Version version() { @Override // since 2.10 protected TypeResolverBuilder _constructDefaultTypeResolverBuilder(DefaultTyping applicability, PolymorphicTypeValidator ptv) { -// return DefaultTypeResolverBuilder.cosntruct(applicability, ptv); - // just a stub for now - return super._constructDefaultTypeResolverBuilder(applicability, ptv); + return new DefaultingXmlTypeResolverBuilder(applicability, ptv); } /* diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlTypeResolverBuilder.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlTypeResolverBuilder.java index e23f14f14..6f7f21aea 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlTypeResolverBuilder.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlTypeResolverBuilder.java @@ -4,6 +4,7 @@ import java.util.Collection; import com.fasterxml.jackson.annotation.JsonTypeInfo; + import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.jsontype.NamedType; @@ -13,6 +14,7 @@ import com.fasterxml.jackson.databind.jsontype.impl.MinimalClassNameIdResolver; import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; import com.fasterxml.jackson.databind.type.TypeFactory; + import com.fasterxml.jackson.dataformat.xml.util.StaxUtil; /** @@ -25,6 +27,7 @@ public class XmlTypeResolverBuilder extends StdTypeResolverBuilder @Override public StdTypeResolverBuilder init(JsonTypeInfo.Id idType, TypeIdResolver idRes) { + super.init(idType, idRes); if (_typeProperty != null) { _typeProperty = StaxUtil.sanitizeXmlTypeName(_typeProperty); diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/DefaultTyping325Test.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/DefaultTyping325Test.java index 3f78b0821..dc8504249 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/DefaultTyping325Test.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/DefaultTyping325Test.java @@ -1,37 +1,36 @@ package com.fasterxml.jackson.dataformat.xml.failing; import java.io.IOException; -import java.util.Arrays; -import java.util.List; 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.testutil.NoCheckSubTypeValidator; public class DefaultTyping325Test extends XmlTestBase { - static class Simple { - protected List list; + static class Simple325 { + protected String[] list; - public List getList( ) { return list; } - public void setList(List l) { list = l; } + public String[] getList( ) { return list; } + public void setList(String[] l) { list = l; } } - public void testCanSerialize() throws IOException + // [dataformat-xml#325] + public void testDefaultTypingWithInnerClass() throws IOException { ObjectMapper mapper = mapperBuilder() .activateDefaultTyping(NoCheckSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_OBJECT) .build(); - // construct test object - Simple s = new Simple(); - s.setList(Arrays.asList("foo", "bar")); + Simple325 s = new Simple325(); + s.setList(new String[] { "foo", "bar" }); String doc = mapper.writeValueAsString(s); - Simple result = mapper.readValue(doc, Simple.class); + Simple325 result = mapper.readValue(doc, Simple325.class); assertNotNull(result.list); - assertEquals(2, result.list.size()); + assertEquals(2, result.list.length); } } diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/testutil/NoCheckSubTypeValidator.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/testutil/NoCheckSubTypeValidator.java index b90f1de7d..ff66a424c 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/testutil/NoCheckSubTypeValidator.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/testutil/NoCheckSubTypeValidator.java @@ -19,4 +19,21 @@ public final class NoCheckSubTypeValidator public Validity validateBaseType(MapperConfig c, JavaType baseType) { return Validity.ALLOWED; } -} \ No newline at end of file + + // Important! With Default Typing, "validateBaseType()" won't necessarily + // be called, at least for root type, so need more than above method: + + @Override + public Validity validateSubClassName(MapperConfig config, + JavaType baseType, String subClassName) { + return Validity.ALLOWED; + } + + /* + @Override + public Validity validateSubType(MapperConfig config, JavaType baseType, + JavaType subType) { + return Validity.ALLOWED; + } + */ +}