
Question:
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
Answer1:<strong>Note:</strong> I'm the <strong><a href="http://www.eclipse.org/eclipselink/moxy.php" rel="nofollow">EclipseLink JAXB (MOXy)</a></strong> lead and a member of the <a href="http://jcp.org/en/jsr/detail?id=222" rel="nofollow"><strong>JAXB (JSR-222)</strong></a> expert group.
In <a href="https://stackoverflow.com/a/11218017/383861" rel="nofollow"><strong>another answer</strong></a> 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:
<blockquote>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.
</blockquote><strong>Root</strong>
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;
}
}
<strong>jaxb.properties</strong>
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 <a href="http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html" rel="nofollow">Specifying EclipseLink MOXy as Your JAXB Provider</a>):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
<strong>binding-acb.xml</strong>
MOXy has an external mapping document extension that allows you to override the mappings on the domain model (see <a href="http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html" rel="nofollow">Extending JAXB - Representing Metadata as XML</a>). 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>
<strong>binding-cab.xml</strong>
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>
<strong>Demo</strong>
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);
}
}
<strong>Output</strong>
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>
Answer2:<a href="http://jcp.org/en/jsr/detail?id=222" rel="nofollow"><strong>JAXB (JSR-222)</strong></a> 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.
<strong>OPTION #1 - @XmlType(propOrder={"c","b", "a"})</strong>
The propOrder
property on the @XmlType
annotation allows you to specify an order.
<em><strong>Root</strong></em>
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;
}
}
<em><strong>Output</strong></em>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<c>Baz</c>
<b>Bar</b>
<a>Foo</a>
</root>
<em><strong>For More Information</strong></em>
<ul><li><a href="http://blog.bdoughan.com/2012/02/jaxbs-xmltype-and-proporder.html" rel="nofollow">http://blog.bdoughan.com/2012/02/jaxbs-xmltype-and-proporder.html</a></li> </ul><strong>OPTION #2 - @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)</strong>
You can also use the @XmlAccessorOrder
annotation to specify that the properties should be marshalled in alphabetical order.
<em><strong>Root</strong></em>
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;
}
}
<em><strong>Output</strong></em>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<a>Foo</a>
<b>Bar</b>
<c>Baz</c>
</root>
<strong>DEMO CODE</strong>
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);
}
}