Hi Again,
can really nobody help me? Is important information missing from my
request? And off course this is a stripped version of my code to reproduce
the case. I really need the inheritance. To me this also looks like the
final road block. It would be really nice when I could get some feedback
from a more experienced dev.
Regards,
Michael
Michael Schäfer schrieb am Mittwoch, 2. März 2022 um 16:47:49 UTC+1:
> Hi guys,
>
> I'm currently facing an odd behavior with how polymorphic XML objects are
> deserialized when using Jackson 2.13.1. The deserialization fails when the
> type attribute is not the first attribute in the setting tag. This seems
> odd, as Jackson can even properly determine the correct type. My issue with
> this case is that my code has to handle XML documents that are generated by
> a source I don't have control over and it sadly generates XML documents
> where the type attribute is put last. So for example the following XML
> works:
>
> <setting type="localizedString" key="testKey">
> <value locale="default">testValue</value>
> <value locale="de-DE">testValue deutsch</value>
> </setting>
>
> and the following XML fails:
>
> <setting key="testKey" type="localizedString">
> <value locale="default">testValue</value>
> <value locale="de-DE">testValue deutsch</value>
> </setting>
>
> with the following error:
> com.fasterxml.jackson.databind.JsonMappingException: Unexpected
> non-whitespace text ('testValue' in Array context: should not occur (or
> should be handled)
> at [Source: (StringReader); line: 2, column: 42] (through reference
> chain:
> ch.michael.experimental.JacksonInheritanceOrder$LocalizedSetting["value"])
> at
> com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:392)
> at
> com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)
> at
> com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1821)
> at
> com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:315)
> at
> com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:214)
> at
> com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:186)
> at
> com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:122)
> at
> com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:144)
> at
> com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:110)
> at
> com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
> at
> com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:74)
> at
> com.fasterxml.jackson.dataformat.xml.deser.XmlDeserializationContext.readRootValue(XmlDeserializationContext.java:91)
> at
> com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
> at
> com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
> at
> com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)
> at
> ch.michael.experimental.JacksonInheritanceOrder.executeTest(JacksonInheritanceOrder.java:143)
> at
> ch.michael.experimental.JacksonInheritanceOrder.deserializeTrailingType(JacksonInheritanceOrder.java:128)
> at
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
> Method)
> at
> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> at
> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> at java.base/java.lang.reflect.Method.invoke(Method.java:566)
> at
> org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
> at
> org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
> at
> org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
> at
> org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
> at
> org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
> at
> org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
> at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
> at
> org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
> at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
> at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
> at
> org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
> at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
> at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
> at
> org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
> at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
> at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
> at
> org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
> at
> org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
> at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
> at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
> at
> org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:93)
> at
> org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
> at
> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
> Caused by: com.fasterxml.jackson.core.JsonParseException: Unexpected
> non-whitespace text ('testValue' in Array context: should not occur (or
> should be handled)
> at [Source: (StringReader); line: 2, column: 42]
> at
> com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2391)
> at
> com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser.nextToken(FromXmlParser.java:847)
> at
> com.fasterxml.jackson.core.util.JsonParserSequence.nextToken(JsonParserSequence.java:151)
> at
> com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:346)
> at
> com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
> at
> com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
> at
> com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
> at
> com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
> ... 43 more
>
> For demo purposes I have create the following little demo test:
>
> import static org.junit.Assert.assertEquals;
>
> import java.util.LinkedList;
> import java.util.List;
>
> import org.junit.After;
> import org.junit.AfterClass;
> import org.junit.Before;
> import org.junit.BeforeClass;
> import org.junit.Test;
>
> import com.fasterxml.jackson.annotation.JsonInclude;
> import com.fasterxml.jackson.annotation.JsonSubTypes;
> import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
> import com.fasterxml.jackson.annotation.JsonTypeInfo;
> import com.fasterxml.jackson.dataformat.xml.XmlMapper;
> import
> com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
> import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
> import
> com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
> import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
>
> public class JacksonInheritanceOrder {
>
> @JsonInclude(JsonInclude.Include.NON_NULL)
> @JacksonXmlRootElement(localName = "setting")
> @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include =
> JsonTypeInfo.As.EXISTING_PROPERTY, property = Setting.ATTRIBUTE_TYPE)
> @JsonSubTypes({@Type(value = LocalizedSetting.class, name =
> LocalizedSetting.TYPE)})
> public static interface Setting {
>
> public static final String ATTRIBUTE_TYPE = "type";
>
> @JacksonXmlProperty(localName = "key", isAttribute = true)
> public String getKey();
>
> public void setKey(String key);
>
> @JacksonXmlProperty(localName = ATTRIBUTE_TYPE, isAttribute = true)
> public String getType();
> }
>
> public static class LocalizedSetting implements Setting {
>
> public static final String TYPE = "localizedString";
>
> @JacksonXmlProperty(localName = "key", isAttribute = true)
> private String key;
>
> @JacksonXmlElementWrapper(useWrapping = false)
> @JacksonXmlProperty(localName = "value")
> private List<LocalizedValue> localizedValues = new LinkedList<>();
>
> @Override
> public String getKey() {
> return key;
> }
>
> @Override
> public void setKey(String key) {
> this.key = key;
> }
>
> public List<LocalizedValue> getLocalizedValues() {
> return localizedValues;
> }
>
> public void setLocalizedValues(List<LocalizedValue> values) {
> this.localizedValues = values;
> }
>
> @Override
> public String getType() {
> return TYPE;
> }
> }
>
> @JacksonXmlRootElement(localName = "value")
> public static class LocalizedValue {
>
> @JacksonXmlProperty(localName = "locale", isAttribute = true)
> private String locale;
>
> @JacksonXmlText
> private String value;
>
> public String getLocale() {
> return locale;
> }
>
> public void setLocale(String locale) {
> this.locale = locale;
> }
>
> public String getValue() {
> return value;
> }
>
> public void setValue(String value) {
> this.value = value;
> }
> }
>
> @BeforeClass
> public static void setUpBeforeClass() throws Exception {}
>
> @AfterClass
> public static void tearDownAfterClass() throws Exception {}
>
> @Before
> public void setUp() throws Exception {}
>
> @After
> public void tearDown() throws Exception {}
>
> private static final String XML_TMPL = "<setting%s key=\"testKey\"%s>\n"
> + "<value locale=\"default\">testValue</value>\n"
> + "<value locale=\"de-DE\">testValue deutsch</value>\n"
> + "</setting>";
>
> /**
> * Fails with JsonMappingException?!
> *
> * @throws Exception
> */
> @Test
> public void deserializeTrailingType() throws Exception {
> executeTest(String.format(XML_TMPL, "", " type=\"localizedString\""));
> }
>
> /**
> * Works as expected.
> *
> * @throws Exception
> */
> @Test
> public void deserializeLeadingType() throws Exception {
> executeTest(String.format(XML_TMPL, " type=\"localizedString\"", ""));
> }
>
> private void executeTest(String xmlInput) throws Exception {
> XmlMapper xmlMapper = new XmlMapper();
> Setting setting = xmlMapper.readValue(xmlInput, Setting.class);
> assertEquals("testKey", setting.getKey());
>
> assertEquals(LocalizedSetting.class, setting.getClass());
> List<LocalizedValue> values =
> ((LocalizedSetting)setting).getLocalizedValues();
> assertEquals(2, values.size());
> assertEquals("default", values.get(0).getLocale());
> assertEquals("testValue", values.get(0).getValue());
> assertEquals("de-DE", values.get(1).getLocale());
> assertEquals("testValue deutsch", values.get(1).getValue());
> }
> }
>
> Is this a bug or known issue? Can I work around it?
>
--
You received this message because you are subscribed to the Google Groups
"jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/jackson-user/0afadee1-5b12-4b33-874c-d4f2632a511cn%40googlegroups.com.