August 23, 2011

JAXB and Java IO (Files, Streams, Readers, and Writers)

JAXB supports many different XML sources and targets.  In this post I'll give an overview of how JAXB can be used with those found in the java.io package: File, InputStream, OutputStream, Reader, Writer and their subclasses.


Java Model

The following domain model will be used for this post.

package blog.jaxb.io;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlType(propOrder = {"street", "city"})
public class Address {

    private String street;
    private String city;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

}

Marshalling an Object to XML

The marshal operation is used to convert objects to XML.  In addition to producing the XML, a JAXB implementation will handle the escaping of special characters: " to &quot;, & to &amp; and < to &lt;. In this post we will focus on marshalling to java.io.OutputStream, and java.io.Writer (and their sub classes).  Below are examples of the marshal operation:

System.out
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(address, System.out);

OutputStream
A JAXB implementation will not close the OutputStream, this is something that you must do in your application code:
Marshaller marshaller = jaxbContext.createMarshaller();
OutputStream outputStream = new FileOutputStream("output.xml");
try {
    marshaller.marshal(address, outputStream);
} finally {
    outputStream.close();
}

Writer
A JAXB implementation will not close the Writer, this is something that you must do in your application code:
Marshaller marshaller = jaxbContext.createMarshaller();
Writer writer = new FileWriter("output.xml");
try {
    marshaller.marshal(address, writer);
} finally {
    writer.close();
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><address><street>1 A Street</street><city>Any Town</city></address>

Writing into a Document (JAXB_FRAGMENT)

By default JAXB will include the XML header.  If you want to marshal contents into an XML stream that is also being populated from other sources you can tell JAXB to omit the header by setting the Marshaller.JAXB_FRAGMENT property to true on the Marshaller.  This is a good example of why a JAXB implementation does not close streams.
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
System.out.print("<ROOT>");
marshaller.marshal(address, System.out);
System.out.print("</ROOT>");
Output
<ROOT><address><street>1 A Street</street><city>Any Town</city></address></ROOT>

Formatting the Result (JAXB_FORMATTED_OUTPUT)

By default JAXB will not format the XML document.  This is done to save space, and not introduce any whitespace that may accidentally be interpreted as being significant.  To have JAXB format the output simply set the Marshaller.JAXB_FORMATTED_OUTPUT property to true on the Marshaller.
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(address, System.out);
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<address>
    <street>1 A Street</street>
    <city>Any Town</city>
</address>

Specifying the Encoding (JAXB_ENCODING)

JAXB defaults the encoding to UTF-8.  You can specify an alternate encoding by setting the Marshaller.JAXB_Encoding property on the Marshaller.
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-1");
marshaller.marshal(address, System.out);
If you are marshalling to a Writer, you must ensure that you create the writer with the proper encoding type:
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-1");
OutputStreamWriter writer = new OutputStreamWriter(System.out, "ISO-8859-1");
marshaller.marshal(address, writer);
Output
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<address>
    <street>1 A Street</street>
    <city>Any Town</city>
</address>

noNamespaceSchemaLocation (JAXB_NO_NAMESPACE_SCHEMA_LOCATION)

We can specify the location of the XML schema corresponding to this document by setting the Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION property on the Marshaller.
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "address.xsd");
marshaller.marshal(address, System.out);
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<address 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:noNamespaceSchemaLocation="address.xsd">
    <street>1 A Street</street>
    <city>Any Town</city>
</address>


schemaLocation (JAXB_SCHEMA_LOCATION)

If our document is namespace qualified then we can specify the schemaLocation by setting the Marshaller.JAXB_SCHEMA_LOCATION property on the Marshaller.
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.example.com/address address.xsd");
marshaller.marshal(address, System.out);
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<address 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.example.com/address address.xsd">
    <street>1 A Street</street>
    <city>Any Town</city>
</address>

Extension Properties

JAXB implementations may offer their own properties to offer capabilities beyond the specification.  EclipseLink JAXB (MOXy) for example offers a property that enables JSON binding.
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media.type", "application/json");
marshaller.marshal(address, System.out);
Output
{"address" : {
   "street" : "1 A Street",
   "city" : "Any Town"}}

Unmarshalling an Object from XML

The unmarshal operation is used to convert XML to objects.  In this post we will focus on unmarshalling from java.io.File, java.io.InputStream, and java.io.Reader (and their sub classes).  Below are examples of an unmarshal operation:

File
Unarshaller unmarshaller = jaxbContext.createUnmarshaller();
File file = new File("input.xml");
Address address = (Address) unmarshaller.unmarshal(file);

InputStream
A JAXB implementation will not close the InputStream, this is something that you must do in your application code:
Unmarshaller unmarshaller = jc.createUnmarshaller();
InputStream inputStream = new FileInputStream("input.xml");
Address address;
try {
    address = (Address) unmarshaller.unmarshal(inputStream);
} finally  {
    inputStream.close();
}

Reader
A JAXB implementation will not close the Reader, this is something that you must do in your application code: 
Unmarshaller unmarshaller = jc.createUnmarshaller();
Reader reader = new FileReader("input.xml");
Address address;
try {
    address = (Address) unmarshaller.unmarshal(reader);
} finally  {
    reader.close();
}

Handling XML Encodings

File
When unmarshalling from a File, there is nothing special you need to do to handle the encoding of the XML document.
Unarshaller unmarshaller = jaxbContext.createUnmarshaller();
File file = new File("input.xml");
Address address = (Address) unmarshaller.unmarshal(file);

InputStream
When unmarshalling from an InputStream, there is nothing special you need to do to handle the encoding of the XML document.:
Unmarshaller unmarshaller = jc.createUnmarshaller();
InputStream inputStream = new FileInputStream("input.xml");
Address address;
try {
    Address address = (Address) unmarshaller.unmarshal(inputStream);
} finally  {
    inputStream.close();
}

Reader
When unmarshalling from a Reader you need to be aware of the XML encoding and configure the Reader accordingly: 
Unmarshaller unmarshaller = jc.createUnmarshaller();
InputStream inputStream = new FileInputStream("input.xml");
Reader reader = new InputStreamReader(inputStream, "UTF-16");
try {
    Address address = (Address) unmarshaller.unmarshal(reader);
} finally  {
    reader.close();
}

Specifying the Type to Unmarshal

If the root element of the XML document being unmarshalled does not match the one that you have associated with your class, then you can use one of the unmarshal methods that takes a Class parameter.  When unmarshalling from a File, InputStream, or Reader you need to wrap your input in a javax.xml.transform.stream.StreamSource:
InputStream inputStream = new FileInputStream("src/blog/jaxb/io/input.xml");
Source source = new StreamSource(inputStream);
try {
    Address address = (Address) unmarshaller.unmarshal(source, Address.class).getValue();
} finally  {
    inputStream.close();
}

Extension Properties

The JAXB specification does not define any Unmarshaller properties, however individual implementations may.  EclipseLink JAXB (MOXy) for example offers a property that enables JSON binding.
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setProperty("eclipselink.media.type", "application/json");
File file = new File("input.json");
Address address = (Address) unmarshaller.unmarshal(file);

Further Reading

If you enjoyed this post, then you may also be interested in:

9 comments:

  1. An absolutely super post. Well done.

    ReplyDelete
  2. Thank you for this post, very informative. I am completely new to JAXB and SAAJ. I am wondering if you could give me some pointers or link me to some tutorial regarding following:

    I want to build XML from java object using JAXB, then use it to populate SOAPMessage. After talking to webservice, convert SOAP response back into java object using JAXB.

    Could you please help me regarding this, thank you?

    ReplyDelete
  3. Hi Muhammad,

    JAXB is the default binding layer for JAX-WS, so when interacting with SOAP Web Services a lot of the message handling is handled for you. You may find the following example useful to get you started:
    - EclipseLink MOXy is the JAXB Provider in WebLogic Server 12c

    -Blaise

    ReplyDelete
  4. hi blaise,

    I am very new to JAXB. Sorry if my question is too novice.I am having a XML file of the following format:





    <\file>




    <\file>




    <\file>
    <\XYZ>


    here number of fields may change per file but the attributes will be four only.

    I am able to read the file using saxParser with fields but when i am trying to read through the attributes i am getting confused. What i want is to have a XML to java objects mapping(unmarshalling) using JAXB.

    Can anyone give me a code help for the given requirement.

    ReplyDelete
    Replies
    1. Hi,

      I believe due to the limitations of the comment area a portion of your XML document has been lost. The following example should help you get going with JAXB:
      - http://wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted

      If you need further help you can use the "Contact Me" link at the top of the page.

      -Blaise

      Delete
  5. Hi Blaise,

    Is there any sort of MOXy annotation equivalent of setting the schema location like this:
    marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.example.com/address address.xsd")

    I would prefer it if I could set the schema location (as it needs to be output to the XML data) in the package.java if possible since this would make the code more portable rather than needing to make the call to the Marshaller.setProperty at runtime.

    Thanks,
    Andrew

    ReplyDelete
    Replies
    1. Hi Andrew,

      There isn't a MOXy or JAXB annotation that does what you describe. Would you mind entering an enhancement request for this using the following link:
      - https://bugs.eclipse.org/bugs/enter_bug.cgi?product=EclipseLink

      -Blaise

      Delete
  6. Awesome Post and very helpful....Thanks!

    Please help me to remove "encoding="UTF-8" standalone="yes"?" for xml header

    i need only



    ReplyDelete
    Replies
    1. You can remove the header with by setting the following property:
      marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);

      Here is a link to an answer I gave on Stack Overflow that explains how to set a custom header:
      - http://stackoverflow.com/questions/18451870/altering-the-xml-header-produced-by-the-jaxb-marshaller/18452018#18452018

      Delete

Note: Only a member of this blog may post a comment.