Can you change the default value of the nillable attribute in JAXB? - java

I am refactoring some code to use JAXB and reflection to output code to the client, it is currently using an XMLWriter and manually creating the tags each time.
The problem I am having is that due to constraints on the client side, I need to have empty elements in the XML for any null fields in the java class.
While I realize this problem can be solved by adding nillable=true to each JAXB XmlElement annotation, that is not the most practical, as I have a lot of those annotations.
I was hoping to find a way to set nillable=true as a global attribute (or as the default value). This would also make it easier for future colleagues to work on it, as they won't need to remember that every annotation should include the nillable attribute.
I haven't found much besides descriptions of the default behavior. I find it surprising that no one else has posted a similar question in the past. From what I have found, it doesn't seem to me that there is any built-in support for making the default configurable. Is this something that might be solved with a custom JAXB implementation or maybe a third party JAXB implementation?

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
I have entered an enhancement request to have this behaviour added to EclipseLink JAXB (MOXy):
http://bugs.eclipse.org/368547
WORK AROUND
As a work around if all your mapped String fields/properties are mapped to XML elements then the following XmlAdapter approach may work for you:
NullStringAdapter
This XmlAdapter will marshal instances of String as an object called AdaptedString. AdaptedString contains the String value as well as a field mapped to the xsi:nil attribute. In the XmlAdapter we will set the value of that field based on whether or not the String value is null.
package forum8841221;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;
public class NullStringAdapter extends XmlAdapter<NullStringAdapter.AdaptedString, String> {
#Override
public AdaptedString marshal(String v) throws Exception {
AdaptedString adaptedString = new AdaptedString();
if(null == v) {
adaptedString.nil = true;
}
adaptedString.value = v;
return adaptedString;
}
#Override
public String unmarshal(AdaptedString v) throws Exception {
return v.value;
}
public static class AdaptedString {
#XmlAttribute(namespace="http://www.w3.org/2001/XMLSchema-instance")
public Boolean nil;
#XmlValue
#XmlJavaTypeAdapter(VoidStringAdapter.class)
public String value;
}
public static class VoidStringAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String v) throws Exception {
return v;
}
#Override
public String unmarshal(String v) throws Exception {
return v;
}
}
}
package-info
We can register that we want this XmlAdapter to apply to all the mapped String fields/properties on this package by registering the XmlAdapter at the package level.
#XmlJavaTypeAdapter(value=NullStringAdapter.class, type=String.class)
#XmlSchema(xmlns={#XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")})
package forum8841221;
import javax.xml.bind.annotation.adapters.*;
import javax.xml.bind.annotation.*;
Root
Below is the domain class I have used for this example. It has several String properties, one of them is annotated with #XmlElement(nillable=true)
package forum8841221;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Root {
private String a;
private String b;
private String c;
private String d;
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;
}
#XmlElement(nillable=true)
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
}
Demo
package forum8841221;
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 root = new Root();
root.setB("B");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<a xsi:nil="true"/>
<b>B</b>
<c xsi:nil="true"/>
<d xsi:nil="true"/>
</root>

Related

Why is the ObjectFactory not used during unmarshalling?

I have defined the following ObjectFactory:
#XmlRegistry
public class ObjectFactory {
public Dogs createDogs() {
return new Dogs();
}
#XmlElementDecl(name = "dog")
public Dog createDog(DogType value) {
return new Dog(value);
}
#XmlElementDecl(name = "fido", substitutionHeadName = "dog", substitutionHeadNamespace = "")
public Dog createFido(DogType value) {
return new Dog("fido", value);
}
#XmlElementDecl(name = "barks", substitutionHeadName = "dog", substitutionHeadNamespace = "")
public Dog createBarks(DogType value) {
return new Dog("barks", value);
}
}
(Dogs class is trivial, Dog and DogType see below or here.)
I am unmarshalling the following XML:
<listOfDogs>
<dogs>
<dog>
<name>henry</name>
<sound>bark</sound>
</dog>
<fido>
<sound>woof</sound>
</fido>
<barks>
<sound>miau</sound>
</barks>
</dogs>
</listOfDogs>
I was sincerely expecting that JAXB will call my createFido(...) and createBarks(...) methods during unmarshalling. But this does not happen. The Dog constructor is invoked directly via reflection, the appropriate create... methods are not used.
My question is:
Why is ObjectFactory not called during unmarshalling?
Shouldn't it be? Or is ist just a dummy to hold the #XmlRegistry/#XmlElementDecl declarations?
I have also checked this question:
What is the ObjectFactory role during JAXB-Unmarshalling?
The solution there is to use #XmlType.factoryClass and factoryMethod. This will not work here because I don't want to statically link my DogType to the certain instantiation routine. I want it to be decided in the runtime based on the element name. My goal is to instantiate the same class but differently, depending on the element name.
Now some code to make it complete.
Root element class:
#XmlRootElement(name = "listOfDogs")
public class Dogs {
private List<JAXBElement<DogType>> dogs = new LinkedList<JAXBElement<DogType>>();
#XmlElementWrapper(name = "dogs")
#XmlElementRef(name = "dog")
public List<JAXBElement<DogType>> getDogs() {
return this.dogs;
}
#Override
public String toString() {
return "Dogs [dogs=" + dogs + "]";
}
}
Dog, the wrapper element class for the DogType:
public class Dog extends JAXBElement<DogType> {
public static final QName NAME = new QName("dog");
private static final long serialVersionUID = 1L;
public Dog(DogType value) {
super(NAME, DogType.class, value);
}
public Dog(String dogName, DogType value) {
super(NAME, DogType.class, value);
}
#Override
public QName getName() {
final DogType value = getValue();
if (value != null && value.getName() != null) {
return new QName(value.getName());
} else {
return super.getName();
}
}
}
DogType:
public class DogType {
private String name;
private String sound;
public String getName() {
return name;
}
public void setName(String dogName) {
this.name = dogName;
}
public String getSound() {
return sound;
}
public void setSound(String sound) {
this.sound = sound;
}
}
Test:
public class DogTest {
#Test
public void unmarshallsDogs() throws JAXBException {
final JAXBContext context = JAXBContext
.newInstance(ObjectFactory.class);
final Dogs dogs = (Dogs) context.createUnmarshaller().unmarshal(
getClass().getResource("dogs.xml"));
Assert.assertEquals(3, dogs.getDogs().size());
// Does not work
// Assert.assertEquals("henry", dogs.getDogs().get(0).getValue()
// .getName());
Assert.assertEquals("bark", dogs.getDogs().get(0).getValue().getSound());
// Does not work
// Assert.assertEquals("fido", dogs.getDogs().get(1).getValue()
// .getName());
Assert.assertEquals("woof", dogs.getDogs().get(1).getValue().getSound());
// Does not work
// Assert.assertEquals("barks", dogs.getDogs().get(2).getValue()
// .getName());
Assert.assertEquals("miau", dogs.getDogs().get(2).getValue().getSound());
}
}
The code is also available on GitHub here and here.
The short answer is because the factory methods are not generated into the #XmlType annotation to tell JAXB to do so:
#XmlRootElement(name = "listOfDogs")
#XmlType(factoryClass=ObjectFactory.class, factoryMethod="createDogs") // not generated
public class Dogs {
Shouldn't it be? Or is ist just a dummy to hold the
#XmlRegistry/#XmlElementDecl declarations?
In my opinion yes it should be used to instantiate the classes.
ObjectFactory is a throw back to JAXB 1.0. In JAXB 1.0 the spec defined what the generated interfaces looked like and implementations could back those generated interfaces with what ever impl they wanted to provide. Back then you needed to use the ObjectFactory class to create your model in a vendor independent way.
JAXB 2.0 switched to a POJO model where you were free to use the default constructor. If JAXB 1.0 had never existed would there be an ObjectFactory class, that's hard to tell. Since it previously existed the ObjectFactory class was kept for a couple of reasons:
It made it easier for people transitioning for people transitioning from JAXB 1.0 to interact with the generated model.
It provided a location to specify the multiple root elements for a class via #XmlElementDecl. The #XmlRegistry annotation is really just a marker annotation used to indicate the class that contains the #XmlElementDecl annotations without restricting it to a class called ObjectFactory.
Your Use Case
Your use case may be able to be achieved with an XmlAdapter, although its not clear to me what logic you are trying to have in the ObjectFactory.
XmlAdapter (DogAdapter)
Your custom logic goes on the XmlAdapter.
import javax.xml.bind.*;
import javax.xml.bind.annotation.adapters.*;
public class DogAdapter extends XmlAdapter<JAXBElement<DogType>, JAXBElement<DogType>> {
#Override
public JAXBElement<DogType> unmarshal(JAXBElement<DogType> v) throws Exception {
return new Dog(v.getName().getLocalPart(), v.getValue());
}
#Override
public JAXBElement<DogType> marshal(JAXBElement<DogType> v) throws Exception {
return v;
}
}
Dogs
The XmlAdapter is referenced from the #XmlJavaTypeAdapter annotation.
import java.util.*;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name = "listOfDogs")
public class Dogs {
private List<JAXBElement<DogType>> dogs = new LinkedList<JAXBElement<DogType>>();
#XmlElementWrapper(name = "dogs")
#XmlElementRef(name = "dog")
#XmlJavaTypeAdapter(DogAdapter.class)
public List<JAXBElement<DogType>> getDogs() {
return this.dogs;
}
#Override
public String toString() {
return "Dogs [dogs=" + dogs + "]";
}
}
ObjectFactory
ObjectFactory is now a dumb class that just holds the #XmlElementDecl annotations:
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
public Dogs createDogs() {
return new Dogs();
}
#XmlElementDecl(name = "dog")
public JAXBElement<DogType> createDog(DogType value) {
return new Dog(value);
}
#XmlElementDecl(name = "fido", substitutionHeadName = "dog", substitutionHeadNamespace = "")
public JAXBElement<DogType> createFido(DogType value) {
return new JAXBElement<DogType>(new QName("fido"), DogType.class, value);
}
#XmlElementDecl(name = "barks", substitutionHeadName = "dog", substitutionHeadNamespace = "")
public JAXBElement<DogType> createBarks(DogType value) {
return new JAXBElement<DogType>(new QName("barks"), DogType.class, value);
}
}
UPDATE
My question, however is more about the specification. According to the
spec, should the create* methods from the ObjectFactory be executed or
not?
In JAXB 2 there is no difference in a model created from scratch versus one generated from an XML Schema. As such you need to look to the spec at what it says about classes. According to what is reference below it comes down to no-arg constructor or a specified factory method.
From section 8.7.1.2 Mapping of the JAXB 2.2 (JSR-222) specification:
a class must have a public or protected no-arg constructor or a
factory method identified by {factoryClass(), factoryMethod()} unless
it is adapted using #XmlJavaTypeAdapter.

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 return null instead empty string

How I can retrieve null value, when unmarshalling, if inside XML attribute value is empty ? Now I make inside my getters checking for null :
public String getLabel() {
if (label.isEmpty()) {
return null;
}
else {
return label;
}
}
But may be exist some other, more elegant way?
Thanks.
I think your XML looks more or less like this:
<myElement></myElement>
This, unfortunately, means, that you are passing an empty string.
If you want to pass null you have two options:
Do not pass this tag at all (your XML should not contain <myElement/> tag at all).
Use xsi:nil.
If using xsi:nil, first you have to declare your xml element (in XSD file) as nilable, like this:
<xsd:element name="myElement" nillable="true"/>
Then, to pass the null value inside XML do this:
<myElement xsi:nil="true"/>
or this:
<myElement xsi:nil="true"></myElement>
This way, JAXB knows, that you are passing null instead of an empty String.
The answer given by npe is a good one, and specifying how you want null represented would be my recommendation as well. To have xsi:nil marshalled you will want to annotate your property as (see Binding to JSON & XML - Handling Null):
#XmlElement(nillable=true)
public String getLabel() {
return label;
}
If you don't want to change your XML representation then you could use an XmlAdapter:
EmptyStringAdapter
package forum10869748;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class EmptyStringAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
if("".equals(v)) {
return null;
}
return v;
}
#Override
public String marshal(String v) throws Exception {
return v;
}
}
Foo
You reference an XmlAdapter through the use of the #XmlJavaTypeAdapter annotation. If you would like this XmlAdapter applied to all Strings then you could register it at the package level (see JAXB and Package Level XmlAdapters).
package forum10869748;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Foo {
private String label;
#XmlJavaTypeAdapter(EmptyStringAdapter.class)
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}
Demo
package forum10869748;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum10869748/input.xml");
Foo foo = (Foo) unmarshaller.unmarshal(xml);
System.out.println(foo.getLabel());
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<label></label>
</foo>
Output
null

Parametrizing JAXB factory methods with class to create

According to the documentation JAXB factory methods do not have arguments. Is there a JAXB implementation that allow me to create a factory method that receives as a parameter the class of the object I need to create ?
It happens that all my JAXB objects follow the same creation pattern (a particular byte code instrumentation), therefore I would like to encapsulate this in one single factory method having as a parameter the class of the JAXB object to create, avoiding in this way the creation of different factory methods for each JAXB class that basically do exactly the same thing.
I found someone asking the same question in an OTN forum: https://forums.oracle.com/forums/thread.jspa?messageID=9969927#9969927, but not a real answer has been proposed yet.
Thanks for any help
This is currently not possible using the standard JAXB APIs. I have entered the following enhancement request to have this behaviour added to EclipseLink JAXB (MOXy):
https://bugs.eclipse.org/363192
MOXy Specific Solution
You could leverage the #XmlCustomizer extension in EclipseLink JAXB (MOXy) to customize how the objects are instantiated. This mechanism is leveraged to tweak MOXy's underlying metadata.
CommonFactory
import java.util.Date;
public class CommonFactory {
public static Object create(Class<?> clazz) {
if(Foo.class == clazz) {
return new Foo(new Date());
} else if(Bar.class == clazz) {
return new Bar(new Date());
}
return null;
}
}
Foo.class
The Foo class is annotated normally except that we will use the #XmlCustomizer annotation to specify a DescriptorCustomizer that we are going to use to tweak MOXy's metadata.
import java.util.Date;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlCustomizer;
#XmlRootElement
#XmlType(factoryClass=CommonFactory.class, factoryMethod="create")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlCustomizer(FactoryCustomizer.class)
public class Foo {
private Date creationDate;
private Bar bar;
// Non-default constructor
public Foo(Date creationDate) {
this.creationDate = creationDate;
}
}
Bar
Again we will use the #XmlCustomizer annotation to reference the same DescriptorCustomizer that we did in the Foo class.
import java.util.Date;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlCustomizer;
#XmlType(factoryClass=CommonFactory.class, factoryMethod="create")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlCustomizer(FactoryCustomizer.class)
public class Bar {
private Date creationDate;
// Non-default constructor
public Bar(Date creationDate) {
this.creationDate = creationDate;
}
}
FactoryCustomizer
MOXy has the concept of an InstantiationPolicy to build new objects. In this example we will swap in our own instance InstantiationPolicy that can use parameterized factory methods:
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.descriptors.InstantiationPolicy;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.sessions.AbstractSession;
public class FactoryCustomizer implements DescriptorCustomizer{
#Override
public void customize(ClassDescriptor descriptor) throws Exception {
descriptor.setInstantiationPolicy(new MyInstantiationPolicy(descriptor));
}
private static class MyInstantiationPolicy extends InstantiationPolicy {
public MyInstantiationPolicy(ClassDescriptor descriptor) {
InstantiationPolicy defaultInstantiationPolicy = descriptor.getInstantiationPolicy();
this.factoryClassName = defaultInstantiationPolicy.getFactoryClassName();
this.factoryClass = defaultInstantiationPolicy.getFactoryClass();
this.methodName = defaultInstantiationPolicy.getMethodName();
}
#Override
public void initialize(AbstractSession session) throws DescriptorException {
super.initialize(session);
}
#Override
protected void initializeMethod() throws DescriptorException {
Class<?>[] methodParameterTypes = new Class[] {Class.class};
try {
this.method = PrivilegedAccessHelper.getMethod(factoryClass, methodName, methodParameterTypes, true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
#Override
public Object buildNewInstance() throws DescriptorException {
Object[] parameters = new Object[] {this.descriptor.getJavaClass()};
try {
return PrivilegedAccessHelper.invokeMethod(method, factory, parameters);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
Demo
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Foo foo = (Foo) unmarshaller.unmarshal(new StringReader("<foo><bar/></foo>"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<creationDate>2011-11-08T12:35:43.198</creationDate>
<bar>
<creationDate>2011-11-08T12:35:43.198</creationDate>
</bar>
</foo>
For More Information
http://blog.bdoughan.com/2011/06/jaxb-and-factory-methods.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

How to marshall an objectgraph with Java XML binding to a specific depth?

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:
Marshall the whole object graph [no problem here]
Marshall an objectgraph until a specific depth!!! [challenge]
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>

Categories