Repository: incubator-juneau Updated Branches: refs/heads/master [created] 7e4f63e6d
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/package.html b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/package.html new file mode 100755 index 0000000..7a23cc7 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/package.html @@ -0,0 +1,2321 @@ +<!DOCTYPE HTML> +<!-- + Licensed Materials - Property of IBM + (c) Copyright IBM Corporation 2014. All Rights Reserved. + + Note to U.S. Government Users Restricted Rights: + Use, duplication or disclosure restricted by GSA ADP Schedule + Contract with IBM Corp. + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>XML serialization and parsing support</p> +<script> + function toggle(x) { + var div = x.nextSibling; + while (div != null && div.nodeType != 1) + div = div.nextSibling; + if (div != null) { + var d = div.style.display; + if (d == 'block' || d == '') { + div.style.display = 'none'; + x.className += " closed"; + } else { + div.style.display = 'block'; + x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' ); + } + } + } +</script> + +<a id='TOC'></a><h5 class='toc'>Table of Contents</h5> +<ol class='toc'> + <li><p><a class='doclink' href='#Overview'>XML support overview</a></p> + <ol> + <li><p><a class='doclink' href='#OverviewExample'>Example</a></p> + </ol> + <li><p><a class='doclink' href='#XmlSerializer'>XmlSerializer class</a></p> + <ol> + <li><p><a class='doclink' href='#XmlAnnotation'>@Xml annotations</a></p> + <ol> + <li><p><a class='doclink' href='#XmlChildName'>@Xml.childName()</a></p> + <li><p><a class='doclink' href='#XmlFormat'>@Xml.format()</a></p> + <li><p><a class='doclink' href='#XmlContentHandler'>@Xml.contentHandler()</a></p> + </ol> + <li><p><a class='doclink' href='#Namespaces'>Namespaces</a></p> + <ol> + <li><p><a class='doclink' href='#AutoDetectNamespaces'>Auto-detection of namespaces</a></p> + </ol> + <li><p><a class='doclink' href='#UriProperties'>URI properties</a></p> + <li><p><a class='doclink' href='#BeanAnnotations'>@Bean and @BeanProperty annotations</a></p> + <li><p><a class='doclink' href='#Collections'>Collections</a></p> + <li><p><a class='doclink' href='#XmlSchemaSupport'>XML-Schema support</a></p> + <li><p><a class='doclink' href='#Recursion'> Non-tree models and recursion detection</a></p> + <li><p><a class='doclink' href='#SerializerConfigurableProperties'>Configurable properties</a></p> + <li><p><a class='doclink' href='#SerializerOtherNotes'>Other notes</a></p> + </ol> + <li><p><a class='doclink' href='#XmlParser'>XmlParser class</a></p> + <ol> + <li><p><a class='doclink' href='#GenericParsing'>Parsing into generic POJO models</a></p> + <ol> + <li><p><a class='doclink' href='#AddJsonTypeAttr'>Serializing with JSON-type attributes</a></p> + </ol> + <li><p><a class='doclink' href='#ParserConfigurableProperties'>Configurable properties</a></p> + <li><p><a class='doclink' href='#ParserOtherNotes'>Other notes</a></p> + </ol> + <li><p><a class='doclink' href='#RestApiSupport'>REST API support</a></p> + <ol> + <li><p><a class='doclink' href='#RestServerSupport'>REST server support</a></p> + <ol> + <li><p><a class='doclink' href='#RestServletDefault'>Using RestServletDefault</a></p> + <li><p><a class='doclink' href='#RestServlet'>Using RestServlet with annotations</a></p> + <li><p><a class='doclink' href='#DefaultProvider'>Using JAX-RS DefaultProvider</a></p> + <li><p><a class='doclink' href='#BaseProvider'>Using JAX-RS BaseProvider with annotations</a></p> + </ol> + <li><p><a class='doclink' href='#RestClientSupport'>REST client support</a></p> + </ol> +</ol> + +<!-- ======================================================================================================== --> +<a id="Overview"></a> +<h2 class='topic' onclick='toggle(this)'>1 - XML support overview</h2> +<div class='topic'> + <p> + Juno supports converting arbitrary POJOs to and from XML using ultra-efficient serializers and parsers.<br> + The XML serializer converts POJOs directly to XML without the need for intermediate DOM objects.<br> + Likewise, the XML parser uses a STaX parser and creates POJOs directly without intermediate DOM objects. + </p> + <p> + Unlike frameworks such as JAXB, Juno does not require POJO classes to be annotated to produce + and consume XML.<br> + For example, it can serialize and parse instances of any of the following POJO types: + </p> + <ul> + <li>Java primitive objects (e.g. <code>String</code>, <code>Integer</code>, <code>Boolean</code>, <code>Float</code>). + <li>Java collections framework objects (e.g. <code>HashSet</code>, <code>TreeMap</code>) containing anything on this list. + <li>Multi-dimensional arrays of any type on this list. + <li>Java Beans with properties of any type on this list. + <li>Classes with standard transformations to and from <code>Strings</code> (e.g. classes containing <code>toString()</code>, <code>fromString()</code>, <code>valueOf()</code>, <code>constructor(String)</code>). + </ul> + <p> + In addition to the types shown above, Juno includes the ability to define filters to transform non-standard object and + property types to serializable forms (e.g. to transform <code>Calendars</code> to and from <code>ISO8601</code> strings, + or <code>byte[]</code> arrays to and from base-64 encoded strings).<br> + These filters can be associated with serializers/parsers, or can be associated with classes or bean properties through type and method annotations. + </p> + <p> + Refer to <a href='../package-summary.html#PojoCategories' class='doclink'>POJO Categories</a> for a complete definition of supported POJOs. + </p> + <p> + While annotations are not required to produce or consume XML, several XML annotations are provided + for handling namespaces and fine-tuning the format of the XML produced. + </p> + <h6 class='topic'>Prerequisites</h6> + <p> + The Juno XML serialization and parsing support does not require any external prerequisites. + It only requires Java 1.6 or above. + </p> + + <!-- ======================================================================================================== --> + <a id="OverviewExample"></a> + <h3 class='topic' onclick='toggle(this)'>1.1 - XML support overview - example</h3> + <div class='topic'> + <p> + The example shown here is from the Address Book resource located in the <code>com.ibm.juno.sample.war</code> application.<br> + The POJO model consists of a <code>List</code> of <code>Person</code> beans, with each <code>Person</code> containing + zero or more <code>Address</code> beans. + </p> + <p> + When you point a browser at <code>/sample/addressBook</code>, the POJO is rendered as HTML: + </p> + <img class='bordered' src="doc-files/Example_HTML.png"> + <p> + By appending <code>?Accept=<i>mediaType</i>&plainText=true</code> to the URL, you can view the data in the various supported XML formats: + </p> + + <h6 class='figure'>Normal XML</h6> + <img class='bordered' src="doc-files/Example_XML.png"> + + <h6 class='figure'>Simplified XML</h6> + <img class='bordered' src="doc-files/Example_XMLSimple.png"> + + <p> + In addition to serializing POJOs to XML, Juno includes support for serializing the POJO metamodel to XML Schema, with + support for multiple namespaces. + </p> + + <h6 class='figure'>XML Schema</h6> + <img class='bordered' src="doc-files/Example_XMLSchema.png"> + + </div> +</div> + +<!-- ======================================================================================================== --> +<a id="XmlSerializer"></a> +<h2 class='topic' onclick='toggle(this)'>2 - XmlSerializer class</h2> +<div class='topic'> + <p> + {@link com.ibm.juno.core.xml.XmlSerializer} is the class used to convert POJOs to XML.<br> + {@link com.ibm.juno.core.xml.XmlDocSerializer} is a subclass that adds an XML declaration element to the output before the POJO is serialized. + </p> + <p> + The XML serializer includes many configurable settings.<br> + Static reusable instances of XML serializers are provided with commonly-used settings: + </p> + <ul> + <li>{@link com.ibm.juno.core.xml.XmlSerializer#DEFAULT} - All default settings. + <li>{@link com.ibm.juno.core.xml.XmlSerializer#DEFAULT_SQ} - Use single quotes on attributes. Typically useful for testing since it makes string comparison simpler. + <li>{@link com.ibm.juno.core.xml.XmlSerializer#DEFAULT_SQ_READABLE} - Use single quotes on attributes and add whitespace for readability. + <li>{@link com.ibm.juno.core.xml.XmlSerializer#DEFAULT_XMLJSON} - Add JSON attribute tags on non-String values for full JSON equivalency. + <li>{@link com.ibm.juno.core.xml.XmlSerializer#DEFAULT_XMLJSON_SQ} - Same as DEFAULT_XMLJSON, but use single quotes on attributes. + <li>{@link com.ibm.juno.core.xml.XmlSerializer#DEFAULT_SIMPLE} - Don't serialize XML namespaces. + <li>{@link com.ibm.juno.core.xml.XmlSerializer#DEFAULT_SIMPLE_SQ} - Same as DEFAULT_SIMPLE, but use single quotes on attributes. + <li>{@link com.ibm.juno.core.xml.XmlSerializer#DEFAULT_SIMPLE_XMLJSON_SQ} - Same as DEFAULT_XMLJSON, but ignore XML namespaces and use single quotes on attributes. + </ul> + <p> + In addition, DTO beans are provided that use the XML serializer and parser for the following languages: + </p> + <ul> + <li>{@link com.ibm.juno.core.dto.atom} - ATOM beans. + <li>{@link com.ibm.juno.core.dto.cognos} - Cognos beans. + </ul> + <p> + Refer to the package-level Javadocs for more information about those formats. + </p> + <h6 class='topic'>Notes about examples</h6> + <p> + The examples shown in this document will use single-quote, readable settings.<br> + For brevity, the examples will use public fields instead of getters/setters to reduce the size of the examples.<br> + In the real world, you'll typically want to use standard bean getters and setters. + </p> + <p> + To start off simple, we'll begin with the following simplified bean and build upon it. + </p> + <p class='bcode'> + <jk>public class</jk> Person { + <jc>// Bean properties</jc> + <jk>public int</jk> <jf>id</jf>; + <jk>public</jk> String <jf>name</jf>; + + <jc>// Bean constructor (needed by parser)</jc> + <jk>public</jk> Person() {} + + <jc>// Normal constructor</jc> + <jk>public</jk> Person(<jk>int</jk> id, String name) { + <jk>this</jk>.<jf>id</jf> = id; + <jk>this</jk>.<jf>name</jf> = name; + } + } + </p> + <p> + The following code shows how to convert this to simple XML (no namespaces): + </p> + <p class='bcode'> + <jc>// Create a new serializer with readable output, no namespaces yet.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>) + .setProperty(XmlSerializerProperties.<jsf>XML_enableNamespaces</jsf>, <jk>false</jk>); + + <jc>// Create our bean.</jc> + Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>); + + <jc>// Serialize the bean to XML.</jc> + String xml = s.serialize(p); + </p> + <p> + <i>Side note:</i> Serializers can also be created by cloning existing serializers: + </p> + <p class='bcode'> + <jc>// Create a new serializer with readable output, no namespaces yet, but use cloning method.</jc> + XmlSerializer s = XmlSerializer.<jsf>DEFAULT_SQ_READABLE</jsf>.clone() + .setProperty(XmlSerializerProperties.<jsf>XML_enableNamespaces</jsf>, <jk>false</jk>); + </p> + <p> + The code above produces the following output: + </p> + <p class='bcode'> + <xt><object></xt> + <xt><id></xt>1<xt></id></xt> + <xt><name></xt>John Smith<xt></name></xt> + <xt></object></xt> + </p> + <p> + The first thing you may notice is how the bean instance is represented by the element <xt><object></xt>.<br> + When objects have no name associated with them, Juno provides a default generalized name that maps to the equivalent JSON data type.<br> + Some cases when objects do not have names: + </p> + <ul> + <li>Root element + <li>Object in an array, collection, or map. + </ul> + <p> + The generalized name reflects the JSON-equivalent data type.<br> + The full list of generalized element names are: + </p> + <ul> + <li><xt><object></xt> - A bean or <code>Map</code> element. + <li><xt><array></xt> - An array or <code>Collection</code> element. + <li><xt><string></xt> - A string value. + <li><xt><number></xt> - An int or float value. + <li><xt><boolean></xt> - A boolean value. + <li><xt><null/></xt> - A null value. + </ul> + <p> + Juno produces JSON-equivalent XML, meaning any valid JSON document can be losslessly converted into an XML equivalent.<br> + In fact, all of the Juno serializers and parsers are built upon this JSON-equivalency. + </p> + + <!-- ======================================================================================================== --> + <a id="XmlAnnotation"></a> + <h3 class='topic' onclick='toggle(this)'>2.1 - @Xml annotations</h3> + <div class='topic'> + <p> + Just because Juno allows you to serialize ordinary POJOs to XML doesn't mean you are limited to just JSON-equivalent XML.<br> + Several annotations are provided in the {@link com.ibm.juno.core.xml.annotation} package for customizing the output. + </p> + + <!-- ======================================================================================================== --> + <a id="XmlName"></a> + <h4 class='topic' onclick='toggle(this)'>2.1.1 - @Xml.name()</h4> + <div class='topic'> + <p> + The {@link com.ibm.juno.core.xml.annotation.Xml#name()} annotation can be used to override the Juno default name on unnamed objects. + </p> + <h6 class='figure'>Example</h6> + <p class='bcode'> + <ja>@Xml</ja>(name=<js>"person"</js>) + <jk>public class</jk> Person { + ... + </p> + <h6 class='figure'>Result</h6> + <p class='bcode'> + <xt><person></xt> + <xt><id></xt>1<xt></id></xt> + <xt><name></xt>John Smith<xt></name></xt> + <xt></person></xt> + </p> + </div> + + + <!-- ======================================================================================================== --> + <a id="XmlChildName"></a> + <h4 class='topic' onclick='toggle(this)'>2.1.2 - @Xml.childName()</h4> + <div class='topic'> + <p> + The {@link com.ibm.juno.core.xml.annotation.Xml#childName()} annotation can be used to specify the name of XML + child elements for bean properties of type collection or array. + </p> + <h6 class='figure'>Example</h6> + <p class='bcode'> + <jk>public class</jk> MyBean { + <ja>@Xml</ja>(childName=<js>"child"</js>} + <jk>public</jk> String[] <jf>children</jf> = {<js>"foo"</js>,<js>"bar"</js>}; + } + </p> + <h6 class='figure'>Results without annotation</h6> + <p class='bcode'> + <xt><object></xt> + <xt><children></xt> + <xt><string></xt>foo<xt></string></xt> + <xt><string></xt>bar<xt></string></xt> + <xt></children></xt> + <xt></object></xt> + </p> + <h6 class='figure'>Results with annotation</h6> + <p class='bcode'> + <xt><object></xt> + <xt><children></xt> + <xt><child></xt>foo<xt></child></xt> + <xt><child></xt>bar<xt></child></xt> + <xt></children></xt> + <xt></object></xt> + </p> + </div> + + + <!-- ======================================================================================================== --> + <a id="XmlFormat"></a> + <h4 class='topic' onclick='toggle(this)'>2.1.3 - @Xml.format()</h4> + <div class='topic'> + <p> + The {@link com.ibm.juno.core.xml.annotation.Xml#format()} annotation can be used to tweak the XML format of a POJO.<br> + The value is set to an enum value of type {@link com.ibm.juno.core.xml.annotation.XmlFormat}.<br> + This annotation can be applied to both classes and bean properties. + </p> + <h6 class='figure'>Possible values</h6> + <ul> + <li>{@link com.ibm.juno.core.xml.annotation.XmlFormat#NORMAL} - Normal formatting (default).<br><br> + <li>{@link com.ibm.juno.core.xml.annotation.XmlFormat#ATTR} - Render as an XML attribute when it would normally be rendered as an element.<br> + Can be applied to classes and properties that serialize to simple types (e.g. <code>String</code>, <code>Number</code>).<br><br> + <li>{@link com.ibm.juno.core.xml.annotation.XmlFormat#ELEMENT} - Render as an XML element when it would normally be rendered as an attribute.<br> + Can be applied to URL and ID bean properties that would normally be rendered as attributes.<br><br> + <li>{@link com.ibm.juno.core.xml.annotation.XmlFormat#COLLAPSED} - Prevents collections and arrays from being enclosed in an <xt><array></xt> (or named equivalent) element.<br> + Can be applied to properties of type collection or array, or to classes that subclass from <code>Collection</code>.<br><br> + <li>{@link com.ibm.juno.core.xml.annotation.XmlFormat#CONTENT} - Render property value directly as content of element.<br> + Can be used in combination with {@link com.ibm.juno.core.xml.annotation.Xml#contentHandler()} to produce something other than plain text, such as embedded XML. + </ul> + <h6 class='figure'>Example</h6> + <p class='bcode'> + <jk>public class</jk> MyBean { + + <jc>// Normally, bean properties would be rendered as child elements of the bean element.</jc> + <jc>// Override so that it's rendered as a "f1='123'" attribute on the bean element instead.</jc> + <ja>@Xml</ja>(format=XmlFormat.<jsf>ATTR</jsf>} + <jk>public int</jk> f1 = 123; + + <jc>// Normally, bean URL properties would be rendered as XML attributes on the bean element.</jc> + <jc>// Override so that it's rendered as an <href>http://foo</href> child element instead.</jc> + <ja>@BeanProperty</ja>(uri=<jk>true</jk>) + <ja>@Xml</ja>(format=XmlFormat.<jsf>ELEMENT</jsf>} + <jk>public</jk> URL <jf>href</jf> = <jk>new</jk> URL(<js>"http://foo"</js>); + + <jc>// Normally, collection properties would be grouped under a single <children> child element on the bean element.</jc> + <jc>// Override so that entries are directly children of the bean element with each entry having an element name of <child>.</jc> + <ja>@Xml</ja>(format=XmlFormat.<jsf>COLLAPSED</jsf>, childName=<js>"child"</js>} + <jk>public</jk> String[] <jf>children</jf> = <js>"foo"</js>,<js>"bar"</js>}; + } + </p> + <h6 class='figure'>Results without annotation</h6> + <p class='bcode'> + <xt><object</xt> <xa>href</xa>=<js>'http://foo'</js><xt>></xt> + <xt><f1></xt>123<xt></f1></xt> + <xt><children></xt> + <xt><string></xt>foo<xt></string></xt> + <xt><string></xt>bar<xt></string></xt> + <xt></children></xt> + <xt></object></xt> + </p> + <h6 class='figure'>Results with annotation</h6> + <p class='bcode'> + <xt><object</xt> <xa>f1</xa>=<js>'123'</js><xt>></xt> + <xt><href></xt>http://foo<xt></href></xt> + <xt><child></xt>foo<xt></child></xt> + <xt><child></xt>bar<xt></child></xt> + <xt></object></xt> + </p> + <p> + The {@link com.ibm.juno.core.xml.annotation.XmlFormat#CONTENT} annotation can be used to serialize a bean + straight to text without any child elements. + </p> + <h6 class='figure'>Example</h6> + <p class='bcode'> + <ja>@Xml</ja>(name=<js>"MyBean"</js>) + <jk>public class</jk> MyBean { + + <ja>@Xml</ja>(format=XmlFormat.<jsf>CONTENT</jsf>) + <jk>public</jk> String beanContents = <js>"This is my bean"</js>; + } + </p> + <h6 class='figure'>Results</h6> + <p class='bcode'> + <xt><MyBean></xt> + This is my bean + <xt></MyBean></xt> + </p> + <p> + There are some restrictions when using the <jsf>CONTENT</jsf> format: + </p> + <ul> + <li>A bean class can only have one property denoted with <jsf>CONTENT</jsf> format. + <li>A bean class cannot have any other properties that would serialize to an element (attributes are okay). + </ul> + <p> + The class type on the property can be anything that serializes to a simple value, like a <code>String</code> or <code>Number</code>.<br> + Note that filters can usually be used to convert more complex class types to simple types. + </p> + </div> + + + <!-- ======================================================================================================== --> + <a id="XmlContentHandler"></a> + <h4 class='topic' onclick='toggle(this)'>2.1.4 - @Xml.contentHandler()</h4> + <div class='topic'> + <p> + The {@link com.ibm.juno.core.xml.annotation.Xml#contentHandler()} annotation is an advanced feature that allows you to + define your own code for serializing and parsing the contents of a serialized bean. + </p> + <p> + For example, the ATOM specification allows for the media type of <code>Text</code> elements to be specified via a <xa>type</xa> attribute: + </p> + <p class='bcode'> + <xt><feed></xt> + <xt><entry></xt> + <xt><title</xt> <xa>type</xa>=<xs>'text'</xs>></xt> + A &lt;em&gt;lot&lt;/em&gt; of effort went into making this effortless + <xt></title></xt> + <xt><content</xt> <xa>type</xa>=<xs>'xhtml'</xs>></xt> + <xt><div</xt> <xa>xmlns</xa>=<xs>"http://www.w3.org/1999/xhtml"</xs><xt>></xt><xt><p></xt><xt><i></xt>This is the contents of this entry.<xt></i></xt><xt></p></xt><xt></div></xt> + <xt></content></xt> + <xt></entry></xt> + <xt></feed></xt> + </p> + <p> + To accomplish this, the {@link com.ibm.juno.core.dto.atom.Text} class uses a content handler that serializes and parses the value by inspecting the <xa>type</xa> value and then handling the content accordingly: + </p> + <p class='bcode'> + <jk>public class</jk> Text <jk>extends</jk> Common { + + <jk>private</jk> String <jf>type</jf>; + <jk>private</jk> String <jf>text</jf>; + + <ja>@Xml</ja>(format=<jsf>ATTR</jsf>) + <jk>public</jk> String getType() { + <jk>return</jk> <jf>type</jf>; + } + + <jk>public</jk> Text setType(String type) { + <jk>this</jk>.<jf>type</jf> = type; + <jk>return this</jk>; + } + + <ja>@Xml</ja>(format=<jsf>CONTENT</jsf>, contentHandler=TextContentHandler.<jk>class</jk>) + <jk>public</jk> String getText() { + <jk>return</jk> text; + } + + <jk>public</jk> Text setText(String text) { + <jk>this</jk>.<jf>text</jf> = text; + <jk>return this</jk>; + } + + <jc>// Converts the text to and from the appropriate media type.</jc> + <jk>public class</jk> TextContentHandler <jk>implements</jk> XmlContentHandler<Text> { + + <ja>@Override</ja> + <jk>public void</jk> parse(XMLStreamReader r, Text text) <jk>throws</jk> Exception { + String type = text.<jf>type</jf>; + <jk>if</jk> (type != <jk>null</jk> && type.equals(<js>"xhtml"</js>)) + text.<jf>text</jf> = <jsm>decode</jsm>(readXmlContents(r).trim()); + <jk>else</jk> + text.<jf>text</jf> = <jsm>decode</jsm>(r.getElementText().trim()); + } + + <ja>@Override</ja> + <jk>public void</jk> serialize(XmlSerializerWriter w, Text text) <jk>throws</jk> Exception { + String type = text.<jf>type</jf>; + String content = text.<jf>text</jf>; + <jk>if</jk> (type != <jk>null</jk> && type.equals(<js>"xhtml"</js>)) + w.encodeTextInvalidChars(content); + <jk>else</jk> + w.encodeText(content); + } + } + } + </p> + <p> + Refer to {@link com.ibm.juno.core.xml.XmlContentHandler} for more information. + </p> + </div> + </div> + + + <!-- ======================================================================================================== --> + <a id="Namespaces"></a> + <h3 class='topic' onclick='toggle(this)'>2.2 - Namespaces</h3> + <div class='topic'> + <p> + Let's go back to the example of our original <code>Person</code> bean class: + </p> + <p class='bcode'> + <jk>public class</jk> Person { + <jc>// Bean properties</jc> + <jk>public int</jk> <jf>id</jf>; + <jk>public</jk> String <jf>name</jf>; + + <jc>// Bean constructor (needed by parser)</jc> + <jk>public</jk> Person() {} + + <jc>// Normal constructor</jc> + <jk>public</jk> Person(<jk>int</jk> id, String name) { + <jk>this</jk>.<jf>id</jf> = id; + <jk>this</jk>.<jf>name</jf> = name; + } + } + </p> + <p> + However, this time we'll leave namespaces enabled on the serializer: + </p> + <p class='bcode'> + <jc>// Create a new serializer with readable output, this time with namespaces enabled.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>); + + <jc>// Create our bean.</jc> + Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>); + + <jc>// Serialize the bean to XML.</jc> + String xml = s.serialize(p); + </p> + <p> + Now when we run this code, we'll see namespaces added to our output: + </p> + <p class='bcode'> + <xt><object</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs><xt>></xt> + <xt><id></xt>1<xt></id></xt> + <xt><name></xt>John Smith<xt></name></xt> + <xt></object></xt> + </p> + <p> + This isn't too exciting yet since we haven't specified any namespaces yet.<br> + Therefore, everything is defined under the default <code>Juno</code> namespace.<br> + The <code>xsi</code> namespace is always present and is used to mark null property values with <code><xa>nil</xa>=<xs>'true'</xs></code>. + </p> + <p> + Namespaces can be defined at the following levels: + </p> + <ul> + <li>At the package level by using the {@link com.ibm.juno.core.xml.annotation.XmlSchema @XmlSchema} annotation. + <li>At the class level by using the {@link com.ibm.juno.core.xml.annotation.Xml @Xml} annotation. + <li>At the bean property level by using the {@link com.ibm.juno.core.xml.annotation.Xml @Xml} annotation. + </ul> + <p> + It's typically best to specify the namespaces used at the package level.<br> + We'll do that here for the package containing our test code. + </p> + <p class='bcode'> + <jc>// XML namespaces used in this package</jc> + <ja>@XmlSchema</ja>( + prefix=<js>"ab"</js>, + xmlNs={ + <ja>@XmlNs</ja>(prefix=<js>"ab"</js>, namespaceURI=<js>"http://www.ibm.com/addressBook/"</js>), + <ja>@XmlNs</ja>(prefix=<js>"per"</js>, namespaceURI=<js>"http://www.ibm.com/person/"</js>), + <ja>@XmlNs</ja>(prefix=<js>"addr"</js>, namespaceURI=<js>"http://www.ibm.com/address/"</js>), + <ja>@XmlNs</ja>(prefix=<js>"mail"</js>, namespaceURI=<js>"http://www.ibm.com/mail/"</js>) + } + ) + <jk>package</jk> com.ibm.sample.addressbook; + <jk>import</jk> com.ibm.juno.core.xml.annotation.*; + </p> + <p> + We're defining four namespaces in this package and designating <js>"http://www.ibm.com/addressBook/"</js> as the default + namespace for all classes and properties within this package. + </p> + <p> + Take special note that the <ja>@XmlSchema</ja> is modelled after the equivalent JAXB annotation, but is + defined in the {@link com.ibm.juno.core.xml.annotation} package.<br> + Other XML annotations are also modelled after JAXB. + However, since many of the features of JAXB are already implemented for all serializers and parsers + at a higher level through various general annotations such as {@link com.ibm.juno.core.annotation.Bean} and {@link com.ibm.juno.core.annotation.BeanProperty} + it was decided to maintain separate Juno XML annotations instead of reusing JAXB annotations.<br> + This may change in some future implementation, but for now it was decided that having separate Juno XML annotations was less confusing. + </p> + <p> + On our bean class, we'll specify to use the <js>"http://www.ibm.com/person/"</js> namespace: + </p> + <p class='bcode'> + <ja>@Xml</ja>(name=<js>"person"</js>, prefix=<js>"per"</js>) + <jk>public class</jk> Person { + ... + </p> + <p> + Now when we serialize the bean, we get the following: + </p> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs><xt>></xt> + <xt><per:id></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt></per:person></xt> + </p> + <p> + We can simplify the output by setting the default namespace on the serializer so that + all the elements do not need to be prefixed: + <p class='bcode'> + <jc>// Create a new serializer with readable output, this time with namespaces enabled.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>) + .setProperty(XmlSerializerProperties.<jsf>XML_defaultNamespaceUri</jsf>, <js>"http://www.ibm.com/person/"</js>); + </p> + <p> + This produces the following equivalent where the elements don't need prefixes since they're already in the default document namespace: + </p> + <p class='bcode'> + <xt><person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:juno</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs><xt>></xt> + <xt><id></xt>1<xt></id></xt> + <xt><name></xt>John Smith<xt></name></xt> + <xt></person></xt> + </p> + + + <!-- ======================================================================================================== --> + <a id="AutoDetectNamespaces"></a> + <h4 class='topic' onclick='toggle(this)'>2.2.1 - Auto-detection of namespaces</h4> + <div class='topic'> + <p> + One important property on the XML serializer class is {@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_autoDetectNamespaces XML_autoDetectNamespaces}.<br> + This property tells the serializer to make a first-pass over the data structure to look for namespaces defined on classes and bean properties.<br> + In high-performance environments, you may want to consider disabling auto-detection and providing your own explicit list of namespaces to the serializer + to avoid this scanning step. + </p> + <p> + The following code will produce the same output as before, but will perform slightly better since it avoids this prescan step. + </p> + <p class='bcode'> + <jc>// Create a new serializer with readable output, this time with namespaces enabled.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>) + .setProperty(XmlSerializerProperties.<jsf>XML_autoDetectNamespaces</jsf>, <jk>false</jk>) + .setProperty(XmlSerializerProperties.<jsf>XML_namespaces</jsf>, <js>"{per:'http://www.ibm.com/person/'}"</js>); + </p> + </div> + + </div> + + <!-- ======================================================================================================== --> + <a id="UriProperties"></a> + <h3 class='topic' onclick='toggle(this)'>2.3 - URI properties</h3> + <div class='topic'> + <p> + The {@link com.ibm.juno.core.annotation.BeanProperty#beanUri} annotation is used to identify the URI + of a bean.<br> + Typically, this property will have a class type of {@link java.net.URI} or {@link java.net.URL}, although + it can essentially be any type that resolves to a simple serializable type. + </p> + <p> + In the following code, we're adding 2 new properties.<br> + The first property is annotated with <ja>@BeanProperty</ja> to identify that this property is the + resource identifier for this bean.<br> + The second unannotated property is interpreted as a normal property. + </p> + <p class='bcode'> + <ja>@Xml</ja>(name=<js>"person"</js>, prefix=<js>"per"</js>) + <jk>public class</jk> Person { + + <jc>// Bean properties</jc> + <ja>@BeanProperty</ja>(beanUri=<jk>true</jk>) + <jk>public</jk> URI <jf>uri</jf>; + + <jk>public</jk> URI <jf>addressBookUri</jf>; + + ... + + <jc>// Normal constructor</jc> + <jk>public</jk> Person(<jk>int</jk> id, String name, String uri, String addressBookUri) <jk>throws</jk> URISyntaxException { + <jk>this</jk>.<jf>id</jf> = id; + <jk>this</jk>.<jf>name</jf> = name; + <jk>this</jk>.<jf>uri</jf> = <jk>new</jk> URI(uri); + <jk>this</jk>.<jf>addressBookUri</jf> = <jk>new</jk> URI(addressBookUri); + } + } + </p> + <p> + We alter our code to pass in values for these new properties. + </p> + <p class='bcode'> + <jc>// Create our bean.</jc> + Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>, <js>"http://sample/addressBook/person/1"</js>, <js>"http://sample/addressBook"</js>); + </p> + <p> + Now when we run the sample code, we get the following: + </p> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><per:id></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt><per:addressBookUri></xt>http://sample/addressBook<xt></per:addressBookUri></xt> + <xt></per:person></xt> + </p> + <p> + Notice how the bean URI property is serialized as an XML attribute while the normal URI property is + serialized normally as an element. + </p> + <p> + Bean properties that resolve as XML attributes can also be assigned namespaces.<br> + For example, let's assign our bean URI to another namespace. + </p> + <p class='bcode'> + <ja>@BeanProperty</ja>(beanUri=<jk>true</jk>) + <ja>@Xml</ja>(prefix=<js>"ab"</js>) + <jk>public</jk> URI <jf>uri</jf>; + </p> + <p> + Now when we run the sample code, we see that our <xa>uri</xa> attribute is assigned that namespace prefix. + </p> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:ab</xa>=<xs>'http://www.ibm.com/addressBook/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>ab:uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><per:id></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt><per:addressBookUri></xt>http://sample/addressBook<xt></per:addressBookUri></xt> + <xt></per:person></xt> + </p> + <p> + The {@link com.ibm.juno.core.annotation.URI} annotation can also be used on classes and properties + to identify them as URLs when they're not instances of <code>java.net.URI</code> or <code>java.net.URL</code> + (not needed if <code><ja>@BeanProperty</ja>(beanUri=<jk>true</jk>)</code> is already specified). + </p> + <p> + The following properties would have produced the same output as before. + </p> + <p class='bcode'> + <jk>public class</jk> Person { + + <jc>// Bean properties</jc> + <ja>@BeanProperty</ja>(beanUri=<jk>true</jk>) <jk>public</jk> String <jf>uri</jf>; + </p> + <p> + Also take note of the {@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_absolutePathUriBase SERIALIZER_absolutePathUriBase} and + {@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_relativeUriBase SERIALIZER_relativeUriBase} + settings that can be specified on the serializer to resolve relative and context-root-relative URIs to fully-qualfied URIs. + </p> + <p> + This can be useful if you want to keep the URI authority and context root information out of the bean logic layer. + </p> + <p> + The following code produces the same output as before, but the URIs on the beans are relative. + </p> + <p class='bcode'> + <jc>// Create a new serializer with readable output.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_relativeUriBase</jsf>, <js>"http://myhost/sample"</js>); + .setProperty(SerializerProperties.<jsf>SERIALIZER_absolutePathUriBase</jsf>, <js>"http://myhost"</js>); + + <jc>// Create our bean.</jc> + Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>, <js>"person/1"</js>, <js>"/"</js>); + + <jc>// Serialize the bean to RDF/XML.</jc> + String rdfXml = s.serialize(p); + </p> + </div> + + <!-- ======================================================================================================== --> + <a id="BeanAnnotations"></a> + <h3 class='topic' onclick='toggle(this)'>2.4 - @Bean and @BeanProperty annotations</h3> + <div class='topic'> + <p> + The {@link com.ibm.juno.core.annotation.Bean @Bean} and {@link com.ibm.juno.core.annotation.BeanProperty @BeanProperty} annotations + are used to customize the behavior of beans across the entire framework.<br> + In addition to using them to identify the resource URI for the bean shown above, they have various other uses: + </p> + <ul> + <li>Hiding bean properties. + <li>Specifying the ordering of bean properties. + <li>Overriding the names of bean properties. + <li>Associating filters at both the class and property level (to convert non-serializable POJOs to serializable forms). + </ul> + <p> + For example, we now add a <code>birthDate</code> property, and associate a filter with it to transform + it to an ISO8601 date-time string in GMT time.<br> + By default, <code>Calendars</code> are treated as beans by the framework, which is usually not how you want them serialized.<br> + Using filters, we can convert them to standardized string forms. + </p> + <p class='bcode'> + <ja>@Xml</ja>(name=<js>"person"</js>, prefix=<js>"per"</js>) + <jk>public class</jk> Person { + + <jc>// Bean properties</jc> + <ja>@BeanProperty</ja>(filter=CalendarFilter.ISO8601DTZ.<jk>class</jk>) <jk>public</jk> Calendar birthDate; + ... + + <jc>// Normal constructor</jc> + <jk>public</jk> Person(<jk>int</jk> id, String name, String uri, String addressBookUri, String birthDate) <jk>throws</jk> Exception { + ... + <jk>this</jk>.<jf>birthDate</jf> = <jk>new</jk> GregorianCalendar(); + <jk>this</jk>.<jf>birthDate</jf>.setTime(DateFormat.<jsm>getDateInstance</jsm>(DateFormat.<jsf>MEDIUM</jsf>).parse(birthDate)); + } + } + </p> + <p> + Next, we alter our code to pass in the birthdate: + </p> + <p class='bcode'> + <jc>// Create our bean.</jc> + Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>, <js>"http://sample/addressBook/person/1"</js>, <js>"http://sample/addressBook"</js>, <js>"Aug 12, 1946"</js>); + </p> + <p> + Now when we rerun the sample code, we'll get the following: + </p> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><per:id></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt><per:addressBookUri></xt>http://sample/addressBook<xt></per:addressBookUri></xt> + <xt><per:birthDate></xt>1946-08-12T00:00:00Z<xt></per:birthDate></xt> + <xt></per:person></xt> + </p> + <p> + Another useful feature is the {@link com.ibm.juno.core.annotation.Bean#propertyNamer()} annotation that allows you to plug in your own + logic for determining bean property names.<br> + The {@link com.ibm.juno.core.PropertyNamerDashedLC} is an example of an alternate property namer. + It converts bean property names to lowercase-dashed format. + </p> + <h6 class='figure'>Example</h6> + <p class='bcode'> + <ja>@Xml</ja>(name=<js>"person"</js>, prefix=<js>"per"</js>) + <ja>@Bean</ja>(propertyNamer=PropertyNamerDashedLC.<jk>class</jk>) + <jk>public class</jk> Person { + ... + </p> + <h6 class='figure'>Results</h6> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><per:id></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt><per:address-book-uri></xt>http://sample/addressBook<xt></per:address-book-uri></xt> + <xt><per:birth-date></xt>1946-08-12T04:00:00Z<xt></per:birth-date></xt> + <xt></per:person></xt> + </p> + </div> + + + <!-- ======================================================================================================== --> + <a id="Collections"></a> + <h3 class='topic' onclick='toggle(this)'>2.5 - Collections</h3> + <div class='topic'> + <p> + In our example, let's add a list-of-beans property to our sample class: + </p> + <p class='bcode'> + <jk>public class</jk> Person { + + <jc>// Bean properties</jc> + <jk>public</jk> LinkedList<Address> <jf>addresses</jf> = <jk>new</jk> LinkedList<Address>(); + ... + } + </p> + <p> + The <code>Address</code> class has the following properties defined: + </p> + <p class='bcode'> + <ja>@Xml</ja>(name=<js>"address"</js>, prefix=<js>"addr"</js>) + <jk>public class</jk> Address { + + <jc>// Bean properties</jc> + <ja>@BeanProperty</ja>(beanUri=<jk>true</jk>) <jk>public</jk> URI <jf>uri</jf>; + <jk>public</jk> URI <jf>personUri</jf>; + <jk>public int</jk> <jf>id</jf>; + <ja>@Xml</ja>(prefix=<js>"mail"</js>) <jk>public</jk> String <jf>street</jf>, <jf>city</jf>, <jf>state</jf>; + <ja>@Xml</ja>(prefix=<js>"mail"</js>) <jk>public int</jk> <jf>zip</jf>; + <jk>public boolean</jk> <jf>isCurrent</jf>; + } + </p> + <p> + Next, add some quick-and-dirty code to add an address to our person bean: + </p> + <p class='bcode'> + <jc>// Create a new serializer with readable output.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>); + + <jc>// Create our bean.</jc> + Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>, <js>"http://sample/addressBook/person/1"</js>, <js>"http://sample/addressBook"</js>, <js>"Aug 12, 1946"</js>); + Address a = <jk>new</jk> Address(); + a.<jf>uri</jf> = <jk>new</jk> URI(<js>"http://sample/addressBook/address/1"</js>); + a.<jf>personUri</jf> = <jk>new</jk> URI(<js>"http://sample/addressBook/person/1"</js>); + a.<jf>id</jf> = 1; + a.<jf>street</jf> = <js>"100 Main Street"</js>; + a.<jf>city</jf> = <js>"Anywhereville"</js>; + a.<jf>state</jf> = <js>"NY"</js>; + a.<jf>zip</jf> = 12345; + a.<jf>isCurrent</jf> = <jk>true</jk>; + p.<jf>addresses</jf>.add(a); + </p> + <p> + Now when we run the sample code, we get the following: + </p> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:addr</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>xmlns:mail</xa>=<xs>'http://www.ibm.com/mail/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><per:id></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt><per:addressBookUri></xt>http://sample/addressBook<xt></per:addressBookUri></xt> + <xt><per:birthDate></xt>1946-08-12T04:00:00Z<xt></per:birthDate></xt> + <xt><per:addresses></xt> + <xt><addr:address</xt> <xa>uri</xa>=<xs>'http://sample/addressBook/address/1'</xs><xt>></xt> + <xt><addr:personUri></xt>http://sample/addressBook/person/1<xt></addr:personUri></xt> + <xt><addr:id></xt>1<xt></addr:id></xt> + <xt><mail:street></xt>100 Main Street<xt></mail:street></xt> + <xt><mail:city></xt>Anywhereville<xt></mail:city></xt> + <xt><mail:state></xt>NY<xt></mail:state></xt> + <xt><mail:zip></xt>12345<xt></mail:zip></xt> + <xt><addr:isCurrent></xt>true<xt></addr:isCurrent></xt> + <xt></addr:address></xt> + <xt></per:addresses></xt> + <xt></per:person></xt> + </p> + </div> + + + <!-- ======================================================================================================== --> + <a id="XmlSchemaSupport"></a> + <h3 class='topic' onclick='toggle(this)'>2.6 - XML-Schema support</h3> + <div class='topic'> + <p> + Juno provides the {@link com.ibm.juno.core.xml.XmlSchemaSerializer} class for generating XML-Schema documents + that describe the output generated by the {@link com.ibm.juno.core.xml.XmlSerializer} class.<br> + This class shares the same properties as <code>XmlSerializer</code>.<br> + Since the XML output differs based on settings on the XML serializer class, the XML-Schema serializer + class must have the same property values as the XML serializer class it's describing.<br> + To help facilitate creating an XML Schema serializer with the same properties as the corresponding + XML serializer, the {@link com.ibm.juno.core.xml.XmlSerializer#getSchemaSerializer()} method + has been added. + </p> + <p> + XML-Schema requires a separate file for each namespace.<br> + Unfortunately, does not mesh well with the Juno serializer architecture which serializes to single writers.<br> + To get around this limitation, the schema serializer will produce a single output, but with multiple + schema documents separated by the null character (<js>'\u0000'</js>) to make it simple to split apart. + </p> + <p> + Lets start with an example where everything is in the same namespace.<br> + We'll use the classes from before, but remove the references to namespaces.<br> + Since we have not defined a default namespace, everything is defined under the default Juno namespace. + </p> + <p class='bcode'> + <ja>@Xml</ja>(name=<js>"person"</js>) + <jk>public class</jk> Person { + <jc>// Bean properties</jc> + <jk>public int</jk> <jf>id</jf>; + <jk>public</jk> String <jf>name</jf>; + <ja>@BeanProperty</ja>(beanUri=<jk>true</jk>) <jk>public</jk> URI <jf>uri</jf>; + <jk>public</jk> URI <jf>addressBookUri</jf>; + <ja>@BeanProperty</ja>(filter=CalendarFilter.ISO8601DTZ.<jk>class</jk>) <jk>public</jk> Calendar <jf>birthDate</jf>; + <jk>public</jk> LinkedList<Address> <jf>addresses</jf> = <jk>new</jk> LinkedList<Address>(); + + <jc>// Bean constructor (needed by parser)</jc> + <jk>public</jk> Person() {} + + <jc>// Normal constructor</jc> + <jk>public</jk> Person(<jk>int</jk> id, String name, String uri, String addressBookUri, String birthDate) <jk>throws</jk> Exception { + <jk>this</jk>.<jf>id</jf> = id; + <jk>this</jk>.<jf>name</jf> = name; + <jk>this</jk>.<jf>uri</jf> = <jk>new</jk> URI(uri); + <jk>this</jk>.<jf>addressBookUri</jf> = <jk>new</jk> URI(addressBookUri); + <jk>this</jk>.<jf>birthDate</jf> = <jk>new</jk> GregorianCalendar(); + <jk>this</jk>.<jf>birthDate</jf>.setTime(DateFormat.getDateInstance(DateFormat.<jsf>MEDIUM</jsf>).parse(birthDate)); + } + } + + <ja>@Xml</ja>(name=<js>"address"</js>) + <jk>public class</jk> Address { + <jc>// Bean properties</jc> + <ja>@BeanProperty</ja>(beanUri=<jk>true</jk>) <jk>public</jk> URI <jf>uri</jf>; + <jk>public</jk> URI <jf>personUri</jf>; + <jk>public int</jk> <jf>id</jf>; + <jk>public</jk> String <jf>street</jf>, <jf>city</jf>, <jf>state</jf>; + <jk>public int</jk> <jf>zip</jf>; + <jk>public boolean</jk> <jf>isCurrent</jf>; + } + </p> + <p> + The code for creating our POJO model and generating XML Schema is shown below: + </p> + <p class='bcode'> + <jc>// Create a new serializer with readable output.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(XmlSerializerProperties.<jsf>XML_enableNamespaces</jsf>, <jk>true</jk>) + .setProperty(XmlSerializerProperties.<jsf>XML_addNamespaceUrisToRoot</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>); + + <jc>// Create the equivalent schema serializer.</jc> + XmlSchemaSerializer ss = s.getSchemaSerializer(); + + <jc>// Create our bean.</jc> + Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>, <js>"http://sample/addressBook/person/1"</js>, <js>"http://sample/addressBook"</js>, <js>"Aug 12, 1946"</js>); + Address a = <jk>new</jk> Address(); + a.<jf>uri</jf> = <jk>new</jk> URI(<js>"http://sample/addressBook/address/1"</js>); + a.<jf>personUri</jf> = <jk>new</jk> URI(<js>"http://sample/addressBook/person/1"</js>); + a.<jf>id</jf> = 1; + a.<jf>street</jf> = <js>"100 Main Street"</js>; + a.<jf>city</jf> = <js>"Anywhereville"</js>; + a.<jf>state</jf> = <js>"NY"</js>; + a.<jf>zip</jf> = 12345; + a.<jf>isCurrent</jf> = <jk>true</jk>; + p.<jf>addresses</jf>.add(a); + + <jc>// Serialize the bean to XML.</jc> + String xml = s.serialize(p); + + <jc>// Get the XML Schema corresponding to the XML generated above.</jc> + String xmlSchema = ss.serialize(p); + </p> + <h6 class='figure'>XML results</h6> + <p class='bcode'> + <xt><person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><id></xt>1<xt></id></xt> + <xt><name></xt>John Smith<xt></name></xt> + <xt><addressBookUri></xt>http://sample/addressBook<xt></addressBookUri></xt> + <xt><birthDate></xt>1946-08-12T00:00:00Z<xt></birthDate></xt> + <xt><addresses></xt> + <xt><address</xt> <xa>uri</xa>=<xs>'http://sample/addressBook/address/1'</xs><xt>></xt> + <xt><personUri></xt>http://sample/addressBook/person/1<xt></personUri></xt> + <xt><id></xt>1<xt></id></xt> + <xt><street></xt>100 Main Street<xt></street></xt> + <xt><city></xt>Anywhereville<xt></city></xt> + <xt><state></xt>NY<xt></state></xt> + <xt><zip></xt>12345<xt></zip></xt> + <xt><isCurrent></xt>true<xt></isCurrent></xt> + <xt></address></xt> + <xt></addresses></xt> + <xt></person></xt> + </p> + <h6 class='figure'>XML-Schema results</h6> + <p class='bcode'> + <xt><schema</xt> + <xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs> + <xa>targetNamespace</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>elementFormDefault</xa>=<xs>'qualified'</xs> + <xa>xmlns:juno</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs><xt>></xt> + <xt><element</xt> <xa>name</xa>=<xs>'person'</xs> <xa>type</xa>=<xs>'juno:com.ibm.sample.Person'</xs><xt>/></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'com.ibm.sample.Person'</xs><xt>></xt> + <xt><sequence></xt> + <xt><element</xt> <xa>name</xa>=<xs>'id'</xs> <xa>type</xa>=<xs>'integer'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'name'</xs> <xa>type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'addressBookUri'</xs> <xa>type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'birthDate'</xs> <xa>type</xa>=<xs>'juno:java.util.Calendar'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'addresses'</xs> <xa>type</xa>=<xs>'juno:java.util.LinkedList_x003C_com.ibm.sample.Address_x003E_'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt></sequence></xt> + <xt><attribute</xt> <xa>name</xa>=<xs>'uri'</xs> <xa>type</xa>=<xs>'string'</xs><xt>/></xt> + <xt></complexType></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'java.util.Calendar'</xs><xt>></xt> + <xt><sequence></xt> + <xt><any</xt> <xa>processContents</xa>=<xs>'skip'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt></sequence></xt> + <xt></complexType></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'java.util.LinkedList_x003C_com.ibm.sample.Address_x003E_'</xs><xt>></xt> + <xt><sequence></xt> + <xt><choice</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>></xt> + <xt><element</xt> <xa>name</xa>=<xs>'address'</xs> <xa>type</xa>=<xs>'juno:com.ibm.sample.Address'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'null'</xs> <xa>type</xa>=<xs>'string'</xs><xt>/></xt> + <xt></choice></xt> + <xt></sequence></xt> + <xt></complexType></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'com.ibm.sample.Address'</xs><xt>></xt> + <xt><sequence></xt> + <xt><element</xt> <xa>name</xa>=<xs>'personUri'</xs> <xa>type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'id'</xs> <xa>type</xa>=<xs>'integer'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'street'</xs> <xa>type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'city'</xs> <xa>type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'state'</xs> <xa>type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'zip'</xs> <xa>type</xa>=<xs>'integer'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'isCurrent'</xs> <xa>type</xa>=<xs>'boolean'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt></sequence></xt> + <xt><attribute</xt> <xa>name</xa>=<xs>'uri'</xs> <xa>type</xa>=<xs>'string'</xs><xt>/></xt> + <xt></complexType></xt> + <xt></schema></xt> + </p> + <p> + Now if we add in some namespaces, we'll see how multiple namespaces are handled. + </p> + <p class='bcode'> + <ja>@Xml</ja></ja>(name=<js>"person"</js>, prefix=<js>"per"</js>) + <jk>public class</jk> Person { + ... + } + + <ja>@Xml</ja>(name=<js>"address"</js>, prefix=<js>"addr"</js>) + <jk>public class</jk> Address { + ... + <ja>@Xml</ja>(prefix=<js>"mail"</js>) <jk>public</jk> String <jf>street</jf>, <jf>city</jf>, <jf>state</jf>; + <ja>@Xml</ja>(prefix=<js>"mail"</js>) <jk>public int</jk> <jf>zip</jf>; + ... + } + </p> + <h6 class='figure'>XML results</h6> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:addr</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>xmlns:mail</xa>=<xs>'http://www.ibm.com/mail/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><per:id></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt><per:addressBookUri></xt>http://sample/addressBook<xt></per:addressBookUri></xt> + <xt><per:birthDate></xt>1946-08-12T00:00:00Z<xt></per:birthDate></xt> + <xt><per:addresses></xt> + <xt><addr:address</xt> <xa>uri</xa>=<xs>'http://sample/addressBook/address/1'</xs><xt>></xt> + <xt><addr:personUri></xt>http://sample/addressBook/person/1<xt></addr:personUri></xt> + <xt><addr:id></xt>1<xt></addr:id></xt> + <xt><mail:street></xt>100 Main Street<xt></mail:street></xt> + <xt><mail:city></xt>Anywhereville<xt></mail:city></xt> + <xt><mail:state></xt>NY<xt></mail:state></xt> + <xt><mail:zip></xt>12345<xt></mail:zip></xt> + <xt><addr:isCurrent></xt>true<xt></addr:isCurrent></xt> + <xt></addr:address></xt> + <xt></per:addresses></xt> + <xt></per:person></xt> + </p> + <p> + The schema consists of 4 documents separated by a <js>'\u0000'</js> character. + </p> + <h6 class='figure'>XML-Schema results</h6> + <p class='bcode'> + <xt><schema</xt> + <xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs> + <xa>targetNamespace</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>elementFormDefault</xa>=<xs>'qualified'</xs> + <xa>xmlns:juno</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:addr</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>xmlns:mail</xa>=<xs>'http://www.ibm.com/mail/'</xs><xt>></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/person/'</xs> <xa>schemaLocation</xa>=<xs>'per.xsd'</xs><xt>/></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/address/'</xs> <xa>schemaLocation</xa>=<xs>'addr.xsd'</xs><xt>/></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/mail/'</xs> <xa>schemaLocation</xa>=<xs>'mail.xsd'</xs><xt>/></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'int'</xs><xt>></xt> + <xt><simpleContent></xt> + <xt><extension</xt> <xa>base</xa>=<xs>'integer'</xs><xt>/></xt> + <xt></simpleContent></xt> + <xt></complexType></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'java.lang.String'</xs><xt>></xt> + <xt><simpleContent></xt> + <xt><extension</xt> <xa>base</xa>=<xs>'string'</xs><xt>/></xt> + <xt></simpleContent></xt> + <xt></complexType></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'java.net.URI'</xs><xt>></xt> + <xt><simpleContent></xt> + <xt><extension</xt> <xa>base</xa>=<xs>'string'</xs><xt>/></xt> + <xt></simpleContent></xt> + <xt></complexType></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'java.util.Calendar'</xs><xt>></xt> + <xt><sequence></xt> + <xt><any</xt> <xa>processContents</xa>=<xs>'skip'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> + <xt></sequence></xt> + <xt></complexType></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'java.util.LinkedList_x003C_com.ibm.sample.Address_x003E_'</xs><xt>></xt> + <xt><sequence></xt> + <xt><choice</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>></xt> + <xt><element</xt> <xa>name</xa>=<xs>'address'</xs> <xa>type</xa>=<xs>'addr:com.ibm.sample.Address'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'null'</xs> <xa>type</xa>=<xs>'string'</xs><xt>/></xt> + <xt></choice></xt> + <xt></sequence></xt> + <xt></complexType></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'boolean'</xs><xt>></xt> + <xt><simpleContent></xt> + <xt><extension</xt> <xa>base</xa>=<xs>'boolean'</xs><xt>/></xt> + <xt></simpleContent></xt> + <xt></complexType></xt> + <xt></schema></xt> + [\u0000] + <xt><schema</xt> + <xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs> + <xa>targetNamespace</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>elementFormDefault</xa>=<xs>'qualified'</xs> + <xa>attributeFormDefault</xa>=<xs>'qualified'</xs> + <xa>xmlns:juno</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:addr</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>xmlns:mail</xa>=<xs>'http://www.ibm.com/mail/'</xs><xt>></xt> + <xt><impor</xt>t <xa>namespace</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> <xa>schemaLocation</xa>=<xs>'juno.xsd'</xs><xt>/></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/address/'</xs> <xa>schemaLocation</xa>=<xs>'addr.xsd'</xs><xt>/></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/mail/'</xs> <xa>schemaLocation</xa>=<xs>'mail.xsd'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'person'</xs> <xa>type</xa>=<xs>'per:com.ibm.sample.Person'</xs><xt>/></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'com.ibm.sample.Person'</xs><xt>></xt> + <xt><sequence></xt> + <xt><any</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>/></xt> + <xt></sequence></xt> + <xt><attribute</xt> <xa>name</xa>=<xs>'uri'</xs> <xa>type</xa>=<xs>'string'</xs><xt>/></xt> + <xt></complexType></xt> + <xt><element</xt> <xa>name</xa>=<xs>'id'</xs> <xa>type</xa>=<xs>'juno:int'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'name'</xs> <xa>type</xa>=<xs>'juno:java.lang.String'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'addressBookUri'</xs> <xa>type</xa>=<xs>'juno:java.net.URI'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'birthDate'</xs> <xa>type</xa>=<xs>'juno:java.util.Calendar'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'addresses'</xs> <xa>type</xa>=<xs>'juno:java.util.LinkedList_x003C_com.ibm.sample.Address_x003E_'</xs><xt>/></xt> + <xt></schema></xt> + [\u0000] + <xt><schema</xt> + <xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs> + <xa>targetNamespace</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>elementFormDefault</xa>=<xs>'qualified'</xs> + <xa>attributeFormDefault</xa>=<xs>'qualified'</xs> + <xa>xmlns:juno</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:addr</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>xmlns:mail</xa>=<xs>'http://www.ibm.com/mail/'</xs><xt>></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> <xa>schemaLocation</xa>=<xs>'juno.xsd'</xs><xt>/></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/person/'</xs> <xa>schemaLocation</xa>=<xs>'per.xsd'</xs><xt>/></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/mail/'</xs> <xa>schemaLocation</xa>=<xs>'mail.xsd'</xs><xt>/></xt> + <xt><complexType</xt> <xa>name</xa>=<xs>'com.ibm.sample.Address'</xs><xt>></xt> + <xt><sequence></xt> + <xt><any</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>/></xt> + <xt></sequence></xt> + <xt><attribute</xt> <xa>name</xa>=<xs>'uri'</xs> <xa>type</xa>=<xs>'string'</xs><xt>/></xt> + <xt></complexType></xt> + <xt><element</xt> <xa>name</xa>=<xs>'personUri'</xs> <xa>type</xa>=<xs>'juno:java.net.URI'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'id'</xs> <xa>type</xa>=<xs>'juno:int'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'isCurrent'</xs> <xa>type</xa>=<xs>'juno:boolean'</xs><xt>/></xt> + <xt></schema></xt> + [\u0000] + <xt><schema</xt> + <xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs> + <xa>targetNamespace</xa>=<xs>'http://www.ibm.com/mail/'</xs> + <xa>elementFormDefault</xa>=<xs>'qualified'</xs> + <xa>attributeFormDefault</xa>=<xs>'qualified'</xs> + <xa>xmlns:juno</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:addr</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>xmlns:mail</xa>=<xs>'http://www.ibm.com/mail/'</xs><xt>></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> <xa>schemaLocation</xa>=<xs>'juno.xsd'</xs><xt>/></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/person/'</xs> <xa>schemaLocation</xa>=<xs>'per.xsd'</xs><xt>/></xt> + <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.ibm.com/address/'</xs> <xa>schemaLocation</xa>=<xs>'addr.xsd'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'street'</xs> <xa>type</xa>=<xs>'juno:java.lang.String'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'city'</xs> <xa>type</xa>=<xs>'juno:java.lang.String'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'state'</xs> <xa>type</xa>=<xs>'juno:java.lang.String'</xs><xt>/></xt> + <xt><element</xt> <xa>name</xa>=<xs>'zip'</xs> <xa>type</xa>=<xs>'juno:int'</xs><xt>/></xt> + <xt></schema></xt> + </p> + <p> + For convenience, the {@link com.ibm.juno.core.xml.XmlSchemaSerializer#getValidator(Object, SerializerContext)} method is provided + to create a {@link javax.xml.validation.Validator} using the input from the serialize method. + </p> + </div> + + + <!-- ======================================================================================================== --> + <a id="Recursion"></a> + <h3 class='topic' onclick='toggle(this)'>2.7 - Non-tree models and recursion detection</h3> + <div class='topic'> + <p> + The XML serializer is designed to be used against POJO tree structures. <br> + It expects that there not be loops in the POJO model (e.g. children with references to parents, etc...).<br> + If you try to serialize models with loops, you will usually cause a <code>StackOverflowError</code> to + be thrown (if {@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_maxDepth} is not reached first). + </p> + <p> + If you still want to use the XML serializer on such models, Juno provides the + {@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_detectRecursions} setting.<br> + It tells the serializer to look for instances of an object in the current branch of the tree and + skip serialization when a duplicate is encountered. + </p> + <p> + For example, let's make a POJO model out of the following classes: + </p> + <p class='bcode'> + <ja>@Xml</ja>(name=<js>"a"</js>) + <jk>public class</jk> A { + <jk>public</jk> B b; + } + + <jk>public class</jk> B { + <jk>public</jk> C c; + } + + <jk>public class</jk> C { + <jk>public</jk> A a; + } + </p> + <p> + Now we create a model with a loop and serialize the results. + </p> + <p class='bcode'> + <jc>// Create a new serializer with readable output.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>) + .setProperty(XmlSerializerProperties.<jsf>XML_enableNamespaces</jsf>, <jk>false</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_detectRecursions</jsf>, <jk>true</jk>); + + <jc>// Create a recursive loop.</jc> + A a = <jk>new</jk> A(); + a.<jf>b</jf> = <jk>new</jk> B(); + a.<jf>b</jf>.<jf>c</jf> = <jk>new</jk> C(); + a.<jf>b</jf>.<jf>c</jf>.<jf>a</jf> = a; + + <jc>// Serialize to XML.</jc> + String xml = s.serialize(a); + </p> + <p> + What we end up with is the following, which does not serialize the contents of the <code>c</code> field: + </p> + <p class='bcode'> + <xt><a></xt> + <xt><b></xt> + <xt><c/></xt> + <xt></b></xt> + <xt></a></xt> + </p> + <p> + Without recursion detection enabled, this would cause a stack-overflow error. + </p> + <p> + Recursion detection introduces a performance penalty of around 20%.<br> + For this reason the setting is disabled by default. + </p> + </div> + + + <!-- ======================================================================================================== --> + <a id="SerializerConfigurableProperties"></a> + <h3 class='topic' onclick='toggle(this)'>2.8 - Configurable properties</h3> + <div class='topic'> + <p> + The full list of configurable settings applicable to the <code>XmlSerializer</code> class is shown below: + </p> + <table class='styled' style='border-collapse: collapse;'> + <tr><th>Property</th><th>Short Description</th></tr> + <tr> + <td>{@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_addJsonTypeAttrs}</td> + <td>Add JSON type attributes to output</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_addJsonStringTypeAttrs}</td> + <td>Add JSON type attributes for strings to output</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_enableNamespaces}</td> + <td>Enable support for XML namespaces</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_autoDetectNamespaces}</td> + <td>Auto-detect namespace usage</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_addNamespaceUrisToRoot}</td> + <td>Add namespace URLs to the root element</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_defaultNamespaceUri}</td> + <td>Default namespace URI</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_xsNamespace}</td> + <td>XMLSchema namespace</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_xsiNamespace}</td> + <td>XMLSchema-Instance namespace</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_namespaces}</td> + <td>Default namespaces</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_maxDepth}</td> + <td>Maximum serialization depth</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_detectRecursions}</td> + <td>Automatically detect POJO recursions</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_useIndentation}</td> + <td>Use indentation in output</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_quoteChar}</td> + <td>Quote character</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_trimNullProperties}</td> + <td>Trim null bean property values from output</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_trimEmptyLists}</td> + <td>Trim empty lists and arrays from output</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_trimEmptyMaps}</td> + <td>Trim empty maps from output</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_relativeUriBase}</td> + <td>URI context root for relative URIs</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.serializer.SerializerProperties#SERIALIZER_absolutePathUriBase}</td> + <td>URI authority for absolute path relative URIs</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_beansRequireDefaultConstructor}</td> + <td>Beans require no-arg constructors</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_beansRequireSerializable}</td> + <td>Beans require <code>Serializable</code> interface</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_beansRequireSettersForGetters}</td> + <td>Beans require setters for getters</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_beansRequireSomeProperties}</td> + <td>Beans require some properties</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_beanConstructorVisibility}</td> + <td>Look for bean constructors with the specified minimum visibility</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_beanClassVisibility}</td> + <td>Look for bean classes with the specified minimum visibility</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_beanFieldVisibility}</td> + <td>Look for bean fields with the specified minimum visibility</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_methodVisibility}</td> + <td>Look for bean methods with the specified minimum visibility</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_useJavaBeanIntrospector}</td> + <td>Use Java bean Introspector for determining bean properties</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_useInterfaceProxies}</td> + <td>Use interface proxies</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_ignoreUnknownBeanProperties}</td> + <td>Ignore unknown properties</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_ignoreUnknownNullBeanProperties}</td> + <td>Ignore unknown properties with null values</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_ignorePropertiesWithoutSetters}</td> + <td>Ignore properties without setters</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_ignoreInvocationExceptionsOnGetters}</td> + <td>Ignore invocation errors when calling getters</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_ignoreInvocationExceptionsOnSetters}</td> + <td>Ignore invocation errors when calling setters</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_addNotBeanPackages}</td> + <td>Add to the list of packages whose classes should not be considered beans</td> + </tr> + <tr> + <td>{@link com.ibm.juno.core.BeanContextProperties#BEAN_removeNotBeanPackages}</td> + <td>Remove from the list of packages whose classes should not be considered beans</td> + </tr> + </table> + </div> + + + <!-- ======================================================================================================== --> + <a id="SerializerOtherNotes"></a> + <h3 class='topic' onclick='toggle(this)'>2.9 - Other notes</h3> + <div class='topic'> + <ul> + <li>Like all other Juno serializers, the XML serializer is thread safe and maintains an internal cache of bean classes encountered.<br> + For performance reasons, it's recommended that serializers be reused whenever possible instead of always creating new instances. + </ul> + </div> +</div> + + +<!-- ======================================================================================================== --> +<a id="XmlParser"></a> +<h2 class='topic' onclick='toggle(this)'>3 - XmlParser class</h2> +<div class='topic'> + <p> + The {@link com.ibm.juno.core.xml.XmlParser} class is the class used to parse Juno-generated XML back into POJOs. + </p> + <p> + A static reusable instance of <code>XmlParser</code> is also provided for convenience: + </p> + <ul> + <li>{@link com.ibm.juno.core.xml.XmlParser#DEFAULT} + </ul> + <p> + Let's build upon the previous example and parse the generated XML back into the original bean.<br> + We start with the XML that was generated. + </p> + <p class='bcode'> + <jc>// Create a new serializer with readable output.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>); + + <jc>// Create our bean.</jc> + Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>, <js>"http://sample/addressBook/person/1"</js>, <js>"http://sample/addressBook"</js>, <js>"Aug 12, 1946"</js>); + Address a = <jk>new</jk> Address(); + a.<jf>uri</jf> = <jk>new</jk> URI(<js>"http://sample/addressBook/address/1"</js>); + a.<jf>personUri</jf> = <jk>new</jk> URI(<js>"http://sample/addressBook/person/1"</js>); + a.<jf>id</jf> = 1; + a.<jf>street</jf> = <js>"100 Main Street"</js>; + a.<jf>city</jf> = <js>"Anywhereville"</js>; + a.<jf>state</jf> = <js>"NY"</js>; + a.<jf>zip</jf> = 12345; + a.<jf>isCurrent</jf> = <jk>true</jk>; + p.<jf>addresses</jf>.add(a); + + <jc>// Serialize the bean to XML.</jc> + String xml = s.serialize(p); + </p> + <p> + This code produced the following: + </p> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:addr</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>xmlns:mail</xa>=<xs>'http://www.ibm.com/mail/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><per:id></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt><per:addressBookUri></xt>http://sample/addressBook<xt></per:addressBookUri></xt> + <xt><per:birthDate></xt>1946-08-12T04:00:00Z<xt></per:birthDate></xt> + <xt><per:addresses></xt> + <xt><addr:address</xt> <xa>uri</xa>=<xs>'http://sample/addressBook/address/1'</xs><xt>></xt> + <xt><addr:personUri></xt>http://sample/addressBook/person/1<xt></addr:personUri></xt> + <xt><addr:id></xt>1<xt></addr:id></xt> + <xt><mail:street></xt>100 Main Street<xt></mail:street></xt> + <xt><mail:city></xt>Anywhereville<xt></mail:city></xt> + <xt><mail:state></xt>NY<xt></mail:state></xt> + <xt><mail:zip></xt>12345<xt></mail:zip></xt> + <xt><addr:isCurrent></xt>true<xt></addr:isCurrent></xt> + <xt></addr:address></xt> + <xt></per:addresses></xt> + <xt></per:person></xt> + </p> + <p> + The code to convert this back into a bean is: + </p> + <p class='bcode'> + <jc>// Parse it back into a bean using the reusable XML parser.</jc> + Person p = XmlParser.<jsf>DEFAULT</jsf>.parse(xml, Person.<jk>class</jk>); + + <jc>// Render it as JSON.</jc> + String json = JsonSerializer.<jsf>DEFAULT_LAX_READABLE</jsf>.serialize(p); + </p> + <p> + We print it out to JSON to show that all the data has been preserved: + </p> + <p class='bcode'> + { + id: 1, + name: <js>'John Smith'</js>, + uri: <js>'http://sample/addressBook/person/1'</js>, + addressBookUri: <js>'http://sample/addressBook'</js>, + birthDate: <js>'1946-08-12T00:00:00Z'</js>, + addresses: [ + { + uri: <js>'http://sample/addressBook/address/1'</js>, + personUri: <js>'http://sample/addressBook/person/1'</js>, + id: 1, + street: <js>'100 Main Street'</js>, + city: <js>'Anywhereville'</js>, + state: <js>'NY'</js>, + zip: 12345, + isCurrent: <jk>true</jk> + } + ] + } + </p> + + + <!-- ======================================================================================================== --> + <a id="GenericParsing"></a> + <h3 class='topic' onclick='toggle(this)'>3.1 - Parsing into generic POJO models</h3> + <div class='topic'> + <p> + The XML parser is not limited to parsing back into the original bean classes.<br> + If the bean classes are not available on the parsing side, the parser can also be used to + parse into a generic model consisting of <code>Maps</code>, <code>Collections</code>, and primitive + objects. + </p> + <p> + You can parse into any <code>Map</code> type (e.g. <code>HashMap</code>, <code>TreeMap</code>), but + using {@link com.ibm.juno.core.ObjectMap} is recommended since it has many convenience methods + for converting values to various types.<br> + The same is true when parsing collections. You can use any Collection (e.g. <code>HashSet</code>, <code>LinkedList</code>) + or array (e.g. <code>Object[]</code>, <code>String[]</code>, <code>String[][]</code>), but using + {@link com.ibm.juno.core.ObjectList} is recommended. + </p> + <p> + When the map or list type is not specified, or is the abstract <code>Map</code>, <code>Collection</code>, or <code>List</code> types, + the parser will use <code>ObjectMap</code> and <code>ObjectList</code> by default. + </p> + + + <!-- ======================================================================================================== --> + <a id="AddJsonTypeAttr"></a> + <h3 class='topic' onclick='toggle(this)'>3.1.1 - Serializing with JSON-type attributes</h3> + <div class='topic'> + <p> + In some circumstances, the XML parser needs additional information to recreate the original + data structure when various annotations (e.g. {@link com.ibm.juno.core.xml.annotation.Xml#childName()}, {@link com.ibm.juno.core.xml.annotation.Xml#format()}) + are used. + </p> + <p> + For example, starting with this XML: + </p> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:addr</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>xmlns:mail</xa>=<xs>'http://www.ibm.com/mail/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><per:id></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt><per:addressBookUri></xt>http://sample/addressBook<xt></per:addressBookUri></xt> + <xt><per:birthDate></xt>1946-08-12T04:00:00Z<xt></per:birthDate></xt> + <xt><per:addresses></xt> + <xt><addr:address</xt> <xa>uri</xa>=<xs>'http://sample/addressBook/address/1'</xs><xt>></xt> + <xt><addr:personUri></xt>http://sample/addressBook/person/1<xt></addr:personUri></xt> + <xt><addr:id></xt>1<xt></addr:id></xt> + <xt><mail:street></xt>100 Main Street<xt></mail:street></xt> + <xt><mail:city></xt>Anywhereville<xt></mail:city></xt> + <xt><mail:state></xt>NY<xt></mail:state></xt> + <xt><mail:zip></xt>12345<xt></mail:zip></xt> + <xt><addr:isCurrent></xt>true<xt></addr:isCurrent></xt> + <xt></addr:address></xt> + <xt></per:addresses></xt> + <xt></per:person></xt> + </p> + <p> + Let's try to parse this into an <code>ObjectMap</code>. + </p> + <p class='bcode'> + <jc>// Parse XML into a generic POJO model.</jc> + ObjectMap m = XmlParser.<jsf>DEFAULT</jsf>.parse(xml, ObjectMap.<jk>class</jk>); + + <jc>// Convert it to JSON.</jc> + String json = JsonSerializer.<jsf>DEFAULT_LAX_READABLE</jsf>.serialize(m); + </p> + <p> + What we end up with is the following: + </p> + <p class='bcode'> + { + uri: <js>'http://sample/addressBook/person/1'</js>, + id: <js>'1'</js>, + name: <js>'John Smith'</js>, + addressBookUri: <js>'http://sample/addressBook'</js>, + birthDate: <js>'1946-08-12T04:00:00Z'</js>, + addresses: { + address: { + uri: <js>'http://sample/addressBook/address/1'</js>, + personUri: <js>'http://sample/addressBook/person/1'</js>, + id: <js>'1'</js>, + street: <js>'100 Main Street'</js>, + city: <js>'Anywhereville'</js>, + state: <js>'NY'</js>, + zip: <js>'12345'</js>, + isCurrent: <js>'true'</js> + } + } + } + </p> + <p> + Note that the addresses field does not parse correctly as an array of addresses.<br> + Without the beans providing meta-information about the data structure, the XML + parser has no way of determining that it's an array. + </p> + <p> + To preserve this information, the {@link com.ibm.juno.core.xml.XmlSerializerProperties#XML_addJsonTypeAttrs XML_addJsonTypeAttrs} property + is provided that will insert <xa>type</xa> attributes into the XML to preserve JSON equivalency in the document when it cannot be determined automatically. + </p> + <h6 class='figure'>Example with XML_addJsonTypeAttrs setting enabled</h6> + <p class='bcode'> + <jc>// Create a new serializer with readable output.</jc> + XmlSerializer s = <jk>new</jk> XmlSerializer() + .setProperty(SerializerProperties.<jsf>SERIALIZER_useIndentation</jsf>, <jk>true</jk>) + .setProperty(SerializerProperties.<jsf>SERIALIZER_quoteChar</jsf>, <js>'\''</js>) + .setProperty(XmlSerializerProperties.<jsf>XML_addJsonTypeAttrs</jsf>, <jk>true</jk>); + </p> + <h6 class='figure'>Results</h6> + <p class='bcode'> + <xt><per:person</xt> + <xa>xmlns</xa>=<xs>'http://www.ibm.com/2013/Juno'</xs> + <xa>xmlns:per</xa>=<xs>'http://www.ibm.com/person/'</xs> + <xa>xmlns:addr</xa>=<xs>'http://www.ibm.com/address/'</xs> + <xa>xmlns:mail</xa>=<xs>'http://www.ibm.com/mail/'</xs> + <xa>xmlns:xsi</xa>=<xs>'http://www.w3.org/2001/XMLSchema-instance'</xs> + <xa>uri</xa>=<xs>'http://sample/addressBook/person/1'</xs><xt>></xt> + <xt><per:id</xt> <xa>type</xa>=<xs>'number'</xs><xt>></xt>1<xt></per:id></xt> + <xt><per:name></xt>John Smith<xt></per:name></xt> + <xt><per:addressBookUri></xt>http://sample/addressBook<xt></per:addressBookUri></xt> + <xt><per:birthDate></xt>1946-08-12T04:00:00Z<xt></per:birthDate></xt> + <xt><per:addresses</xt> <xa>type</xa>=<xs>'array'</xs><xt>></xt> + <xt><addr:address</xt> <xa>uri</xa>=<xs>'http://sample/addressBook/address/1'</xs><xt>></xt> + <xt><addr:personUri></xt>http://sample/addressBook/person/1<xt></addr:personUri></xt> + <xt><addr:id</xt> <xa>type</xa>=<xs>'number'</xs><xt>></xt>1<xt></addr:id></xt> + <xt><mail:street></xt>100 Main Street<xt></mail:street></xt> + <xt><mail:city></xt>Anywhereville<xt></mail:city></xt> + <xt><mail:state></xt>NY<xt> <TRUNCATED>
