2

I wish to represent the following schema using MOXy

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <xsd:complexType name="container5">
      <xsd:sequence>
         <xsd:element name="A" type="xsd:string"/>
         <xsd:sequence minOccurs="0">
             <xsd:element name="innerMarker" type="xsd:string" />
             <xsd:element name="O" type="xsd:string"/>
             <xsd:element name="P" type="xsd:string"/>
         </xsd:sequence>
         <xsd:element name="postInnerMarker" type="xsd:string"/>
         <xsd:element name="O" type="xsd:string"/>
         <xsd:element name="P" type="xsd:string"/>
         <xsd:element name="Z" type="xsd:string"/>
      </xsd:sequence>
   </xsd:complexType>
   <xsd:element name="Container" type="container5"/>
</xsd:schema>

Because the inner sequence is optional I represent it using the following. I have to come up with some form of XPath which says that the 'O' in the Container5OptionalInner should only be set if the innerMarker element has been found. I duck out of specifying the Container5 'O' element as being after its predecessor as I wanted to test that last() seemed to work when marshalling an object with no Container5OptionalInner.

package uk.co.his.test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlAccessorType(XmlAccessType.NONE)
@XmlType(propOrder={"a", "optionalInner", "postInnerMarker", "o", "p", "z"})
@XmlRootElement(name = "Container")
public class Container5 {
    @XmlElement(name="A", required=true)
    public String a;

    @XmlPath(".")
    @XmlElement(required=false)
    public Container5OptionalInner optionalInner;


    @XmlElement(required=true, name="postInnerMarker")
    public String postInnerMarker;


    @XmlPath("O[last()]/text()")
    @XmlElement(required=true)
    public String o;

    @XmlPath("P[last()]/text()")
    @XmlElement(required=true)
    public String p;

    @XmlElement(name="Z", required=true)
    public String z;

    @Override
    public String toString() {
        return "Container5 [a=" + a + ", optionalInner=" + optionalInner + ", o=" + o + ", p=" + p + ", z=" + z + "]";
    }
}

@XmlAccessorType(XmlAccessType.NONE)
@XmlType(propOrder= {"innerMarker", "o", "p"})
public class Container5OptionalInner {

    @XmlElement(name="innerMarker", required=true)
    public String innerMarker;

    @XmlPath("O[preceding-sibling::innerMarker]/text()")
    @XmlElement(name="O", required=true)
    public String o;

    @XmlPath("P[preceding-sibling::innerMarker]/text()")
    @XmlElement(name="P", required=true)
    public String p;

    @Override
    public String toString() {
        return "Container5InnerChoice [marker=" + innerMarker + ", o=" + o + ", p=" + p + "]";
    }
}

If I run the following test

@Test
    public void test2() throws JAXBException
    {
        System.setProperty("javax.xml.bind.context.factory", "org.eclipse.persistence.jaxb.JAXBContextFactory");
        final File etXml = new File("target/Container5.xml");
        Container5 et = new Container5();
        et.a = "A";
        et.optionalInner = new Container5OptionalInner();
        et.optionalInner.innerMarker = "optionalInner";
        et.optionalInner.o = "O";
        et.optionalInner.p = "P";
        et.postInnerMarker = "postInnerMarker";
        et.o = "O2";
        et.p = "P2";
        et.z = "Z";

        JAXBContext jbc =  JAXBContext.newInstance(Container5.class);
        System.out.println("jaxbContext is=" +jbc.getClass().getName());
        Marshaller m = jbc.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        m.marshal(et, etXml);

        Unmarshaller u = jbc.createUnmarshaller();
        Container5 et2 = (Container5) u.unmarshal(etXml);
        m.marshal(et, System.out);
        m.marshal(et2, System.out);

        System.out.println(et);
        System.out.println(et2);
    }

I get the following NPE, which seems to be down to the fact that the parsing of the @XmlPath does not set the localname. For example the function public void setXPath(String xpathString) in org.eclipse.persistence.internal.oxm.XPathFragment simply returns when I finds an axis in the XPath predicate;

 // handle case:  ancestor::*/jaxb:class/@name
            if (xpath.indexOf("::") != -1) {
                setShouldExecuteSelectNodes(true);
                return;
            }

where as in simpler cases such as @XmlPath("O[last()]/text()") it always calls setupNamespaceInformation as thus sets the localName whose null value seems to be the cause of the NPE.

The NPE:

javax.xml.bind.MarshalException
 - with linked exception:
[javax.xml.bind.MarshalException
 - with linked exception:
[java.lang.NullPointerException]]
    at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:522)
    at uk.co.his.test.genpi.jaxbgen.Tests4.test2(Tests4.java:76)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    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 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:86)
    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)
Caused by: javax.xml.bind.MarshalException
 - with linked exception:
[java.lang.NullPointerException]
    at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:500)
    at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:515)
    ... 24 more
Caused by: java.lang.NullPointerException
    at org.eclipse.persistence.oxm.record.OutputStreamRecord.outputStreamWrite(OutputStreamRecord.java:581)
    at org.eclipse.persistence.oxm.record.OutputStreamRecord.outputStreamWrite(OutputStreamRecord.java:568)
    at org.eclipse.persistence.oxm.record.FormattedOutputStreamRecord.openStartElement(FormattedOutputStreamRecord.java:120)
    at org.eclipse.persistence.oxm.record.MarshalRecord.openStartGroupingElements(MarshalRecord.java:482)
    at org.eclipse.persistence.internal.oxm.XMLDirectMappingNodeValue.marshalSingleValue(XMLDirectMappingNodeValue.java:86)
    at org.eclipse.persistence.internal.oxm.XMLDirectMappingNodeValue.marshal(XMLDirectMappingNodeValue.java:65)
    at org.eclipse.persistence.internal.oxm.NodeValue.marshal(NodeValue.java:102)
    at org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext.marshal(ObjectMarshalContext.java:59)
    at org.eclipse.persistence.internal.oxm.XPathNode.marshal(XPathNode.java:448)
    at org.eclipse.persistence.internal.oxm.XPathNode.marshal(XPathNode.java:423)
    at org.eclipse.persistence.internal.oxm.XPathObjectBuilder.buildRow(XPathObjectBuilder.java:243)
    at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:118)
    at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:1)
    at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.marshalSingleValue(XMLCompositeObjectMappingNodeValue.java:260)
    at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.marshal(XMLCompositeObjectMappingNodeValue.java:151)
    at org.eclipse.persistence.internal.oxm.NodeValue.marshal(NodeValue.java:102)
    at org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext.marshal(ObjectMarshalContext.java:59)
    at org.eclipse.persistence.internal.oxm.XPathNode.marshal(XPathNode.java:448)
    at org.eclipse.persistence.internal.oxm.XPathObjectBuilder.buildRow(XPathObjectBuilder.java:243)
    at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:118)
    at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:1)
    at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.java:767)
    at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshalStreamOrWriter(XMLMarshaller.java:1143)
    at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.java:934)
    at org.eclipse.persistence.internal.oxm.XMLMarshaller.marshal(XMLMarshaller.java:878)
    at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:496)
    ... 25 more

Moreover the value is ignored in parsing the instance document;

<?xml version="1.0" encoding="UTF-8"?>
<Container>
   <A>A</A>
   <choiceMarker>choiceMarker</choiceMarker>
   <O>O</O>
   <P>P</P>
   <postChoiceMarker>postChoiceMarker</postChoiceMarker>
   <O>O2</O>
   <P>P2</P>
   <Z>Z</Z>
</Container>

Will cause Container5 to print out;

Container5 [a=A, optionalInner=Container5InnerChoice [marker=null, o=null, p=null], o=O2, p=P2, z=Z]

So it seems that MOXy does a restricted st of XPath ( which might seem logical if one considered the cases when it was using a SAX type streaming parse to unpack an Object ).

I wish to ask 0) Can my example be done ins some other way which MOXy will accept - am I just missing something? 1) Are the set of XPaths which MOXy can bind to resticted to those in the examples O1, ".", is there any definition (beyond the source, which I am still getting to know) of what it does accept? 2) Is the set of valid @XmlPath values depedendent on the intermediate representation of the XML (Full XPath on DOM, restricted on streaming, a bit like the XSLT2 streaming restrictions)?

Question also asked on Eclipselink forum


Should have searched SO first.

The following questions and answers on SO say that axes and more complex XPaths are not supported by MOXy. They also know that more could be done to signal XPaths which will not work, rather than failing silently;

Statement characterising what is and isn't supported

Eclipselink tool for help composing XPaths

In the Update at the bottom of the answer to this question, Blaise Doughan who used to be a key MOXy engineer has a JIRA for adding more error reporting, but it had not been updated when I looked

Community
  • 1
  • 1
JFK
  • 1,527
  • 16
  • 21

0 Answers0