By default, a Jakarta XML Binding marshaller uses random namespace prefixes (such
as ns1
, ns2
, ...) when it needs to declare new
namespace URIs. While this is perfectly valid XML wrt the schema, for
human readability, you might want to change them to something that makes
more sense.
The Eclipse Implementation of JAXB defines NamespacePrefixMapper to allow you to do this. See
the namespace-prefix
sample in the distribution for more
details.
5.2. Element default values and marshalling
Because of a "strange" way element default values in XML Schema
work, people often get confused about its behavior. This section describes
how this works.
When a class has an element property with the default value, and if
a value is null, then the marshaller will not produce the corresponding
element in XML:
Example 72. Element defaults and XML
@XmlRootElement
class Foo {
@XmlElement(defaultValue="value") public String a=null;
}
marshaller.marshal(new Foo(),System.out);
Example 73. Marshalling output from above
This is consistent with the XML Schema spec, where it essentially
states that the element defaults do not kick in when the element is
absent. Attribute default values do not have this problem, so if you can
change the schema, changing it to an attribute is usually a better idea.
Alternatively, depending on your expectation, setting the field to a
default value in Java may achieve what you are looking for.
Example 74. Possible changes
@XmlRootElement
class Foo {
@XmlElement public String a="value";
}
@XmlRootElement
class Bar {
@XmlAttribute public String a;
}
marshaller.marshal(new Foo(),System.out);
marshaller.marshal(new Bar(),System.out);
Example 75. Marshalling output from above
<foo>
<a>value</a>
</foo>
<bar/>
Also, see Element default values and unmarshalling.
5.3. Different ways of marshalling
5.3.2. Marshalling into a subtree
Another common use of Jakarta XML Binding is where you are writing a bigger
document, and you use Jakarta XML Binding to generate part(s) of it. The Eclipse Implementation of XML Web Services is
the prime example. It produces a SOAP message, and Jakarta XML Binding is only used
to produce the body. When you are doing this, you first set
JAXB_FRAGMENT
property on the marshaller. This changes
the behaviors of the marshaller so that it works better in this
situation.
If you are writing to an OutputStream
or
Writer
and generally sending it to someone else, you can
do something like this:
Example 79. Marshalling into a subtree
System.out.println("<envelope>");
marshaller.marshal( object, System.out );
System.out.println("</envelope>");
Like I mentioned, this is probably the fastest, even though
println
isn't very pretty. JAXB_FRAGMENT
prevents the marshaller from producing an XML declaration, so the
above works just fine. The downside of this approach is that if the
ancestor elements declare the namespaces, Jakarta XML Binding won't be able to take
advantage of them.
You can also marshal an object as a subtree of an existing DOM
tree. To do this, you pass the Element
object as the
second parameter, and the marshaller will marshal an object as a child
of this node.
StAX is also very convenient for doing this sort of things. You
can create XMLStreamWriter
, write some stuff, and then
pass that to the marshaller. JAXB_FRAGMENT
prevents the
marshaller from producing startDocument
and
endDocument
token. When doing this sub-tree marshaling to
DOM and StAX, Jakarta XML Binding can take advantage of available in-scope namespace
bindings.
Finally, you can marshal an object as a subtree into
ContentHandler
, but it requires a fair amount of SAX
programming experience, and it goes beyond the scope of this
entry.
5.3.3. Marshalling a non-element
Another common use case is where you have an object that doesn't
have @XmlRootElement
on it. Jakarta XML Binding allows you to marshal it
like this:
Example 80. Marshalling a non-element
marshaller.marshal( new JAXBElement(
new QName("","rootTag"),Point.class,new Point(...)));
This puts the <rootTag>
element as the root element,
followed by the contents of the object, then </rootTag>
. You can
actually use it with a class that has @XmlRootElement
,
and that simply renames the root element name.
At the first glance the second Point.class
parameter may look redundant, but it's actually necessary to determine
if the marshaller will produce (infamous)
@xsi
:type. In this example, both the class and the
instance are Point
, so you won't see
@xsi
:type. But if they are different, you'll see
it.
This can be also used to marshal a simple object, like
String
or an integer.
Marshalling a non-element with
@xsi
:type
But unfortunately it cannot be
used to marshal objects like List
or Map
, as
they aren't handled as the first-class citizen in the Jakarta XML Binding
world.
5.3.4. Connecting to other XML APIs
Because of the Source
and Result
support, Jakarta XML Binding objects can be easily marshalled into other XML APIs
that are not mentioned here. For example, dom4j has
DocumentResult
that extends Result
, so you
can do:
Example 81. Marshalling to dom4j
DocumentResult dr = new DocumentResult();
marshaller.marshal( object, dr );
o = dr.getDocument();
Similar mechanism is available for JDOM and XOM. This conversion
is much more efficient than first marshalling to
ByteArrayOutputStream
and then read it back into these
DOMs. The same mechanism can be used to marshal to FastInfoset or send the
marshaled document to an XSLT engine (TransformerHandler
.)
The other interesting connector is JAXBSource
,
which wraps a marshaller and allows a Jakarta XML Binding object to be used as a
"source" of XML. Many XML APIs take Source
as an input,
and now Jakarta XML Binding object can be passed to them directly.
For example, you can marshal a Jakarta XML Binding object and unmarshal it into
another JAXBContext like this:
Example 82. Loading into a different JAXBContext
JAXBContext context1 = ... ;
JAXBContext context2 = ... ;
context1.createUnmarshaller().unmarshal( new JAXBSource(context2,object) );
This amounts to looking at the same XML by using different
schema, and again this is much more efficient than going through
ByteArrayOutputStream
.
5.4. Interaction between marshalling and DOM
Sometimes you may notice that Jakarta XML Binding is producing XML with seemingly
unnecessary namespace declarations. In this section, we'll discuss the
possible causes and how to resolve this.
5.4.1. Caused by DOM mapping
The #1 cause of extra namespace declarations is due to the DOM
mapping. This mainly happens because of a schema construct that forces
XJC to generate a property with DOM. This includes the use of wildcard
<xs:any/>
(see more about this Mapping of <xs:any />
), as well as xs:anyType
(which can also happen by omission, such as <xs:element
name="foo"/>
, which is interpreted as <xs:element
name="foo" type="xs:anyType" />
.
During unmarshalling, when a subtree of the input XML is
converted into XML, Jakarta XML Binding copies all the in-scope namespace bindings
active at that time to the root of the DOM element. So for example,
given the following Java class and XML, the DOM tree that the
child
field will get will look like the following:
Example 83. Bean with wildcard
@XmlRootElement
class Foo {
@XmlAnyElement
public Element child;
}
Example 84. Instance with subtree matching wildcard
<foo xmlns:a="a" xmlns:b="b" xmlns:c="c">
<subtree xmlns:c="cc">
<data>a:xyz</data>
</subtree>
</foo>
Example 85. DOM tree to be stored in Foo.child
<subtree xmlns:a="a" xmlns:b="b" xmlns:c="cc">
<data>a:xyz</data>
</subtree>
Note that the two namespace declarations are copied over, but
c
is not because it's overridden. Also not that Jakarta XML Binding is
not touching the whitespace in document. This copying of namespace
declarations is necessary to preserve the infoset in the input
document. For example, if the <data>
is a QName, its meaning
would change if Jakarta XML Binding unmarshaller doesn't copy it.
Now, imagine what happens when you marshal this back to XML.
Despite the fact that in this example neither b
nor
c
prefixes are in use, Jakarta XML Binding cannot delete them, because
it doesn't know if those attributes are significant to the application
or not. Therefore, this could end up producing XML with "extra
namespace declarations" like:
Example 86. DOM tree to be stored in Foo.child
<foo>
<subtree xmlns:a="a" xmlns:b="b" xmlns:c="cc">
<data>a:xyz</data>
</subtree>
</foo>
Resolving this problem is not possible in the general case, but
sometimes one of the following strategy works:
Sometimes schema author incorrectly assumes that
<xs:element name="foo"/>
means
<xs:element name="foo" type="xs:string"/>
,
because attribute declarations work somewhat like this. In
such a case, adding explicit type
attribute
avoids the use of DOM, so things will work as expected.
The wildcard processing mode " strict
"
would force a typed binding, and thereby eliminate any DOM
mapping.
You might be able to manulally go into the DOM tree and
remove unnecessary namespace declarations, if your application
knows what are necessary and what are not.