being a newbie to Java XML binding i am facing a challenge.
Let say i have a scenario where my domain model is constructed and i want to marshall this domain to an xml structure.
Now i want to provide different unmarshall path's:
I cannot figure out a good way on how to tackle this without introducing to much complexity. One can make a copy of the domain and manually later that, but that does not feel right. Any other solutions available?
You could leverage XmlAdapter and Marshal.Listener to get this behaviour:
Demo
A Marshal.Listener will be set to keep track of the depth of the tree we are marshalling. Also we will set runtime level XmlAdapters that are aware of the depth listener. These adapters will start returning null when the desired depth has been reached.
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(Root.class);
Root rootA = new Root();
rootA.setName("A");
Root rootB = new Root();
rootB.setName("B");
rootA.setChild(rootB);
Root rootC = new Root();
rootC.setName("C");
rootB.setChild(rootC);
Root rootD = new Root();
rootD.setName("D");
rootC.setChild(rootD);
Root rootE = new Root();
rootE.setName("E");
rootD.setChild(rootE);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
DepthListener depthListener = new DepthListener(3);
marshaller.setListener(depthListener);
marshaller.setAdapter(new RootAdapter(depthListener));
marshaller.marshal(rootA, System.out);
}
}
DepthListener
The purpose of this class is to to keep track of the current depth.
import javax.xml.bind.Marshaller;
public class DepthListener extends Marshaller.Listener {
private int targetDepth;
private int currentDepth = 0;
public DepthListener(int depth) {
this.targetDepth = depth;
}
@Override
public void beforeMarshal(Object source) {
currentDepth++;
}
@Override
public void afterMarshal(Object source) {
currentDepth--;
}
public boolean isMarshalDepth() {
return currentDepth <= targetDepth;
}
}
RootAdapter
The purpose of the XmlAdapter is to start returning null when the desired depth has been reached to stop the marshalling process.
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class RootAdapter extends XmlAdapter<Root, Root> {
private DepthListener depthListener;
public RootAdapter() {
}
public RootAdapter(DepthListener depthListener) {
this.depthListener = depthListener;
}
@Override
public Root unmarshal(Root root) throws Exception {
return root;
}
@Override
public Root marshal(Root root) throws Exception {
if(depthListener != null && !depthListener.isMarshalDepth()) {
return null;
}
return root;
}
}
Root
The following demonstrates how to specify the XmlAdapter on the domain object via the @XmlJavaTypeAdapter annotation:
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
@XmlJavaTypeAdapter(RootAdapter.class)
@XmlType(propOrder={"name", "child"})
public class Root {
private String name;
private Root child;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Root getChild() {
return child;
}
public void setChild(Root report) {
this.child = report;
}
}
Output
The following is the output from the demo code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<name>A</name>
<child>
<name>B</name>
<child>
<name>C</name>
</child>
</child>
</root>