jaxbeclipselinkabstractmoxyoxm

JAXB-Eclipselink: Mapping abstract "getter" to XML


I am using the EclipseLink implementation (2.3) of JAXB to map POJOs to XML and encountering a problem with following usecase:

public abstract class A {

    public abstract Set<X> getX();
    // There is no setter
}


public class B extends A {

    // Set via constructor
    private Set<X> x;

    @Override
    public Set<X> getX();

}

I am defining the mapping itself completely in an external bindings-file, i set class A to be transient like so:

<java-type name="foo.A" xml-transient="true"/>

and for class B:

<java-type name="bar.B" xml-accessor-type="PROPERTY">
    <xml-root-element name="B" />
    <java-attributes>
        <xml-element java-attribute="x" xml-path="..."/>
    </java-attributes>
</java-type>

Now, upon marshalling i am getting the exception: "Duplicate Property named [x] found on class [bar.B]" which in my opinion is coming from the abstract declaration in A, being inherited by B.

Setting the accessor-type for B to FIELD, gets rid of this error, unfortunately this is not an option because i do have an extra property in B to marshal which does not return a field but a calculated value, so i am stuck with PROPERTY (following works: setting accessor-type for B to FIELD and mapping the extra property with an @XmlPath annotation - but i dont want annotations in my code).

Being stuck with accessor-type PROPERTY for class B, my next attempt was:

<java-type name="foo.A" xml-accessor-type="NONE"/>

to prevent the abstract property from being inherited by B, which gets me:

Ignoring attribute [x] on class [bar.B] as no Property was generated for it.

Same is happening using this mapping:

<java-type name="foo.A" xml-accessor-type="PROPERTY">
    <java-attributes>
        <xml-transient java-attribute="x"/>
    </java-attributes>
</java-type>

In both cases property 'x' is ignored.

I have really spent quite some time on this now - i cant imagine that its not possible to get this to work??

My workaround at the moment:

Leaving foo.A to be transient, specifying accessor-type FIELD for bar.B (which gets me property 'x' without problems) and mapping the extra property in B using an annotation in code. But as mentioned before: I would like to solve this completely without annotations - anybody any idea? Blaise? :)

regards,

--qu


Solution

  • Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.

    You appear to have hit a bug. You can track our progress on this issue at the following link. I have provided additional details on this issue below:


    Using Annotations

    If you were going to map this use case with JAXB/MOXy annotations you could set @XmlAccessorType(XmlAccessType.NONE) on the A class and do something like:

    A

    package forum8727402;
    
    import javax.xml.bind.annotation.*;
    
    @XmlAccessorType(XmlAccessType.NONE)
    public abstract class A {
    
        public abstract String getX();
    
    }
    

    B

    package forum8727402;
    
    import javax.xml.bind.annotation.*;    
    import org.eclipse.persistence.oxm.annotations.XmlPath;
    
    @XmlRootElement
    public class B extends A {
    
        @XmlPath("a/b/c/text()")
        private String x;
    
        public B() {
            x = "Hello World";
        }
    
        @Override
        public String getX() {
            return x;
        }
    
        @XmlElement
        public String getCalculatedValue() {
            return "Calculated Value";
        }
    
    }
    

    Demo

    package forum8727402;
    
    import javax.xml.bind.*;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(B.class);
    
            B b = new B();
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(b, System.out);
        }
    
    }
    

    Output

    <?xml version="1.0" encoding="UTF-8"?>
    <b>
       <a>
          <b>
             <c>Hello World</c>
          </b>
       </a>
       <calculatedValue>Calculated Value</calculatedValue>
    </b>
    

    Using MOXy's External Mapping File

    oxm.xml

    Below is a MOXy external mapping file that represents the equivalent of the previously shown annotations:

    <?xml version="1.0" encoding="UTF-8"?>
    <xml-bindings
        xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
        package-name="forum8727402">
        <java-types>
            <java-type name="A" xml-accessor-type="NONE"/>
            <java-type name="B">
                <xml-root-element/>
                <java-attributes>
                    <xml-element java-attribute="x" xml-path="a/b/c/text()"/>
                    <xml-element java-attribute="calculatedValue"/>
                </java-attributes>
            </java-type>
        </java-types>
    </xml-bindings>
    

    Demo

    The code below demonstrates how to reference the mapping file:

    package forum8727402;
    
    import java.util.*;
    import javax.xml.bind.*;    
    import org.eclipse.persistence.jaxb.JAXBContextFactory;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            Map<String, Object> properties = new HashMap<String, Object>(1);
            properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum8727402/oxm.xml");
            JAXBContext jc = JAXBContext.newInstance(new Class[] {A.class, B.class}, properties);
    
            B b = new B();
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(b, System.out);
        }
    
    }
    

    Output

    [EL Warning]: 2012-01-04 14:45:46.366--Ignoring attribute [x] on class [forum8727402.xml.B] as no Property was generated for it.
    <?xml version="1.0" encoding="UTF-8"?>
    <b>
       <calculatedValue>Calculated Value</calculatedValue>
    </b>