XML Output with JAXB 2.0

Elisha Peterson

Abstract: This note covers some basics of JAXB (Java API for XML Binding). See also the Wikipedia entry, the development site, a tutorial by Sun, and a description of JAXB annotations.


Keywords: Java, XML, JAXB, JavaBeans, Binding

Marshalling

Suppose you want to automatically generate an XML file for your object. In JAXB, this process is called "marshalling". The idea is to annotate the appropriate fields that you want to store in the XML file. It handles the file-generation automatically. Here's a simple example:

public class Main {  
    @XmlRootElement
    static class Dog {
        @XmlAttribute String name;
        @XmlElement float height;
        @XmlElement float width;
        Dog(){name="Spot";height=10;width=10;}
    }    
 
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Dog.class);
        jc.createMarshaller().marshal(new Dog(),new FileOutputStream("dog.xml"));
    }
}

Here @XmlRootElement identifies the root element of the XML document tree (DOM), which is required for "well-formed" SML. @XmlAttribute specifies an attribute supplied "inline" with the object definition, while @XmlElement identifies attributes that are specified separately. Running this file produces the following output:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<dog name="Spot">
    <height>10.0</height>
    <width>10.0</width>
</dog>

Automating the Process

You can shorten the code a bit by telling the marshaller to automatically put fields into the output file. The command for this is @XmlAccessorType, as shown in this abbreviated version:

public class Main {  
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    static class Dog {
        @XmlAttribute String name;
        float height;
        float width;
        Dog(){name="Spot";height=10;width=10;}
    }    
 
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Dog.class);
        jc.createMarshaller().marshal(new Dog(),new FileOutputStream("dog.xml"));
    }
}

Working with Vectors of Data

We can easily modify the above code to work with deeper nesting structures and vectors of data. Here is the code for marshalling a "pack" of dogs:

public class Main {  
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    static class Pack {
        Vector<Dog> dog;
 
        Pack(){dog=new Vector<Dog>();dog.add(new Dog());dog.add(new Dog());}
    }
 
    static class Dog {
        @XmlAttribute String name;
        @XmlAttribute float height;
        @XmlAttribute float width;
 
        @XmlElement Coat coat;
 
        Dog(){name="Spot";height=10;width=10;coat=new Coat();}
    }    
 
    static class Coat {
        @XmlElement ColorX color;
 
        @XmlAttribute float thickness;
        @XmlAttribute String type;
 
        public Coat(){color=new ColorX(); thickness=2; type="solid";}
    }
 
    static class ColorX {
        Color col;
 
        public ColorX(){col=new Color(33,33,33);}
 
        @XmlAttribute
        public int getRed(){return col.getRed();}
        public void setRed(int r){col=new Color(r,col.getGreen(),col.getBlue());}
 
        @XmlAttribute
        public int getGreen(){return col.getGreen();}
        public void setGreen(int g){col=new Color(col.getRed(),g,col.getBlue());}
 
        @XmlAttribute
        public int getBlue(){return col.getBlue();}
        public void setBlue(int b){col=new Color(col.getRed(),col.getGreen(),b);}
    }
 
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Pack.class);
        jc.createMarshaller().marshal(new Pack(),new FileOutputStream("dog.xml"));
    }

Note that saving a color in terms of its components requires creating "get" and "set" methods for the color components (making it a "Java bean"). A better approach is to use the @XmlJavaTypeAdapter (discussed later). Here is the resulting XML file, with the pack initialized to contain two of the default dogs:

<pack>
    <dog width="10.0" height="10.0" name="Spot">
     <coat type="solid" thickness="2.0">
     <color red="33" green="33" blue="33"/>
     </coat>
    </dog>
    <dog width="10.0" height="10.0" name="Spot">
     <coat type="solid" thickness="2.0">
     <color red="33" green="33" blue="33"/>
     </coat>
    </dog>
</pack>

Unmarshalling

The process of returning the XML code into objects is called "unmarshalling". If the XML file doggin.xml is edited, maintaining the same structure, then one can read the data back in just as easily. Here is the code to read the dogs back into a Pack object:

        Pack inputPack = (Pack) jc.createUnmarshaller().unmarshal(new FileInputStream("doggin.xml"));

It seems easy, but one must be design the Java class carefully to ensure that things end up back in the proper places. Here is the order in which the unmarshal command calls Java methods:
  1. Call to no-argument constructor to create the class
  2. Call to get and set methods in the order they appear within the XML

It is a good idea to design constructors with arguments to work the same way within your code (this avoids bugs).

Source Code

Here is a sample input file: doggin.xml. You can modify it directly, and see the resulting change in the inputPack object. The complete source code with String outputs to test the process is: Main.java.

Working with Inheritance

First Technique

This section is partially incomplete/inaccurate. I am still trying to figure out how this works!

XML binding will usually take place with a declared class rather than the "runtime" class. One approach that works is annotating with a list of possible concrete types. Suppose Animal is an abstract class that might represent a Cat or Dog or other animal? One can annotate the corresponding property with @XmlElements as follows:

@XmlRootElement
class Zoo {
    @XmlElements({
     @XmlElement(name="dog",type=Dog.class),
     @XmlElement(name="cat",type=Cat.class)
    })
    public Animal animal;
    ...
}

This will allow for multiple types of XML tags within the output's <zoo> tag:
<zoo>
    <dog .../>
</zoo>

or
<zoo>
    <cat .../>
</zoo>

are both acceptable.

The use of the @XmlElements tag makes the following differences in the XML:

  • tag names are specific to the type of subclass of the Animal class
  • any additional attributes or elements specified as part of the subclass are included in the XML

Thus, one generally needs to use this technique to ensure that inheritance works properly.

Collections

How do you deal with structures such as Vector<Animal>? The process is similar:

@XmlRootElement
class Zoo {
    @XmlElements({
     @XmlElement(name="dog",type=Dog.class),
     @XmlElement(name="cat",type=Cat.class)
    })
    public Vector<Animal> animals;
    ...
}

This will allow for multiple types of XML tags within the output's <zoo> tag:
<zoo>
    <dog .../>
    <dog .../>
    <cat .../>
</zoo>

Second Technique

Another approach is to register the descendant classes into the JAXBContext and to use the @XmlAnyElement annotation. Here's how. First, you'll need to annotate the subclasses as follows:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Zoo {
    @XmlAnyElement
    public List<Animal> animal;
    ...
}
 
interface Animal {
    ...
}
 
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Lion implements Animal {
    String name = "Simba";
}
 
// other subclasses of animal

Now when initializing the JAXBContext use:
JAXBContext jc = JAXBContext.newInstance(Zoo.class, Lion.class, ...);

This registers the subclasses with the context so it recognizes them during the marshalling process.

Customizing the XML

The @XmlJavaTypeAdapter annotation allows one to "intercept" the marshalling process and so specify exactly what the XML will look like. This requires writing two additional classes: one for the adapted version of the class, and an adapter class that handles the conversion process. Here is an example that stores dogs at half their height:

// our original class
@XmlJavaTypeAdapter(DogAdapter.class)
static class Dog {
    double height;
    public Dog(double height) {
        this.height = height;
    }
}
 
// our adapted class
@XmlAccessorType(XmlAccessType.FIELD)
static class LittleDog {
    double height;
    public LittleDog() {}  // no-arg default constructor required for marshalling
    public LittleDog(double height) {
        this.height = height;
    }
}
 
// this performs the class conversion
static class DogAdapter extends XmlAdapter<LittleDog, Dog> {
    @Override
    public Dog unmarshal(LittleDog v) throws Exception {
        return new Dog(v.height*2);
    }
 
    @Override
    public LittleDog marshal(Dog v) throws Exception {
        return new LittleDog(v.height/2);
    }
}
 
// the enclosing instance
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
static class Pack {
    Dog dog1 = new Dog(5.0);
    Dog dog2 = new Dog(3.0);
}

Now when we perform the marshalling process, we get the following XML:

<pack>
    <dog1>
        <height>2.5</height>
    </dog1>
    <dog2>
        <height>1.5</height>
    </dog2>
</pack>
Add a New Comment

Please up-vote this page if you like its contents!

rating: +1+x

Leave a comment or a question below:

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License