@Xml(format) Annotation

The {@link oaj.xml.annotation.Xml#format() @Xml(format)} annotation can be used to tweak the XML format of a POJO. The value is set to an enum value of type {@link oaj.xml.annotation.XmlFormat}. This annotation can be applied to both classes and bean properties.

The {@link oaj.xml.annotation.XmlFormat#ATTR} format can be applied to bean properties to serialize them as XML attributes instead of elements. Note that this only supports properties of simple types (e.g. strings, numbers, booleans).

##### Example

Data type | JSON example | Without annotation | With annotation
--- | --- | --- | ---
class MyBean { @Xml(format=XmlFormat.ATTR) public String a; } | { a: 'foo' } | <object> <a>foo</a> </object> | <object a='foo'/>

The {@link oaj.xml.annotation.XmlFormat#ATTRS} format can be applied to bean classes to force all bean properties to be serialized as XML attributes instead of child elements.

##### Example

Data type | JSON example | Without annotation | With annotation
--- | --- | --- | ---
@Xml(format=XmlFormat.ATTRS) class MyBean { public String a; public int b; } | { a: 'foo', b: 123 } | <object> <a>foo</a> <b>123</b> </object> | <object a='foo' b='123'/>

The {@link oaj.xml.annotation.XmlFormat#ELEMENT} format can be applied to bean properties to override the {@link oaj.xml.annotation.XmlFormat#ATTRS} format applied on the bean class.

##### Example

Data type | JSON example | Without annotation | With annotation
--- | --- | --- | ---
@Xml(format=XmlFormat.ATTRS) class MyBean { public String a; @Xml(format=XmlFormat.ELEMENT) public int b; } | { a: 'foo', b: 123 } | <object> <a>foo</a> <b>123</b> </object> | <object a='foo'> <b>123</b> </object>

The {@link oaj.xml.annotation.XmlFormat#ATTRS} format can be applied to a single bean property of type Map<String,Object> to denote arbitrary XML attribute values on the element. These can be mixed with other {@link oaj.xml.annotation.XmlFormat#ATTR} annotated properties, but there must not be an overlap in bean property names and map keys.

##### Example

Data type | JSON example | Without annotation | With annotation
--- | --- | --- | ---
class MyBean { @Xml(format=XmlFormat.ATTRS) public Map<String,Object> a; @Xml(format=XmlFormat.ATTR) public int b; } | { a: { k1: 'foo', k2: 123, }, b: 456 } | <object> <a> <k1>foo</k1> <k2 _type='number'>123</k2> </a> <b>456</b> </object> | <object k1='foo' k2='123' b='456'/>

The {@link oaj.xml.annotation.XmlFormat#COLLAPSED} format can be applied to bean properties of type array/Collection. This causes the child objects to be serialized directly inside the bean element. This format must be used in conjunction with {@link oaj.xml.annotation.Xml#childName() @Xml(childName)} to differentiate which collection the values came from if you plan on parsing the output back into beans. Note that child names must not conflict with other property names.

Data type | JSON example | Without annotation | With annotation
--- | --- | --- | ---
class MyBean { @Xml(childName="A",format=XmlFormat.COLLAPSED) public String[] a; @Xml(childName="B",format=XmlFormat.COLLAPSED) public int[] b; } | { a: ['foo','bar'], b: [123,456] } | <object> <a> <string>foo</string> <string>bar</string> </a> <b> <number>123</number> <number>456</number> </b> </object> | <object> <A>foo</A> <A>bar</A> <B>123</B> <B>456</B> </object>

The {@link oaj.xml.annotation.XmlFormat#ELEMENTS} format can be applied to a single bean property of either a simple type or array/Collection. It allows free-form child elements to be formed. All other properties on the bean MUST be serialized as attributes.

Data type | JSON example | With annotation
--- | --- | ---
class MyBean { @Xml(format=XmlFormat.ATTR) public String a; @Xml(format=XmlFormat.ELEMENTS) public String b; } | { a: 'foo', b: 'bar' } | <object a='foo'> <string>bar</string> </object>
class MyBean { @Xml(format=XmlFormat.ATTR) public String a; @Xml(format=XmlFormat.ELEMENTS) public Object[] b; } | { a: 'foo', b: [ 'bar', 'baz', 123, true, null ] } | <object a='foo'> <string>bar</string> <string>baz</string> <number>123</number> <boolean>true</boolean> <null/> </object>

The {@link oaj.xml.annotation.XmlFormat#MIXED} format is similar to {@link oaj.xml.annotation.XmlFormat#ELEMENTS} except elements names on primitive types (string/number/boolean/null) are stripped from the output. This format particularly useful when combined with bean dictionaries to produce mixed content. The bean dictionary isn't used during serialization, but it is needed during parsing to resolve bean types.

The {@link oaj.xml.annotation.XmlFormat#MIXED_PWS} format identical to {@link oaj.xml.annotation.XmlFormat#MIXED} except whitespace characters are preserved in the output.

Data type | JSON example | Without annotations | With annotations
--- | --- | --- | ---
class MyBean { @Xml(format=XmlFormat.MIXED) @Beanp(dictionary={MyBeanX.class, MyBeanY.class}) public Object[] a; } @Bean(typeName="X") class MyBeanX { @Xml(format=XmlFormat.ATTR) public String b; } @Bean(typeName="Y") class MyBeanY { @Xml(format=XmlFormat.ATTR) public String c; } | { a: [ 'foo', { _type:'X', b:'bar' } 'baz', { _type:'Y', b:'qux' }, 'quux' ] } | <object> <a> <string>foo</string> <object> <b>bar</b> </object> <string>baz</string> <object> <b>qux</b> </object> <string>quux</string> </a> </object> | <object>foo<X b='bar'/>baz<Y c='qux'/>quux</object>

Whitespace (tabs and newlines) are not added to MIXED child nodes in readable-output mode. This helps ensures strings in the serialized output can be losslessly parsed back into their original forms when they contain whitespace characters. If the {@link javax.xml.stream.XMLInputFactory#IS_REPLACING_ENTITY_REFERENCES} setting was not useless in Java, we could support lossless readable XML for MIXED content. But as of Java 8, it still does not work.

XML suffers from other deficiencies as well that affect MIXED content. For example, <X></X> and <X/> are equivalent in XML and indistinguishable by the Java XML parsers. This makes it impossible to differentiate between an empty element and an element containing an empty string. This causes empty strings to get lost in translation. To alleviate this, we use the constructs "_xE000_" to represent an empty string, and "_x0020_" to represent leading and trailing spaces.

The examples below show how whitespace is handled under various circumstances:

Data type | XML
--- | ---
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT) public String a = null; } | <X/>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT) public String a = ""; } | <X>_xE000_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT) public String a = " "; } | <X>_x0020_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT) public String a = " "; } | <X>_x0020__x0020_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT) public String a = " foobar "; } | <X>_x0020_ foobar _x0020_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT_PWS) public String a = null; } | <X/>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT_PWS) public String a = ""; } | <X>_xE000_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT_PWS) public String a = " "; } | <X> </X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT_PWS) public String a = " "; } | <X> </X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.TEXT_PWS) public String a = " foobar "; } | <X> foobar </X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED) public String[] a = null; } | <X/>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED) public String[] a = {""}; } | <X>_xE000_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED) public String[] a = {" "}; } | <X>_x0020_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED) public String[] a = {" "}; } | <X>_x0020__x0020_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED) public String[] a = {" foobar "}; } | <X>_x0020_ foobar _x0020_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED_PWS) public String[] a = null; } | <X/>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED_PWS) public String[] a = {""}; } | <X>_xE000_</X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED_PWS) public String[] a = {" "}; } | <X> </X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED_PWS) public String[] a = {" "}; } | <X> </X>
@Bean(typeName="X") class MyBean { @Xml(format=XmlFormat.MIXED_PWS) public String[] a = {" foobar "}; } | <X> foobar </X>

It should be noted that when using MIXED, you are not guaranteed to parse back the exact same content since side-by-side strings in the content will end up concatenated when parsed.

The {@link oaj.xml.annotation.XmlFormat#TEXT} format is similar to {@link oaj.xml.annotation.XmlFormat#MIXED} except it's meant for solitary objects that get serialized as simple child text nodes. Any object that can be serialize to a String can be used. The {@link oaj.xml.annotation.XmlFormat#TEXT_PWS} is the same except whitespace is preserved in the output.

Data type | JSON example | Without annotations | With annotations
--- | --- | --- | ---
class MyBean { @Xml(format=XmlFormat.TEXT) public String a; } | { a: 'foo' } | <object> <a>foo</a> </object> | <object>foo</object>

The {@link oaj.xml.annotation.XmlFormat#XMLTEXT} format is similar to {@link oaj.xml.annotation.XmlFormat#TEXT} except it's meant for strings containing XML that should be serialized as-is to the document. Any object that can be serialize to a String can be used. During parsing, the element content gets parsed with the rest of the document and then re-serialized to XML before being set as the property value. This process may not be perfect (e.g. double quotes may be replaced by single quotes, etc...).

Data type | JSON example | With TEXT annotation | With XMLTEXT annotation
--- | --- | --- | ---
class MyBean { @Xml(format=XmlFormat.XMLTEXT) public String a; } | { a: 'Some <b>XML</b> text' } | <object>Some &lt;b&gt;XML&lt;/b&gt; text</object> | <object>Some <b>XML</b> text</object>