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.

Reply via email to