JAXB generic #XmlValue - java

The goal is to produce the following XML with JAXB
<foo>
<bar>string data</bar>
<bar>binary data</bar>
</foo>
Is there a workaround to allow generic #XmlValue fields (I need to store byte[] and String data)? Below is what I desire:
#XmlRootElement
public class Foo {
private #XmlElement List<Bar> bars;
}
#XmlRootElement
public class Bar<T> {
private #XmlValue T value; // (*)
}
But I get this exception
(*) IllegalAnnotationException:
#XmlAttribute/#XmlValue need to reference a Java type that maps to text in XML.

You could leverage an XmlAdapter for this use case instead of #XmlValue:
BarAdapter
package forum8807296;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class BarAdapter extends XmlAdapter<Object, Bar<?>> {
#Override
public Bar<?> unmarshal(Object v) throws Exception {
if(null == v) {
return null;
}
Bar<Object> bar = new Bar<Object>();
bar.setValue(v);
return bar;
}
#Override
public Object marshal(Bar<?> v) throws Exception {
if(null == v) {
return null;
}
return v.getValue();
}
}
Foo
The XmlAdapter is associated with the bars property using the #XmlJavaTypeAdapter annotation:
package forum8807296;
import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Foo {
private List<Bar> bars;
#XmlElement(name="bar")
#XmlJavaTypeAdapter(BarAdapter.class)
public List<Bar> getBars() {
return bars;
}
public void setBars(List<Bar> bars) {
this.bars = bars;
}
}
Bar
package forum8807296;
public class Bar<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
Demo
You can test this example using the following demo code:
package forum8807296;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
List<Bar> bars = new ArrayList<Bar>();
foo.setBars(bars);
Bar<String> stringBar = new Bar<String>();
stringBar.setValue("string data");
bars.add(stringBar);
Bar<byte[]> binaryBar = new Bar<byte[]>();
binaryBar.setValue("binary data".getBytes());
bars.add(binaryBar);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
Output
Note how the output includes the xsi:type attributes to preserve the type of the value. You can eliminate the the xsi:type attribute by having your XmlAdapter return String instead of Object, if you do this you will need handle the conversion from String to the appropriate type yourself for the unmarshal operation:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo>
<bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">string data</bars>
<bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:base64Binary">YmluYXJ5IGRhdGE=</bars>
</foo>

I couldn't get #XmlValue working as I always got NullPointerException along the way—not sure why. I came up with something like the following instead.
Drop your Bar class entirely, because, as you want it to be able to contain anything you can simply represent it with Object.
#XmlRootElement(name = "foo", namespace = "http://test.com")
#XmlType(name = "Foo", namespace = "http://test.com")
public class Foo {
#XmlElement(name = "bar")
public List<Object> bars = new ArrayList<>();
public Foo() {}
}
Without telling JAXB which namespaces your types are using every bar element inside a foo would contain separate namespace declarations and stuff—the package-info.java and all the namespace stuff serves only fancification purposes only.
#XmlSchema(attributeFormDefault = XmlNsForm.QUALIFIED,
elementFormDefault = XmlNsForm.QUALIFIED,
namespace = "http://test.com",
xmlns = {
#XmlNs(namespaceURI = "http://test.com", prefix = ""),
#XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),
#XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema", prefix = "xs")})
package test;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Running this simple test would spout-out something similar to your XML snippet.
public static void main(String[] args) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
foo.bars.add("a");
foo.bars.add("b".getBytes());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(foo, System.out);
}
Output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://test.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<bar xsi:type="xs:string">a</bar>
<bar xsi:type="xs:base64Binary">Yg==</bar>
</foo>

Is there a reason you don't simply construct a String with your byte[]? Do you truly need a generic?

The trick I'm usually using is to create schema with types you want and then use xjc to generate Java classes and see how annotations are used. :) I believe in XML schema proper type mapping for byte[] is 'base64Binary', so creating schema like this:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/NewXMLSchema" xmlns:tns="http://www.example.org/NewXMLSchema" elementFormDefault="qualified">
<element name="aTest" type="base64Binary"></element>
</schema>
and running xjc we would get following code generated:
#XmlElementDecl(namespace = "http://www.example.org/NewXMLSchema", name = "aTest")
public JAXBElement<byte[]> createATest(byte[] value) {
return new JAXBElement<byte[]>(_ATest_QNAME, byte[].class, null, ((byte[]) value));
}

Related

JAXB Marshall String to base64

I need to marshall an object which includes a String variable.
The String var. contains an XML document, and it gets marshalled with escaping to XMLElement.
I'd like to marshall the String variable to base64 format, and back to String on unmarshall.
Is this posiible?
You can use an XmlAdapter to convert the String to/from a byte[] during the marshalling/unmarshalling process. By default JAXB will represent a byte[] as base64Binary.
XmlAdapter (Base64Adapter)
Below is an XmlAdapter that will convert between a String and a byte[].
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class Base64Adapter extends XmlAdapter<byte[], String> {
#Override
public String unmarshal(byte[] v) throws Exception {
return new String(v);
}
#Override
public byte[] marshal(String v) throws Exception {
return v.getBytes();
}
}
Java Model (Foo)
The XmlAdapter is configured using the #XmlJavaTypeAdapter annotation.
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Foo {
private String bar;
#XmlJavaTypeAdapter(Base64Adapter.class)
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
Demo
In the demo code below we will create an instance of Foo and marshal it to XML.
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
foo.setBar("<abc>Hello World</abc>");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
Output
Below is the output from running the demo code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo>
<bar>PGFiYz5IZWxsbyBXb3JsZDwvYWJjPg==</bar>
</foo>

Handling missing nodes with JAXB

I am currently using JAXB to parse xml files. I generated the classes needed through an xsd file. However, the xml files I receive do not contain all the nodes declared in the generated classes. The following is an example of my xml file's structure:
<root>
<firstChild>12/12/2012</firstChild>
<secondChild>
<firstGrandChild>
<Id>
</name>
<characteristics>Description</characteristics>
<code>12345</code>
</Id>
</firstGrandChild>
</secondChild>
</root>
I am confronted with the following two cases :
The node <name> is present in the generated classes but not in the XML files
The node has no value
In both cases, the value is set to null. I would like to be able to differentiate when the node is absent from the XML file and when it's present but has a null value. Despite my searches, I didn't figure out a way to do so. Any help is more than welcome
Thank you so much in advance for your time and help
Regards
A JAXB (JSR-222) implementation won't call the set method for absent nodes. You could put logic in your set method to track whether or not it has been called.
public class Foo {
private String bar;
private boolean barSet = false;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
this.barSet = true;
}
}
UPDATE
JAXB will also treat empty nodes as having a value of empty String.
Java Model
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Root {
private String foo;
private String bar;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum15839276/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<foo></foo>
</root>

JAXB - element value field

I have a class, that is something like this:
public class Property {
private double floorArea;
public double getFloorArea() {
return floorArea;
}
#XmlElement
public void setFloorArea(double floorArea) {
this.floorArea = floorArea;
}
}
Which will give me something like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<property>
<floorArea>x</floorArea>
</property>
But I need something like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<property>
<floorArea>
<value>x</value>
</floorArea>
</property>
The API I am using requires it this way. My limited JAXB knowledge is preventing me from figuring this out. Any help is appreciated.
EDIT:
something I am researching. Would I need to create a value class with its own JAXB annotations for this to work? (and set floorArea to the type of value)?
Below is how your use case could be supported with an XmlAdapter using any JAXB (JSR-222) implementation.
XmlAdapter (DoubleValueAdapter)
An XmlAdapter is a mechanism that allows an object to be converted to another type of object . Then it is the converted object that is converted to/from XML.
package forum14045961;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DoubleValueAdapter extends XmlAdapter<DoubleValueAdapter.AdaptedDoubleValue, Double>{
public static class AdaptedDoubleValue {
public double value;
}
#Override
public AdaptedDoubleValue marshal(Double value) throws Exception {
AdaptedDoubleValue adaptedDoubleValue = new AdaptedDoubleValue();
adaptedDoubleValue.value = value;
return adaptedDoubleValue;
}
#Override
public Double unmarshal(AdaptedDoubleValue adaptedDoubleValue) throws Exception {
return adaptedDoubleValue.value;
}
}
Property
The #XmlJavaTypeAdapter annotation is used to specify the XmlAdapter. I needed to change double to Double so I moved the mapping to the field as to not affect the public API.
package forum14045961;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Property {
#XmlJavaTypeAdapter(DoubleValueAdapter.class)
private Double floorArea;
public double getFloorArea() {
return floorArea;
}
public void setFloorArea(double floorArea) {
this.floorArea = floorArea;
}
}
Demo
Below is some demo code to prove that everything works.
package forum14045961;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Property.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14045961/input.xml");
Property property = (Property) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(property, System.out);
}
}
input.xml/Output
Below is the input to and output from the demo code.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<property>
<floorArea>
<value>1.23</value>
</floorArea>
</property>
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Below is how you could map your use case using MOXy's #XmlPath extension:
Property
package forum14045961;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
public class Property {
private double floorArea;
public double getFloorArea() {
return floorArea;
}
#XmlPath("floorArea/value/text()")
public void setFloorArea(double floorArea) {
this.floorArea = floorArea;
}
}
jaxb.properties
To specify MOXy as your JAXB (JSR-222) provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
Only the standard JAXB runtime APIs are required to read the objects from and write them back to XML when MOXy is used as the JAXB provider.
package forum14045961;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Property.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14045961/input.xml");
Property property = (Property) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(property, System.out);
}
}
input.xml/Output
Below is the input to and output from running the demo code.
<?xml version="1.0" encoding="UTF-8"?>
<property>
<floorArea>
<value>1.23</value>
</floorArea>
</property>
For More Information
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
Your hunch is correct the way described will generate the xml as you have displayed.
public class Property {
#XmlElement(required = true)
protected FloorArea floorArea;
public FloorArea getFloorArea() {
return floorArea;
}
public void setFloorArea(FloorArea value) {
this.floorArea = value;
}
}
And your FloorArea class would look something like the code snapshot below.
public class FloorArea {
#XmlElement(required = true)
protected String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

JAXB: how to append sub-tree to an object and create a full tree

I have one sub-tree that I would like to append on an object and make JAXB marshall the all thing as a single tree (and with appropriate tags). But currently, the root tag of the sub-tree is replaced by the tag of another object
Unfortunately, I am not allowed to publish the original code here, so I reproduced my problem in a test code (so bear with me if you find this dumb).
The idea is that I would like to output the following structure:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:Root xmlns:ns2="urn:my:foo:bar:1.0" xmlns:ns3="urn:other:foo:bar:1.1">
<Content>
<Header>
<ns3:Leaf/>
</Header>
</Content>
</ns2:Root>
but currently, all I get is this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:Root xmlns:ns2="urn:my:foo:bar:1.0" xmlns:ns3="urn:other:foo:bar:1.1">
<Content>
<Header/>
</Content>
</ns2:Root>
I have two XSD's to generate all the necessary classes, so I am ok on that side (but since those classes are generated, I cannot modify them).
Here is a sample code that produces the second XML (the wrong one):
package foo.bar;
import java.io.OutputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
public class Test {
private JAXBContext context;
public Test() throws JAXBException {
context = JAXBContext.newInstance(RootElement.class, LeafElement.class);
}
#XmlRootElement(name = "Root", namespace = "urn:my:foo:bar:1.0")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Root", propOrder = { "content" })
public static class RootElement {
#XmlElement(name = "Content")
protected ContentElement content;
public ContentElement getContent() {
return content;
}
public void setContent(ContentElement content) {
this.content = content;
}
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Content", propOrder = { "dummy" })
public static class ContentElement {
#XmlElement(name = "Header")
protected Object dummy;
public Object getDummy() {
return dummy;
}
public void setDummy(Object dummy) {
this.dummy = dummy;
}
}
#XmlRootElement(name = "Leaf", namespace = "urn:other:foo:bar:1.1")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Leaf")
public static class LeafElement {
}
public Node marshal(Object obj) throws JAXBException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = null;
try {
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.newDocument();
} catch (ParserConfigurationException ex) {
throw new JAXBException(ex);
}
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(obj, doc);
return doc.getDocumentElement();
}
public void marshal(Object obj, OutputStream stream) throws JAXBException {
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(obj, stream);
}
public void test() throws JAXBException {
RootElement root = new RootElement();
ContentElement content = new ContentElement();
root.setContent(content);
LeafElement leaf = new LeafElement();
content.setDummy(marshal(leaf));
marshal(root, System.out);
}
public static void main(String[] args) throws JAXBException {
new Test().test();
}
}
In that code you find 3 "marshallable" classes:
RootElement,
ContentElement and
LeafElement.
The first two classes come from one XSD (with a given namespace) and the last one comes from another XSD (with another namespace), as illustrated in the sample code.
So far, all I found to fix this, was to create an additional class that would be set as dummy on the ContentElement and would itself hold the LeafElement, so that JAXB creates the appropriate intermdiate Node. But I find this solution quite ugly, not really maintainable and was hoping that JAXB had some way to handle such cases.
If you need more info, or you need me to re-formulate my question, don't hesitate. I am having a hard time to explain my problem with simple words.
Constraints are the following:
I cannot modify RootElement, ContentElement nor LeafElement
I cannot use something else than JAXB
If you cannot change any element class, then you must create a holder object to leaf.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public static class LeafElementHolder {
#XmlAnyElement
private Object leaf;
public Object getLeaf() {
return leaf;
}
public void setLeaf(Object leaf) {
this.leaf = leaf;
}
}
Add this class to context
public Test() throws JAXBException {
context = JAXBContext.newInstance(RootElement.class, LeafElement.class, LeafElementHolder.class);
}
and use this in your test() method
LeafElement leaf = new LeafElement();
LeafElementHolder holder = new LeafElementHolder();
holder.setLeaf(leaf);
content.setDummy(marshal(holder));
You have 4 elements in XML so you must have 4 classes in Java.
ns2:Root
ns2:Content
ns2:Header
ns3:Leaf

reordering XML tags

I am trying to implement something which is for writing back the content tree of java object to XML file (object marshalling) (I know there are a lot of APIs for doing that but its required from me), I want to let the user to reorder the tags as he/she wants, I know using annotation like what JAXB has may solve that, but I think using annotation may cause a lot of pain. it will be so helpful if any one could offer any good approach.
Thanks
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
In another answer I described the standard JAXB mechanisms for specifying the order of elements. In this answer I will explain how MOXy's external mapping document can be used to address this part of your question:
I want to let the user to reorder the tags as he/she wants, I know
using annotation like what JAXB has may solve that, but I think using
annotation may cause a lot of pain.
Root
In the Root class I have used the #XmlType annotation to specify an ordering.
package forum11217734;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlType(propOrder={"c", "b", "a"})
public class Root {
private String a;
private String b;
private String c;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to add a file called jaxb.properties in the same package as your domain model with the following entry (see Specifying EclipseLink MOXy as Your JAXB Provider):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
binding-acb.xml
MOXy has an external mapping document extension that allows you to override the mappings on the domain model (see Extending JAXB - Representing Metadata as XML). We will use this document to specify another ordering.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum11217734">
<java-types>
<java-type name="Root">
<xml-type prop-order="a c b"/>
</java-type>
</java-types>
</xml-bindings>
binding-cab.xml
We can use additional mapping documents to provide alternate orderings.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum11217734">
<java-types>
<java-type name="Root">
<xml-type prop-order="c a b"/>
</java-type>
</java-types>
</xml-bindings>
Demo
The following demo code demonstrates how to leverage the external mapping document when creating a JAXBContext. We will marshal the same instance of Root three different ways.
package forum11217734;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Root root = new Root();
root.setA("Foo");
root.setB("Bar");
root.setC("Baz");
// CBA
JAXBContext cbaContext = JAXBContext.newInstance(Root.class);
Marshaller cbaMarshaller = cbaContext.createMarshaller();
cbaMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
cbaMarshaller.marshal(root, System.out);
// ACB
Map<String, Object> acbProperties = new HashMap<String, Object>(1);
acbProperties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum11217734/binding-acb.xml");
JAXBContext acbContext = JAXBContext.newInstance(new Class[] {Root.class}, acbProperties);
Marshaller acbMarshaller = acbContext.createMarshaller();
acbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
acbMarshaller.marshal(root, System.out);
// CAB
Map<String, Object> cabProperties = new HashMap<String, Object>(1);
cabProperties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum11217734/binding-cab.xml");
JAXBContext cabContext = JAXBContext.newInstance(new Class[] {Root.class}, cabProperties);
Marshaller cabMarshaller = cabContext.createMarshaller();
cabMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
cabMarshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<c>Baz</c>
<b>Bar</b>
<a>Foo</a>
</root>
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>Foo</a>
<c>Baz</c>
<b>Bar</b>
</root>
<?xml version="1.0" encoding="UTF-8"?>
<root>
<c>Baz</c>
<a>Foo</a>
<b>Bar</b>
</root>
JAXB (JSR-222) implementations provide a couple different mechanisms for specifying the order of XML elements when then content is marshalled to XML. JAXB does not require the elements be in order when unmarshalling.
OPTION #1 - #XmlType(propOrder={"c","b", "a"})
The propOrder property on the #XmlType annotation allows you to specify an order.
Root
package forum11217734;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlType(propOrder={"c","b", "a"})
public class Root {
private String a;
private String b;
private String c;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<c>Baz</c>
<b>Bar</b>
<a>Foo</a>
</root>
For More Information
http://blog.bdoughan.com/2012/02/jaxbs-xmltype-and-proporder.html
OPTION #2 - #XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
You can also use the #XmlAccessorOrder annotation to specify that the properties should be marshalled in alphabetical order.
Root
package forum11217734;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class Root {
private String a;
private String b;
private String c;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<a>Foo</a>
<b>Bar</b>
<c>Baz</c>
</root>
DEMO CODE
The following demo code was used to produce the output for each of the options above.
package forum11217734;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.setA("Foo");
root.setB("Bar");
root.setC("Baz");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}

Categories