June 30, 2011

JAXB and Complex Types with Simple Content

A common question that appears on the forums is something like: "How do I map an object to an XML element with attributes and text in JAXB?".  The answer is to use JAXB's @XmlValue annotation.  In this post I'll demonstrate how this annotation can be leveraged.

Related Stack Overflow Questions:


With @XmlValue

We will begin by using the @XmlValue annotation.  It is important to note that the @XmlValue annotation can not be used with a class that maps any of its fields/properties to XML elements.

XML Schema

Below is an example of a complex type with simple content.  Essentially this means that the phone-number element will behave similar to an element with type string, except that it may have an attribute called type.

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
    <element name="phone-number">
        <complexType>
            <simpleContent>
                <extension base="string">
                    <attribute name="type" type="string"/>
                </extension>
            </simpleContent>
        </complexType>
    </element>
</schema>

Java Model

In JAXB we use the @XmlValue annotation to map the number property to the text portion of the phone-number element.

package blog.xmlvalue;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;

@XmlRootElement(name="phone-number")
public class PhoneNumber {

    private String type;
    private String number;

    @XmlAttribute
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @XmlValue
    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

}

XML

Below is a sample XML document produced using the PhoneNumber object (with the number property annotated with @XmlValue) that conforms to our target XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<phone-number type="work">555-1234</phone-number>

Without @XmlValue

XML Schema

If we do not use the @XmlValue annotation then the number property will be treated as an XML element.

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
    <element name="phone-number">
        <complexType>
            <sequence>
                <element name="number" type="string"/>
            </sequence>
            <attribute name="type" type="string"/>
        </complexType>
    </element>
</schema>

Java Model

In our Java model we have not annotated the number property so it will be treated as @XmlElement.

package blog.xmlvalue;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="phone-number")
public class PhoneNumber {

    private String type;
    private String number;

    @XmlAttribute
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

}

XML

Below is a sample XML document produced using the PhoneNumber object (with the number property not annotated with @XmlValue) that conforms to our target XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<phone-number type="work">
   <number>555-1234</number>
</phone-number>

Demo Code

The following demo code can be used to run this example:

package blog.xmlvalue;

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(PhoneNumber.class);

        PhoneNumber phoneNumber = new PhoneNumber();
        phoneNumber.setType("work");
        phoneNumber.setNumber("555-1234");

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(phoneNumber, System.out);
    }

}

Further Reading

If you enjoyed this post, then you may also be interested in:

2 comments:

  1. It is good tutorial helped me solve my problem for which I was trying from last 24 hours

    ReplyDelete
  2. Nicely explained...!

    ReplyDelete