javaxmljacksonjaxbxmlmapper

How to fix xmlMapper "Multiple fields representing property "PS"" issue with Jaxb?


I used Jaxb to generate a Jar file from xsd:

CustomObject inputRequest = xmlMapper.readValue(input, CustomObject.class); 

The error message I am getting is:

com.fasterxml.jackson.databind.JsonMappingException: Multiple fields representing property "PS": OrderLineType#pS vs OrderLineType#pSV5 at [Source: (StringReader); line: 1, column: 1]

And my class is:

@XmlElement(name = "PS", namespace = "financetypes:defn:v4")
protected financetypes.v4.PSType pS;
@XmlElement(name = "PS", namespace = "financetypes:defn:v5")
protected financetypes.v5.PSType pSV5;

They have the same name, but different namespace.

Is xmlmapper able to handle XmlElement that has the same name and different namespace?

If yes, how do I fix this? If no, what should I do?

I am building jar file from jenkin. Is there a plugin that I can use to fix this issue?

Thanks!

here is my xsd v4.xsd

    <!--Begin Definition: Complex Type: PriceStructureType-->
<xs:complexType name="PriceStructureType">
    <xs:sequence>
        <xs:element name="PriceModel" type="xs:string" minOccurs="0">
            <xs:annotation>
                <xs:documentation>
                    Will be required in future...
                    TODO: Add valid items...
                </xs:documentation>
            </xs:annotation>
        </xs:element>
        <xs:element ref="finance:SourceSystemPrice" minOccurs="0">
            <xs:annotation>
                <xs:documentation>
                    Will be required in future...
                </xs:documentation>
            </xs:annotation>
        </xs:element>
        <xs:element name="ComponentPrice" type="finance:ComponentListType" minOccurs="0">
            <xs:annotation>
                <xs:documentation>
                    Will be required in future...
                </xs:documentation>
            </xs:annotation>
        </xs:element>
    </xs:sequence>
</xs:complexType>
<xs:element name="PriceStructure" type="finance:PriceStructureType"/>
<!--End Definition: Complex Type: PriceStructureType-->

v5.xsd

    <!--Begin Definition: Complex Type: PriceStructureType-->
<xs:complexType name="PriceStructureType">
    <xs:sequence>
        <xs:element name="PriceModel" type="xs:string" minOccurs="0">
            <xs:annotation>
                <xs:documentation>
                    Will be required in future...
                    TODO: Add valid items...
                </xs:documentation>
            </xs:annotation>
        </xs:element>
        <xs:element ref="finance:SourceSystemPrice" minOccurs="0">
            <xs:annotation>
                <xs:documentation>
                    Will be required in future...
                </xs:documentation>
            </xs:annotation>
        </xs:element>
        <xs:element name="ComponentPrice" type="finance:ComponentListType" minOccurs="0">
            <xs:annotation>
                <xs:documentation>
                    Will be required in future...
                </xs:documentation>
            </xs:annotation>
        </xs:element>
    </xs:sequence>
</xs:complexType>
<xs:element name="PriceStructure" type="finance:PriceStructureType"/>
<!--End Definition: Complex Type: PriceStructureType-->

common.xsd

xmlns:finance="urn:financetypes:defn:v4"
xmlns:finance_v5="urn:financetypes:defn:v5"
<xs:import namespace="urn:financetypes:defn:v4" schemaLocation="financetypes.v4.xsd"/>
<xs:import namespace="urn:financetypes:defn:v5" schemaLocation="financetypes.v5.xsd"/>

<xs:choice>
            <xs:element ref="finance:PriceStructure" minOccurs="0">
                <xs:annotation>
                    <xs:documentation xml:lang="en">
                        Price Structure itemizes the price components of this OrderLine.  Pricing
                        is a integral part of the Sales Order record.
                        This should be a required element for a sales order, but it is marked
                        as optional for backward compatibility. 
                    </xs:documentation>
                </xs:annotation>
            </xs:element>
            <xs:element ref="finance_v5:PriceStructure" minOccurs="0">
                <xs:annotation>
                    <xs:documentation xml:lang="en">
                        Price Structure itemizes the price components of this OrderLine.  Pricing
                        is a integral part of the Sales Order record.
                        This should be a required element for a sales order, but it is marked
                        as optional for backward compatibility. 
                    </xs:documentation>
                    <xs:appinfo>
                        <jaxb:property name="PriceStructureV5"/>
                    </xs:appinfo>
                </xs:annotation>
            </xs:element>
        </xs:choice>

Solution

  • You could do this. For a root class containing these 2 elements:

    @XmlRootElement(name = "root")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Root {
    
        @XmlElement(name = "PS", namespace = "financetypes:defn:v4")
        protected financetypes.v4.PSType pS;
        @XmlElement(name = "PS", namespace = "financetypes:defn:v5")
        protected financetypes.v5.PSType pSV5;
    }
    

    You can create you classes with the different versions like this:

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "v4PS", namespace = "financetypes:defn:v4")
    public class PSType {
    
        @XmlValue
        private String value;
    }
    

    and:

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "v5PS", namespace = "financetypes:defn:v5")
    public class PSType {
    
        @XmlValue
        private String value;
    }
    

    For a sample xml as below:

    <root xmlns:v4="financetypes:defn:v4" xmlns:v5="financetypes:defn:v5">
        <v4:PS>version 4</v4:PS>
        <v5:PS>version 5</v5:PS>
    </root>
    

    You will be able to unmarshall properly.

    Update to respond to the comment:

    You use xsd to generate the classes. You did not provide the xsd so I will assume you are not allowed to. I created an xsd to generate the classes you show in your question. The namespace.xsd looks like this:

    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:v4="financetypes:defn:v4" xmlns:v5="financetypes:defn:v5">
    
        <xs:element name="root">
            <xs:complexType>
                <xs:all>
                    <xs:element name="v4PSType" type="PSTypev4" />
                    <xs:element name="v5PSType" type="PSTypev5" />
                </xs:all>
            </xs:complexType>
        </xs:element>
    
        <xs:complexType name="PSTypev4">
            <xs:simpleContent>
                <xs:extension base="xs:string" />
            </xs:simpleContent>
        </xs:complexType>
    
        <xs:complexType name="PSTypev5">
                <xs:simpleContent>
                    <xs:extension base="xs:string" />
                </xs:simpleContent>
        </xs:complexType>
    
    </xs:schema>
    

    Then your bindings would be:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
                   xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
                   xmlns:xs="http://www.w3.org/2001/XMLSchema"
                   xmlns:annox="http://annox.dev.java.net"
                   jaxb:extensionBindingPrefixes="xjc">
    
        <jaxb:bindings schemaLocation="../xsd/namespaces.xsd">
            <jaxb:bindings node="//xs:complexType[@name='PSTypev4']">
                <annox:annotate target = "class">
                    <annox:annotate annox:class="javax.xml.bind.annotation.XmlType" name="v4PSType" namespace="financetypes:defn:v4" />
                </annox:annotate>
            </jaxb:bindings>
    
            <jaxb:bindings node="//xs:complexType[@name='PSTypev5']">
                <annox:annotate target = "class">
                    <annox:annotate annox:class="javax.xml.bind.annotation.XmlType" name="v5PSType" namespace="financetypes:defn:v5" />
                </annox:annotate>
            </jaxb:bindings>
    
            <jaxb:bindings node="//xs:element[@name='root']//xs:complexType//xs:all//xs:element[@name='v5PSType']">
                <annox:annotate target = "field">
                    <annox:annotate annox:class="javax.xml.bind.annotation.XmlElement" name="PS" namespace="financetypes:defn:v5" />
                </annox:annotate>
            </jaxb:bindings>
    
            <jaxb:bindings node="//xs:element[@name='root']//xs:complexType//xs:all//xs:element[@name='v4PSType']">
                <annox:annotate target = "field">
                    <annox:annotate annox:class="javax.xml.bind.annotation.XmlElement" name="PS" namespace="financetypes:defn:v4" />
                </annox:annotate>
            </jaxb:bindings>
        </jaxb:bindings>
    </jaxb:bindings>
    

    And then you would end up with classes as those in my answer.