I have a XSD which is not self created but received from another party. So I can't change this XSD because I must ensure comptibility with the other party.
Using XJC 2.2 and JAXB 2.2 using simple binding mode I want to create an root element with inside an empty hello element. But when marshalled I get a lot of extra namespace crap. Which to me looks unneeded. (It works though, but it's more data to send ect...)
XSD Rootelement:
<element name="epp">
<complexType>
<choice>
<element name="greeting" type="epp:greetingType" />
<element name="hello" />
<element name="command" type="epp:commandType" />
<element name="response" type="epp:responseType" />
<element name="extension" type="epp:extAnyType" />
</choice>
</complexType>
</element>
Java code:
Epp epp = new Epp();
epp.setHello("");
Marshalled Result:
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<hello xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string"></hello>
</epp>
Preferred Result:
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<hello />
</epp>
Or:
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<hello></hello>
</epp>
Is there any way to make this possible, preferably without changing XSD or manually change the XJC compiled classes?
The problem is as follows: the schema doesn't define a type for element hello
. As a result, XJC generates a field with type Object
. That means that JAXB must detect, during marshalling, what kind of object we're dealing with. I'm not certain about the details, but I suppose it'll check the runtime type and then deals with it accordingly. Since String
- which is what you actually put into the hello
field - has a direct binding to a schema type (namely xs:string
) JAXB is gonna go with that. So far, so good.
But JAXB is trying to generate XML that'll be useful for unmarshalling as well. Since the schema didn't specify a type and the hello
field is an Object, trying to unmarshal from XML would leave JAXB guessing as to what it should actually turn the content into. One way to tell it how is to specify the type in the XML element, using the xsi:type
attribute. This attribute falls within the xsi
-bound namespace, so that prefix must be declared and bound. That's what happens with xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
. But that's not all... The declared xsi:type
makes use of a type in the XML Schema namespace, bound to prefix xs
which means THAT must be declared as well! Hence the xmlns:xs="http://www.w3.org/2001/XMLSchema"
.
The result: a mess of namespace declarations just to tell whoever uses the XML that it does, in fact, contain a string. This could be solved by adding string as the type for the hello element in the schema, but that's not an option for you.
Fortunately, you're not totally out of luck. You can customize bindings using an external bindings file. Details can be found here: http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html
Normally, this bindings file ought to do the trick:
<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
version="2.1">
<!-- Bindings for the general schema -->
<bindings schemaLocation="test.xsd" node="/xs:schema">
<bindings node="xs:element[@name='epp']">
<bindings node=".//xs:element[@name='hello']">
<javaType name="java.lang.String" />
</bindings>
</bindings>
</bindings>
</bindings>
... but when I try xjc with this I'm getting the error the compiler was unable to honor this javaType customization
. It does work when I specify some standard schema type (like string or int) on the hello element in the schema, but again didn't work when I actually tried to supply parse and print methods for the conversion, so I'm gonna have to assume this is a bug in xjc that occurs when no type is specified in the schema.
I'm hoping someone else can give advice regarding the bindings issue. Otherwise, the only option I see is sending your schema through some XSLT transform prior to unleashing XJC on it to set every non-typed element to string by default.
When the schema doesn't specify a type for an element, the default type is xs:anyType
, which is the root of the XML Schema type hierarchy (all simple and complex types are subtypes of anyType
).
When JAXB encounters an anyType
element it will bind it to a property of type Object
. The value you put into this property can be
null
, meaning omit the elementxsi:type
added to indicate what the original type was, ororg.w3c.dom.Element
representing the actual XML to use.So try this:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().newDocument();
epp.setHello(doc.createElementNS("urn:ietf:params:xml:ns:epp-1.0", "hello"));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With