June 24, 2011

Using JAXB's @XmlAccessorType to Configure Field or Property Access

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

This access type is useful when your domain object has many fields/properties and you only want to map a few to XML.   Handling this use case with the other access types would involve using @XmlTransient to exclude fields/properties from the XML mapping.

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;

14 comments:

  1. Hi Blaise,
    could 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

    ReplyDelete
  2. Many thanks! this saved me

    @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!

    ReplyDelete
  3. really nice article .. after all, i also got cleared btw field and a property :)

    ReplyDelete
  4. Still it is not clear to me :(
    Some attribute is getting generated like attributes in xml and some are getting generated like child-element of 'root' element!!!
    What's going on?

    ReplyDelete
    Replies
    1. 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.

      Delete
  5. Thank you for this explanation!

    ReplyDelete
  6. does 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!!

    ReplyDelete
    Replies
    1. This 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.

      Delete
  7. Nice Explanation..It really helped me.

    ReplyDelete
  8. Hi,

    Thanks 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.

    ReplyDelete
    Replies
    1. 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.

      Delete
  9. Thanks, really nice..

    ReplyDelete