JAXB offers a lot of flexibility when interacting with your object model. One area is configuring the use of fields or properties to access the data in your domain objects. This is specified as an XmlAccessType (PUBLIC_MEMBER, PROPERTY, FIELD, or NONE) via the @XmlAccessorType annotation. In this post we'll examine what these options really mean.
Java Model
The following will be used for our domain model. It has been contrived for demonstrating the concepts described in this post, and is not mean to represent best practices for object-to-XML mapping. We will vary the XmlAccessType set on the @XmlAccessorType annotation between runs.
package blog.xmlaccessortype; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Root { private String fieldA; @XmlAttribute private String fieldB; public String fieldC; @XmlAttribute public String getPropertyA() { return fieldA; } public void setPropertyA(String a) { this.fieldA = a; } public String getPropertyB() { return fieldB; } public void setPropertyB(String b) { this.fieldB = b; } }
Demo Code
The following demo code will be used for this post.
package blog.xmlaccessortype; 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.setA("a"); root.setB("b"); root.fieldC = "c"; Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(root, System.out); } }
XmlAccessType.PUBLIC_MEMBER
PUBLIC_MEMBER is the default access type in JAXB. It means that a JAXB implementation will generate bindings for:
- public fields
- annotated fields
- properties
When to Use
This access type will support most of your use cases. Check out the descriptions of the other access types for use cases where you may consider switching.
Java Model
package blog.xmlaccessortype; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER) public class Root { private String fieldA; @XmlAttribute private String fieldB; public String fieldC; @XmlAttribute public String getPropertyA() { return fieldA; } public void setPropertyA(String a) { this.fieldA = a; } public String getPropertyB() { return fieldB; } public void setPropertyB(String b) { this.fieldB = b; } }
XML Output
<?xml version="1.0" encoding="UTF-8"?> <root fieldB="b" propertyA="a"> <fieldC>c</fieldC> <propertyB>b</propertyB> </root>
XmlAccessType.PROPERTY
When the PROPERTY access type is used, JAXB implementations will generate bindings for:
- annotated fields
- properties
When to Use
This access type is very similar to PUBLIC_MEMBER. The primary reason to switch to PROPERTY is to guard against any public fields that may be in your domain model.
There are advantages to using PROPERTY access over FIELD access when using your domain objects with JPA implementations that may alter the byte codes of your domain classes:
- Some JPA implementations inject byte code into the properties to trigger "lazy loading". If you use the FIELD access type your JAXB marshal operation will not bring in this data.
- Some JPA implementations inject fields to support such things as change tracking. If you use FIELD access you may find your JAXB implementation complaining about fields that you did not explicitly add to your domain model.
Java Model
package blog.xmlaccessortype; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement @XmlAccessorType(XmlAccessType.PROPERTY) public class Root { private String fieldA; @XmlAttribute private String fieldB; public String fieldC; @XmlAttribute public String getPropertyA() { return fieldA; } public void setPropertyA(String a) { this.fieldA = a; } public String getPropertyB() { return fieldB; } public void setPropertyB(String b) { this.fieldB = b; } }
XML Output
<?xml version="1.0" encoding="UTF-8"?> <root fieldB="b" propertyA="a"> <propertyB>b</propertyB> </root>
XmlAccessType.FIELD
The use of access type FIELD will cause JAXB implementations to create bindings for:
- fields
- annotated properties
When to Use
I tend to use this access type in my examples as it allows me to omit the properties to save space. In reality this access type is most useful in scenarios where not all of the fields are exposed through properties.
Java Model
package blog.xmlaccessortype; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Root { private String fieldA; @XmlAttribute private String fieldB; public String fieldC; @XmlAttribute public String getPropertyA() { return fieldA; } public void setPropertyA(String a) { this.fieldA = a; } public String getPropertyB() { return fieldB; } public void setPropertyB(String b) { this.fieldB = b; } }
XML Output
<?xml version="1.0" encoding="UTF-8"?> <root fieldB="b" propertyA="a"> <fieldA>a</fieldA> <fieldC>c</fieldC> </root>
XmlAccessType.NONE
When access type NONE is used JAXB will create bindings for:
- annotated fields
- annotated properties
When to Use
Java Model
package blog.xmlaccessortype; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) public class Root { private String fieldA; @XmlAttribute private String fieldB; public String fieldC; @XmlAttribute public String getPropertyA() { return fieldA; } public void setPropertyA(String a) { this.fieldA = a; } public String getPropertyB() { return fieldB; } public void setPropertyB(String b) { this.fieldB = b; } }
XML Output
<?xml version="1.0" encoding="UTF-8"?> <root fieldB="b" propertyA="a"/>
Access Type and Inheritance
The access type specified on a class will be inherited by all its subclasses. @XmlAccessorType can be used to override the access type that was configured on the parent class.
Overriding the Default Access Type
The @XmlAccessorType annotation can be used at the type level to configure a single class, or at the package level to change the default access type for all classes within that package (individual classes can override the access type). Package level annotations can be supplied through a package-info class.
@XmlAccessorType(XmlAccessType.FIELD) package blog.xmlaccessortype; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType;
Hi Blaise,
ReplyDeletecould you please show an example to map composite key (PK) as an inline in JAXB?
For example:
public class FreeUnit implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
protected FreeUnitPK freeUnitPK;
}
I would like to have fields of FreeUnitPK inline in XML of FreeUnit.
Regards,
tinku
Many thanks! this saved me
ReplyDelete@XmlAccessorType(XmlAccessType.FIELD)
package blog.xmlaccessortype;
After many searches, I couldn't suppress members from a third party parent class. I ended up adding a package-info.java file for that package in my codebase and that solved it!
really nice article .. after all, i also got cleared btw field and a property :)
ReplyDeleteStill it is not clear to me :(
ReplyDeleteSome attribute is getting generated like attributes in xml and some are getting generated like child-element of 'root' element!!!
What's going on?
One of the things I'm trying to demonstrate with this post is that JAXB considers the fields and corresponding properties as separate mappable entities.
DeleteThank you for this explanation!
ReplyDeletedoes this work with JSON, because I have classes with these annotation and when I retrieve using JSON it seem to ignore the annotation and returns all the attribute!!
ReplyDeleteThis definitely works with EclipseLink JAXB (MOXy)'s JSON-binding (see: JSON Binding with EclipseLink MOXy - Twitter Example). Other JSON-binding providers only support portions of JAXB annotations and may not support @XmlAccessorType.
DeleteNice Explanation..It really helped me.
ReplyDeletethanks a million
ReplyDeleteHi,
ReplyDeleteThanks for this article, it really clarifies this issue.
But I am still having a problem, and I thought maybe you could help me to understand it:
I have overrided the Default Access Type to @XmlAccessorType(XmlAccessType.NONE) in my whole package, and then, I guess, that only my annontated fields/properties will be mapped. But this is not happening, I get one of them duplicated, and it is because it is mapping also the getter (which I haven't annotated).
Of course using the @XmlTransient annotation, I can fix it and everything works properly, but I am curious and still want to know what's going on and why having the @XmlAccessorType(XmlAccessType.NONE) annotation, I have anyway to use @XmlTransient to make it work.
And again, thank you so much for the post. I think many people have benefited from it.
If you have specified accessor type at the package level with a package-info class and it's not working then your package-info.java may not be compiling.
DeleteThanks, really nice..
ReplyDeleteGood Article..Thank You
ReplyDelete