I have a schema that defines the following type:
<xsd:complexType name="Payload">
<xsd:sequence>
<xsd:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
</xsd:sequence>
</xsd:complexType>
And that creates an object like so:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Payload", propOrder = {
"any"
})
public class Payload {
@XmlAnyElement(lax = true)
protected List<Object> any;
}
Now I try adding another generated JAXB object to that Payload doing something like this:
Class payloadClass = ...;
JAXBContext context = JAXBContext.newInstance( WrapperRequest.class, payloadClass);
...
marshaller.marshal( wrappedRequest );
But I get a terrible exception that looks like it'll never work so I decide to serialize the payload object to XML first then add that as a string in the payload.
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance( sdoRequest.getClass() );
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(new JAXBElement(new QName("uri", sdoRequest.getClass().getSimpleName()), sdoRequest.getClass(), sdoRequest), writer);
payload.getAny().add( writer.toString() );
And this blows up with an exception saying "java.lang.String" does not contain an @XmlRootElement.
So how will the use of xs:any ever work with JAXB? Nothing seems to want to work because JAXB turns the Payload into Object, and it won't serialize just anything in Object. This is all inside Axis2 as well so it's been very challenging to get to this point.
Below I will demonstrate JAXB (JSR-222) and any
with an example:
Payload
The any
property is annotated with @XmlAnyElement(lax=true)
. This means that for that property if an element is associated with a class via @XmlRootElement
or @XmlElementDecl
then an instance of the corresponding object will be used to populate the property if not the element will be set as an instance of org.w3c.dom.Element
.
package forum13941747;
import java.util.List;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Payload", propOrder = {
"any"
})
public class Payload {
@XmlAnyElement(lax = true)
protected List<Object> any;
}
Foo
Below is an example of a class annotated with @XmlRootElement
.
package forum13941747;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Foo {
}
Bar
Below is an example of a class without the @XmlRootElement
annotation. In this use case we will leverage the @XmlElementDecl
annotation on a factory class (usually called ObjectFactory
) annotated with @XmlRegistry
.
package forum13941747;
public class Bar {
}
ObjectFactory
Below is an example of specifying an @XmlElementDecl
annotation for the Bar
class.
package forum13941747;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
@XmlRegistry
public class ObjectFactory {
@XmlElementDecl(name="bar")
public JAXBElement<Bar> createBar(Bar bar) {
return new JAXBElement<Bar>(new QName("bar"), Bar.class, bar);
}
}
input.xml
Below is the input document we'll use for this example. There are 3 elements that correspond to the any
property. The first corresponds to the @XmlRootElement
annotation on the Foo
class. The second corresponds to the @XmlElementDecl
annotation for the Bar
class and the third does not correspond to any of the domain classes.
<?xml version="1.0" encoding="UTF-8"?>
<payload>
<foo/>
<bar/>
<other/>
</payload>
Demo
In the demo code below we will unmarshal the input document, then output the classes of the objects in the resulting any
property and then marshal the payload
object back to XML.
package forum13941747;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Payload.class, Foo.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum13941747/input.xml");
Payload payload = (Payload) unmarshaller.unmarshal(xml);
for(Object o : payload.any) {
System.out.println(o.getClass());
}
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(payload, System.out);
}
}
Output
Below is the output from running the demo code. Note the classes corresponding to the objects in the any
property. The foo
element became an instance of the Foo
class. The bar
element became an instance of JAXBElement
that holds an instance of Bar
. The other
element became an instance of org.w3c.dom.Element
.
class forum13941747.Foo
class javax.xml.bind.JAXBElement
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<payload>
<foo/>
<bar/>
<other/>
</payload>