Search code examples
xmlbindingjaxbeclipselinkmoxy

EclipseLink MOXy: How to match XML structure variant to same bean model


XML Variant 1:

<root>
  <elements>
    <element />
  </elements>
</root>

XML Variant 2:

<root>
  <element />
</root>

The bean structure is a class for each of the elements in XML Variant 1, which nest each other as shown.

The desired behavior is for the unmarshaller to create the same beans for Variant 2 as for Variant 1. That means, it should create a Elements class even though it is not existent in the structure.

Here's the bindings I use for Variant 1:

<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="demo">
<java-types>
    <java-type name="Root">
        <xml-root-element name="root"/>
        <java-attributes>
            <xml-element java-attribute="elements" xml-path="elements" type="demo.Elements"/>
        </java-attributes>
    </java-type>
    <java-type name="Elements">
        <java-attributes>
            <xml-element java-attribute="element" xml-path="element" type="demo.Element" container-type="java.util.List"/>
        </java-attributes>
    </java-type>
    <java-type name="Element" />
</java-types>

I tried adapting xml-path="elements" to xml-path="." and thought that may work for Variant 2, but without success. What's the easiest way to accomplish what I want?


Solution

  • You could use multiple mapping files for your use case.

    Mapping File - Variant 1

    <?xml version="1.0"?>
    <xml-bindings
        xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
        package-name="demo"
        xml-accessor-type="FIELD">
        <java-types>
            <java-type name="Root">
                <xml-root-element/>
            </java-type>
        </java-types>
    </xml-bindings>
    

    Mapping File - Variant 2

    <?xml version="1.0"?>
    <xml-bindings
        xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
        package-name="demo"
        xml-accessor-type="FIELD">
        <java-types>
            <java-type name="Root">
                <xml-root-element/>
                <java-attributes>
                    <xml-element java-attribute="elements" xml-path="."/>
                </java-attributes>
            </java-type>
        </java-types>
    </xml-bindings>
    

    Demo

    In the demo code below we will create two different instances of JAXBContext for the same domain model with different metadata.

    package demo;
    
    import java.io.File;
    import java.util.*;
    import javax.xml.bind.*;
    import org.eclipse.persistence.jaxb.JAXBContextProperties;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            // VARIANT #1
            Map<String, Object> properties1 = new HashMap<String, Object>(1);
            properties1.put(JAXBContextProperties.OXM_METADATA_SOURCE, "demo/oxm1.xml");
            JAXBContext jc1 = JAXBContext.newInstance(new Class[] {Root.class}, properties1);
            Unmarshaller unmarshaller1 = jc1.createUnmarshaller();
            File variant1 = new File("src/demo/variant1.xml");
            Root root = (Root) unmarshaller1.unmarshal(variant1);
    
            // VARIANT #2
            Map<String, Object> properties2 = new HashMap<String, Object>(1);
            properties2.put(JAXBContextProperties.OXM_METADATA_SOURCE, "demo/oxm2.xml");
            JAXBContext jc2 = JAXBContext.newInstance(new Class[] {Root.class}, properties2);
            Marshaller marshaller2 = jc2.createMarshaller();
            marshaller2.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller2.marshal(root, System.out);
         }
    
    }