This section describes the various ways of marshalling JaxMe objects and how to configure the marshalling process. Note, that this section uses both methods and features, which are specified by JAXB and others, which are proprietary to JaxMe. Explicit remarks will tell you about the latter, so that you are still able to write 100% portable code.
The following methods are specified by JAXB. We'll present each method, followed by a small piece of example code.
Marshaller methods
public void marshal(Object, OutputStream) throws JAXBException
Marshals the object into the given java.io.OutputStream
.
Example 2.1. Marshalling into an OutputStream
public void writeToFile( JAXBContext pContext, Object pObject, String pFileName) throws JAXBException, IOException { java.io.FileOutputStream fos = new FileOutputStream(pFileName); Marshaller marshaller = pContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.marshal(pObject, fos); fos.close(); }
The important thing to note in our case is the use of an encoding.
Depending on the encoding, the Marshaller
will convert
characters (16 bit entities) into bytes (8 bit entities). The encoding may
also have influence on the marshallers decision, which characters to escape
using &#ddd; or not.
public void marshal(Object, Writer) throws JAXBException
Marshals the object into the given java.io.Writer
.
Example 2.2. Marshalling into a Writer
public void writeToFile( JAXBContext pContext, Object pObject, String pFileName) throws JAXBException, IOException { java.io.FileWriter fw = new FileWriter(pFileName); Marshaller marshaller = pContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.marshal(pObject, fw); fw.close(); }
This example is almost equivalent to the previous example with an OutputStream
as the target. In practice, the result may be slightly different anyways, for example, because the
Marshaller
may choose a different set of characters to escape.
Example 2.3. Marshalling into a String
public String asString( JAXBContext pContext, Object pObject) throws JAXBException { java.io.StringWriter sw = new StringWriter(); Marshaller marshaller = pContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.marshal(pObject, sw); return sw.toString(); }
The example shows, how to convert a JaxMe object into a String
. Note
that the use of an encoding still applies, although there occurs no conversion from characters into
bytes. (In the former example using the FileWriter
there has been an implicit
conversion, because any file is in fact a byte stream.) However, the Marshaller
may still choose to escape characters or not.
public void marshal(Object, org.w3c.dom.Node) throws JAXBException
This method is used to convert a JaxMe object into a DOM tree. An instance
of org.w3c.dom.Element
will be created and appended to the given node.
node
must be ready to accept an element as a child.
Suitable nodes are, for example, documents, document fragments, or elements.org.w3c.dom.Document
.Example 2.4.
public Document asDOMDocument( JAXBContext pContext, Object pObject, javax.xml.parsers.DocumentBuilderFactory pFactory) throws JAXBException, javax.xml.parsers.ParserConfigurationException { org.w3c.dom.Document result = pFactory.newDocumentBuild().newDocument(); Marshaller marshaller = pContext.createMarshaller(); marshaller.marshal(pObject, result); return result; }
Note, that no encoding issues arise in this example, because the DOM tree
is an abstract representation of the XML documents, much like JaxMe objects.
However, the same issues are still to be applied, if you serialize the DOM tree
into an OutputStream
or Writer
.
public void marshal(Object, org.xml.sax.ContentHandler) throws JAXBException
This method is by far the most powerfull of all the marshaller methods: A ContentHandler
is in fact just a generic XML processor, implemented as an event handler. In the case of
JaxMe all the other marshalling methods are just thin wrappers around this one: XML serialization
is implemented by the XMLWriter
, which is just an extension of the
ContentHandler
. As an example, we'll demonstrate how to transform a
JaxMe object using an XSL stylesheet, writing the result into an OutputStream
:
Example 2.5. Transformation of a JaxMe object using an XSL stylesheet
public void transformToFile(JAXBContext pContext, Object pObject, TransformerFactory pFactory, String pStylesheetFileName, String pOutputFileName) throws JAXBException, TransformerConfigurationException { javax.xml.transform.Source stylesheetSource = new javax.xml.transform.StreamSource( new java.io.File(pStylesheetFileName)); javax.xml.transform.Result outputResult = new javax.xml.transform.StreamResult( new java.io.File(pOutputFileName)); javax.xml.transform.sax.TransformerHandler handler = ((SAXTransformerFactory) pFactory).newTransformerHandler(stylesheetSource); handler.setResult(outputResult); handler.getTransformer().setOutputProperty("output.encoding", "UTF-8"); Marshaller marshaller = pContext.createMarshaller(); marshaller.marshal(pObject, handler); }
The SAX ContentHandler receives an abstract representation of the JaxMe object. So there's no
need to set the Marshaller
's encoding. This is the same as in the
DOM example.
However, the above example contains in fact two transformations: The first one is the
stylesheet transformation, performed by the transformerHandler
.
But there is another, implicit, transformation, which the
transformerHandler
also performs, because we have choosen an
instance of StreamResult
as the transformers destination. In other
words, its the Transformer
who needs to know the desired encoding and not the
Marshaller
. That's why we set the stylesheet parameter
output.encoding.
public void marshal(Object, javax.xml.transform.Result) throws JAXBException
This marshalling method is a generalization of the methods we've seen so far. An instance
of Result
is an abstraction of the marshalling destination. Concrete
implementations are the StreamResult
(writing to an
OutputStream
or Writer
), the
DOMResult
(creating a DOM tree), and the SAXResult
,
which fires SAX events into a ContentHandler
.
In theory, a Result
can encapsulate arbitrary destinations. In
practice one is restricted to the above standard cases, because the Result
has no useful methods at all. In other words, a new implementation can only be used, if all
users of the Result
know how to deal with it. In our case the users
include JaxMe.
The above limitations are best described, if we use the JaxMe implementation of the method as an example:
Example 2.6. JaxMe's implementation of the Result
destination
public void marshal(Object pObject, Result pResult) throws JAXBException { if (pResult instanceof SAXResult) { ContentHandler ch = ((SAXResult) pResult).getHandler(); if (ch == null) { throw new MarshalException("The SAXResult doesn't have its ContentHandler set."); } marshal(pObject, ch); } else if (pResult instanceof StreamResult) { StreamResult sr = (StreamResult) pResult; Writer w = sr.getWriter(); if (w == null) { OutputStream s = sr.getOutputStream(); if (s == null) { throw new MarshalException( "The StreamResult doesn't have its Writer or OutputStream set."); } marshal(pObject, s); } else { marshal(pObject, w); } } else if (pResult instanceof DOMResult) { Node node = ((DOMResult) pResult).getNode(); if (node == null) { throw new MarshalException("The DOMResult doesn't have its Node set."); } marshal(pObject, node); } else { throw new MarshalException( "Unknown type of Result: " + pResult.getClass().getName() + ", only SAXResult, StreamResult and DOMResult are supported."); } }
In other words, the method is implemented as a thin wrapper around the others we've seen so far. (Funny enough, it would have been possible to go the other way and implement all others as wrappers around this one. However, that would require the presence of an XSLT implementation at runtime and most possibly quite inefficient.
In the previous section we've already seen how to configure the Marshaller
's
encoding by setting a property. In this section we'll present a complete list of all
the properties supported by the JaxMe Marshaller
.
The following properties are part by the JAXB specification. In other words: You are safe to use them with any JAXB compliant implementation, including JaxMe. Whenever possible, we recommend to use only this properties and no others.
JAXB standard Marshaller
properties
Marshaller
.JAXB_ENCODING
As we have already seen, this property specifies the encoding used by the
Marshaller
, if applicable. The encoding is used to determine
marshal(Object, OutputStream)
is used,
how the character is converted into bytesFor example, if we have the encoding ISO-8859-1
(also known as
Latin-1), which is mainly used in the U.S. and western europe (Great
Britain, France, Germany, ...) then the Marshaller
knows, that
no other characters that the character set should be limited to the Unicode points
0..255. In other words, all other characters will be escaped. The Latin-1
characters, however, will be passed through to the Writer
, or
OutputStream
, because they are immediately mapped to the
corresponding bytes.
Marshaller
.JAXB_FORMATTED_OUTPUT
By default, any Marshaller
will create formatted output,
when marshalling to an OutputStream
or Writer
.
In other words, the created output will look roughly like the following:
<Person> <FullName> <FirstName>Jochen</FirstName> <SurName>Wiedmann</SurName> </FullName> </Person>As opposed to unformatted output:
<Person><FullName><FirstName>Jochen</FirstName><SurName>Wiedmann</SurName></FullName></Person>The following example turns off the default handling and creates a String containing unformatted XML:
Example 2.7. Creating unformatted XML
public String asUnformattedString( JAXBContext pContext, Object pObject, String pFileName) throws JAXBException { java.io.StringWriter sw = new StringWriter(); Marshaller marshaller = pContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE); marshaller.marshal(pObject, sw); return sw.toString(); }
JaxMe specific Marshaller
properties
JMControllerImpl
.JAXME_DATATYPE_CONVERTER
The javax.xml.bind.DatatypeConverterInterface
is used by
JAXB implementations to convert various data types into strings
and vice versa. For example, there is a method parseInt(String)
,
converting the given string into a primitive integer. Likewise, there is a reverse
method printInt(int)
, converting the same integer back into a
string. The various "parse" methods are used by the Unmarshaller
and the "print" methods are used by the Marshaller
.
The datatype converter is accessible for two reasons: First of all, you need one
when writing custom parse and print methods (as specified by the
jaxb:property/jaxb:conversion tags in the schema). In that case you
can use a datatype converter by invoking the static methods of the class
DatatypeConverter
, which is internally using a single, global
instance of DatatypeConverterInterface
.
In the special case of JaxMe, however, you are by no
means restricted to the global instance. In contrary, any JaxMe
Marshaller
or Unmarshaller
holds an
internal instance, by default the global instance from
DatatypeConverter
.
The advantage is, that you may customize how documents marshalled by JaxMe may look like. As an example, we'll show how to customize the look of the data type xs:datetime. The XML Schema specification requests, that timestamps are marshalled as yyyy-MM-ddThh:mm:ss, which only few people will like. Additionally the specification requires that timestamps are given in UTC time, not local time.
The specification does so for good reason: Timezone and Locale issues are eliminated. If your XML instances conform to the XML Schema format, it is most likely that you won't have interoperability problems with foreign software. Fact is, however, that not all of us are working in a multinational environment and prefer human readable formats and local time.
Example 2.8. Using a custom datatype converter
// Create a subclass of the JaxMe datatype converter, overriding its parseDateTime() and // printDateTime() methods. public class MyDatatypeConverter extends org.apache.ws.jaxme.impl.DatatypeConverterImpl { public Calendar parseDateTime(String arg0) { DateFormat df = DateFormat.getDateTimeInstance(); Calendar cal = Calendar.getInstance(); try { cal.setTime(df.parse(arg0)); } catch (ParseException e) { if (df instanceof SimpleDateFormat) { SimpleDateFormat sdf = (SimpleDateFormat) df; throw new IllegalArgumentException( "Failed to parse dateTime " + arg0 + " with pattern " + sdf.toPattern()); } else { throw new IllegalArgumentException("Failed to parse dateTime " + arg0); } } return cal; } public String printDateTime(Calendar arg0) { return DateFormat.getDateTimeInstance().format(arg0.getTime()); } } // Convert a JaxMe object into a String, using the above converter public String asString(JAXBContext pContext, Object pObject) throws JAXBException { java.io.StringWriter sw = new StringWriter(); Marshaller marshaller = pContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.setProperty( JMControllerImpl.JAXME_DATATYPE_CONVERTER, new MyDatatypeConverter()); marshaller.marshal(pObject, sw); return sw.toString(); }
JMMarshallerImpl
.JAXME_INDENTATION_SEPARATOR
This property is used in conjunction with formatted output. If formatted output is
turned on, it contains the string which is used as a line terminator. The property is
ignored, if formatted output is turned off or marshalling to a DOM
tree or SAX ContentHander
occurs.
The default property value is Line Feed (ASCII 10, or \n), which is the standard on Linux or Unix. On Windows the standard is Carriage Return, followed by a Line Feed (ASCII 13 and 10, or \r\n). Rumours are, they are even using a single Carriage Return on some Apple Macintosh boxes.
If you want to see the problem live, just take a file with Unix line terminators and open it in the Windows application Notepad. No matter, which version of Windows you choose, it remains the same after 20 years of development: You'll see a text file containing a single line and where you'd expect the lines being splitted, the editor will show you black boxes only. But rumbling doesn't help, we've got to live with it.
The following example shows how to turn on Windows line terminators:
Example 2.9. Turning on Windows line terminators
public String asUnformattedString( JAXBContext pContext, Object pObject, String pFileName) throws JAXBException { java.io.StringWriter sw = new StringWriter(); Marshaller marshaller = pContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.setProperty(JMMarshallerImpl.JAXME_INDENTATION_STRING, "\r\n"); marshaller.marshal(pObject, sw); return sw.toString(); }
JMMarshallerImpl
.JAXME_INDENTATION_STRING
This property is used in conjunction with formatted output. If formatted output is
turned on, it contains the string which is used to indent one element: By default " " (two
blanks). The property is ignored, if formatted output is turned off or when marshalling
to a DOM tree or SAX ContentHandler
occurs.
To demonstrate the effect, we'll show two example documents. First an instance with default indentation:
<Person> <FullName> <FirstName>Jochen</FirstName> <SurName>Wiedmann</SurName> </FullName> </Person>If we set the indentation string to " " (four blanks), we'll get
<Person> <FullName> <FirstName>Jochen</FirstName> <SurName>Wiedmann</SurName> </FullName> </Person>
JMControllerImpl
.JAXME_PRIVATE_
When writing custom code for formatting or parsing element or attribute values,
you will most probably find at any time, that you wish to access data, which isn't
available by default. In most cases you have the data, when invoking the
Marshaller
but not inside the formatter or parser.
A typical example might be, that you want to localize the resulting XML
document by using a resource bundle.
The JaxMe Marshaller
allows
to set arbitrary, application specific properties, but only if they are prefixed
with jaxme.private.. The idea is that an unknown property name
is most probably just a typo or a similar problem and it is better to throw
an exception to let the programmer know that there's something wrong. However,
if the property name is prefixed with jaxme.private., the
JaxMe user advises, that "this unknown property is
fine".
contains(Object)
.JMMarshallerImpl
.JAXME_XML_DECLARATION
When writing to an OutputStream
or Writer
,
it is likely that you want your resulting XML document to have an XML declaration like
<?xml version="1.0" encoding="UTF-8"?>By default JaxMe does not create such a declaration, because that would make it difficult to embed the resulting XML into a larger document.
An XML declaration is particularly important, if you use another encoding than
UTF-8
. In that case you should consider a declaration as
mandatory.
The property has no effect, when marshalling to DOM trees, or SAX handlers.
Example 2.10. Enabling an XML declaration
// Convert a JaxMe object into a String, using the above converter public String asString(JAXBContext pContext, Object pObject) throws JAXBException { java.io.StringWriter sw = new StringWriter(); Marshaller marshaller = pContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.setProperty(JMMarshallerImpl.JAXME_XML_DECLARATION, Boolean.TRUE); marshaller.marshal(pObject, sw); return sw.toString(); }
jaxme.xml.writer
, JMMarshallerImpl.JAXME_XML_WRITER
If the above properties still don't satisfy your needs, this property is your
friend. It allows to take complete control on the created XML document, when
marshalling to an OutputStream
or Writer
.
(It takes no effect otherwise.)
The property value is an instance of java.lang.Class
implementing org.apache.ws.jaxme.XMLWriter
, which is in turn
extending org.xml.sax.ContentHandler
. That means, that you
can do arbitrary things, including use of an XSLT stylesheet and stuff like that.
JaxMe comes with three ready-to-use implementations:
org.apache.ws.jaxme.impl.XMLWriterImpl
This is the default implementation, suitable for any encoding and Java platform. It is very simple: It is in fact using the US-ASCII encoding, escaping all Unicode points above 126.
org.apache.ws.jaxme.impl.CharSetXMLWriter
This implementation is used, whenever you are running Java 1.4 or newer.
It is internally using the class java.nio.charset.CharsetEncoder
to determine the Unicode points being escaped.
org.apache.ws.jaxme.impl.PassThroughXMLWriter
This implementation does no escaping at all.
As an example, we'll write and use and XMLWriter
which is
escaping the german Umlauts, but no other characters.
Example 2.11. Implementation of an XMLWriter escaping german Umlauts
public class UmlautXMLWriter extends org.apache.ws.jaxme.impl.XMLWriterImpl { protected boolean canEncode(char c) { switch (c) { case 228: case 246: case 252: case 196: case 214: case 220: case 223: return false; default: return true; } } } // Convert a JaxMe object into a String, using the above XMLWriter public String asString(JAXBContext pContext, Object pObject) throws JAXBException { java.io.StringWriter sw = new StringWriter(); Marshaller marshaller = pContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.setProperty(JMMarshallerImpl.JAXME_XML_WRITER, UmlautXMLWriter.class); marshaller.marshal(pObject, sw); return sw.toString(); }