0

I am working with a third-party service that has provided us with an XSD, and I use a maven plugin (maven-jaxb2-plugin) to generate the corresponding java classes.

Using the JAXB Unmarshaller, I can deserialize the responses from the third-party service. However, due to constraints on the target execution environment, I cannot use the JAXB Unmarshaller - so I settled on using Jackson (jackson-dataformat-xml). However, I cannot get Jackson to deserialize the responses from the third-party service. I have provided representative samples of the XSD, generated code, and usage code below.

What I have tried so far:

What I have not tried:

  • Modifying the generated code: I don't want to have to re-do any work if the XSD changes. This would be a next-to-last resort, if I can be sure it will work.
  • Hand-coding classes per the XSD: ditto above, and the XSD is quite large with a lot of objects. This would be a last resort.

I suspect that Jackson does not like the nested xs:choice tags, but I do not know for sure - I will investigate that soon and post my findings on here, if any.

XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:element name="element1">
        <xs:complexType>
            <xs:choice maxOccurs="unbounded">
                <xs:element name="element1_1">
                    <xs:complexType>
                        <xs:simpleContent>
                            <xs:extension base="xs:string">
                                <xs:attribute name="attribute1">
                                    <xs:simpleType>
                                        <xs:restriction base="xs:string">
                                            <xs:enumeration value="aa"/>
                                            <xs:enumeration value="de"/>
                                            <xs:enumeration value="en"/>
                                        </xs:restriction>
                                    </xs:simpleType>
                                </xs:attribute>
                                <xs:attribute name="attribute2" use="optional" />
                            </xs:extension>
                        </xs:simpleContent>
                    </xs:complexType>
                </xs:element>
                <xs:choice minOccurs="0">
                    <xs:element ref="element2"/>
                    <xs:element name="element1_2" type="xs:string"/>
                </xs:choice>
                <xs:element ref="element3" minOccurs="0"/>
                <xs:element name="element1_3" type="xs:dateTime" minOccurs="0"/>
                <xs:element name="element1_4" type="xs:string" nillable="true" minOccurs="0"/>
            </xs:choice>
        </xs:complexType>
    </xs:element>
    <xs:element name="element2">
        <xs:complexType>
            <xs:choice minOccurs="0" maxOccurs="unbounded">
                <xs:element ref="element4" minOccurs="0" maxOccurs="unbounded"/>
            </xs:choice>
        </xs:complexType>
    </xs:element>
    ...
</xs:schema>

JAXB Generated Class:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "element1_1OrElement2OrElement1_2"
})
@XmlRootElement(name = "ContentObject")
public class Element1 {

    @XmlElements({
        @XmlElement(name = "element1_1", type = JAXBElement.class, required = false),
        @XmlElement(name = "element2", type = Element2.class, required = false),
        @XmlElement(name = "element1_2", type = JAXBElement.class, required = false),
        @XmlElement(name = "element3", type = Element3.class, required = false),
        @XmlElement(name = "element1_3", type = JAXBElement.class, required = false),
        ...
    })
    protected List<Object> element1_1OrElement2OrElement1_2;

    ...

    public List<Object> getElement1_1OrElement2OrElement1_2() {
        if (element1_1OrElement2OrElement1_2 == null) {
            element1_1OrElement2OrElement1_2 = new ArrayList<Object>();
        }
        return this.element1_1OrElement2OrElement1_2;
    }

    ...

}

JUnit Test: The CustomeDeserializer can be ignored - it did not make any difference.

package my.project.webservice;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.bind.JAXBElement;

import org.junit.Test;
import org.w3c.dom.Document;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.jaxb.XmlJaxbAnnotationIntrospector;
import my.project.converter.ConverterException;
import my.project.converter.DocumentToStringConverter;
import my.project.converter.InputStreamToDocumentConverter;
import my.project.jaxb.CustomSerializer;
import my.project.jaxb.JaxbElementMixin;
import my.project.generated.GeneratedClass;

public class JacksonMapperTest {

    @Test
    public void test() {
        try {
            SimpleModule module = new SimpleModule("customeSerializerModule", new Version(1, 0, 0, null));
            module.addSerializer(JAXBElement.class, new CustomSerializer());

            XmlMapper m = new XmlMapper();
            m.setAnnotationIntrospector(new XmlJaxbAnnotationIntrospector (m.getTypeFactory()));
            m.addMixIn(JAXBElement.class, JaxbElementMixin.class);
            m.registerModule(module);

            InputStream is = this.getClass().getResourceAsStream("/stubbed-response.xml");
            Document doc = new InputStreamToDocumentConverter().convert(is);
            String xml = new DocumentToStringConverter().convert(doc);
            GeneratedClass c = m.readValue(xml, GeneratedClass.class);

        } catch (ConverterException e) {
            e.printStackTrace();
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

Stack Trace:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "element2" (class my.project.models.xjb.Element1), not marked as ignorable (15 known properties: "element1_1OrElement2OrElement3", "element1_3", ...])
 at [Source: java.io.StringReader@6b419da; line: 3, column: 39] (through reference chain: my.project.generated.Element1["element2"]->java.util.ArrayList[0]->my.project.generated.Element1["element2"])
    at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
    at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:817)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:954)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1315)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1293)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:249)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:136)
    at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:109)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:240)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:212)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:25)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:523)
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:101)
    at com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap.findDeserializeAndSet(BeanPropertyMap.java:285)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:248)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:136)
    at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:109)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3560)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2576)
    at my.project.webservice.JacksonMapperTest.test(JacksonMapperTest.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at mockit.integration.junit4.internal.JUnit4TestRunnerDecorator.executeTestMethod(JUnit4TestRunnerDecorator.java:131)
    at mockit.integration.junit4.internal.JUnit4TestRunnerDecorator.invokeExplosively(JUnit4TestRunnerDecorator.java:71)
    at mockit.integration.junit4.internal.MockFrameworkMethod.invokeExplosively(MockFrameworkMethod.java:42)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at mockit.internal.util.MethodReflection.invokeWithCheckedThrows(MethodReflection.java:112)
    at mockit.internal.mockups.MockMethodBridge.callMock(MockMethodBridge.java:85)
    at mockit.internal.mockups.MockMethodBridge.invoke(MockMethodBridge.java:44)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

1 Answers1

-1

Use this:

objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Here example: http://wiki.fasterxml.com/JacksonHowToIgnoreUnknown

Sushant Tambare
  • 526
  • 2
  • 9
  • Unfortunately, that does not work. The objects that are deserialized have many fields that are null - and I need the entire object populated based on the response. – Arun Mascarenhas Feb 06 '15 at 14:43
  • Unknown property is the property which is not defined in XSD and is not known. Also it's setter method is not defined. Hence this issue comes is my understanding. right? So if this is correct, then I think fields that are null is different than fields not known in XSD. – Sushant Tambare Feb 06 '15 at 17:14
  • 1
    If I did not call it out clearly in my question, I apologize. But the property is defined in the XSD, and the corresponding setter is available in the XJC generated class. However, the setter name is of the format setAOrBOrC because the missing field is one option in an xs:choice type in the XSD. Also, I can deserialize using JAXB. It is Jackson that does not work. – Arun Mascarenhas Feb 07 '15 at 01:53