The Source for Java Technology Collaboration
User: Password:



Kohsuke Kawaguchi

Kohsuke Kawaguchi's Blog

101 ways to marshal objects with JAXB

Posted by kohsuke on October 13, 2005 at 05:13 PM | Comments (13)

Different Output Media

The most basic notion of the marshalling is to take a JAXB-bound object that has @XmlRootElement, and write it out as a whole XML document. So perhaps you have a class like this:

@XmlRootElement
class Point {
  @XmlElement
  public int x;
  @XmlElement
  public int y;
  Point(...) { ... }
}

Then you can do:

marshaller.marshal( new Point(1,3), System.out );

.. and so on. There're seven Marshaller.marshal methods that takes different output media as the second parameter. If you are writing to a file, a socket, or memory, then you should use the version that takes OutputStream. Unless you change the target encoding to something else (default is UTF-8), there's a special marshaller codepath for OutputStream, which makes it run really fast. You also don't have to use BufferedOutputStream, since the JAXB RI does the adequate buffering.

You can also write to Writer, but in this case you'll be responsible for encoding characters, so in general you need to be careful. If you want to marshal XML into an encoding other than UTF-8, it's best to use the JAXB_ENCODING property and then write to OutputStream, as it escapes characters to things like ᠤ correctly.

The next medium we support is W3C DOM. This is bit unintuitive, but you'll do it like this:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().newDocument();

marshaller.marshal( new Point(1,3), doc );

And after the method invocation you get a complete DOM tree that represents the marshalled document.

The other versions of the marshal methods are there to write XML documents in terms of other XML APIs, such as SAX and StAX. The version that takes ContentHandler is useful when you need a custom formatting needs (like you want each attribute to be in new line, etc), but otherwise they are not very interesting if you are writing a whole document.

Marshalling into a subtree

Another common use of JAXB is where you are writing a bigger document, and you use JAXB to generate part(s) of it. The JAX-WS RI is the prime example. It produces a SOAP message, and JAXB 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:

System.out.println("<envelope>");
marshaller.marshal( object, System.out );
System.out.println("</envelope>");

Like I mentioned, this is probably the fastest. 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, JAXB 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, JAXB 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.

Marshalling a non-element

Another common use case is where you have an object that doesn't have @XmlRootElement on it. JAXB allows you to marshal it like this:

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.

marshaller.marshal( new JAXBElement(
  new QName("","rootTag"),String.class,"foo bar"));

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 JAXB world.

Connecting to other XML APIs

Because of the Source and Result support, JAXB 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:

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 JAXB object to be used as a "source" of XML. Many XML APIs take Source as an input, and now JAXB object can be passed to them directly.

For example, you can marshal a JAXB object and unmarshal it into another JAXBContext like this:

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.

Let's see here... did I miss anything?


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Good move, coupling to Source and Result. I didn't understand the significance of the JAXBContext? Basically, I think it is a mistake to have a tight coupling between objects and XML tags, because (at least in the scenarios I work) the objects tend to map to different XML tags at different points/processes, so JAXB has, IMO, previously had "mistake" written all over it. But now I see more the possibility of using JAXB to easily output an "internal format" xml and transform it to the real XML with XSLT.

    Posted by: tobega on October 14, 2005 at 02:51 AM

  • When you tell JAXB a set of classes, it does a lot of preparation (like composing marshaller/unmarshaller code, or doing bytecode generation.)

    JAXBContext represents all the work done at this stage.

    Posted by: kohsuke on October 14, 2005 at 10:02 AM

  • Thank you for the very useful article. I have a question that perhaps you can answer. I have written a Provider style web service and I am trying to marshal a JAX-B class back into the SOAP output body (Source). However when I do I get the following exception: javax.xml.bind.MarshalException - with linked exception: [com.sun.istack.SAXException2: unable to marshal type "org.duke.AddNumbersResponse" as an element because it is missing an @XmlRootElement annotation] at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:281) at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:216) at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:77) This is despite having created the Marshaller like this (result is the instance I would like to marshal): Marshaller m = jc.createMarshaller(); m.setProperty( Marshaller.JAXB_FRAGMENT, Boolean.TRUE ); m.setProperty( Marshaller.JAXB_ENCODING, "UTF-8" ); m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); StringWriter writer = new StringWriter(); m.marshal( result, writer ); Source resultSource = new StreamSource( new ByteArrayInputStream( writer.getBuffer().toString().getBytes( "UTF-8") ) ); What am I missing? I am using JAXWS_SI_20060410.jar. Thanks, Dan

    Posted by: dselman on April 12, 2006 at 08:29 AM

  • Please consider re-posting your question to http://forums.java.net/jive/forum.jspa?forumID=46

    thanks!

    Posted by: kohsuke on April 12, 2006 at 10:05 AM

  • Excellent! I have been wondering how to unmarshall non-element data (like ArrayList's)

    I have a somewhat trickier marshalling/unmarshalling howto problem. Trying to marshal/unmarshal XML into a mix of model classes & dynamic property-bag objects and I am not sure if I can get JAXB to do it.

    The XML schema for Property-bag objects is defined by their metadata (rather than their java-class attributes)

    http://forums.java.net/jive/thread.jspa?threadID=16615

    Any advice you can give me would be really appreciated :-)

    -Nick

    Posted by: nickminutello on July 03, 2006 at 11:25 PM

  • I tried your example with an java.awt.Point with the jaxb installation given in Java 6 beta 2:
    JAXBContext context = JAXBContext.newInstance( Point.class );
    Marshaller m = context.createMarshaller();
    m.marshal( new JAXBElement( new QName( "", "rootTag" ), Point.class,
                                  new Point( 1, 2 ) ), System.out );
    
    The result is a large recursive stacktrace with

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?><rootTag xmlns:ns2="... in thread "main" java.lang.StackOverflowError
    at com.sun.xml.bind.v2.runtime.LeafBeanInfoImpl.serializeURIs(LeafBeanInfoImpl.java:118)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:586)
    at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:114)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:293)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:594)
    at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:114)
     

    ... The second example with 'String.class,"foo bar"' works well. Christian

    Posted by: ullenboom on July 07, 2006 at 09:59 AM

  • I don't suppose there is a way to modify that last comment? It killed the formatting/readability of this informative blog entry with that extremely long XML string (at least on firefox).

    Posted by: grizel on October 19, 2006 at 02:04 PM

  • Thanks. I edited the posting so that it doesn't stretch the width of the window.

    Folks, please don't use blog comment entries for support questions. Use the JAXB forum on java.net for that.

    Posted by: kohsuke on October 23, 2006 at 10:40 PM

  • I'm wondering if it is possible to force the writing of an xsi:type info for the marshalling of a non element. I found no way to do this, and I need it to get a rude validating jaxws webservice running.

    Posted by: breadfan_de on November 17, 2006 at 06:28 AM

  • I continue to encourage people to post support questsions to the forum.

    Posted by: kohsuke on November 17, 2006 at 09:06 AM

  • How to unmarshal xml fragment, JAXB package provides example to do partial unmarshal, it is a schema based example, when I tried to modify for JAXB annotated class without schema, I can not make it work. Is there example somewhere, Thanks

    Posted by: dingwen_ca on May 09, 2007 at 09:32 AM

  • When I marshall a Java object tree "ns2" gets appended to the root element. Is there a way to avoid it?

    Posted by: altijori on November 19, 2007 at 03:46 PM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds