Java6 JAXB marshalling, from POJO to XML.

JavaSE6からJAXB2.0が標準で使えるようである。
javax.xml.bind パッケージとして含まれている。

POJOからXMLをマーシャリング(marshaling - 整列)、その逆をアンマーシャリング(unmarshalling 無秩序化)という。

マーシャリング(POJO->XML)の例を上げる。
クラスにXmlRootElementをアノテーションする。

import java.util.Calendar;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Message {
    
    private long id;
    private Date postDate;
    private String name;
    private String comment;

    //constructor,getter,setter,etc...

}
これを、XMLにマーシャリングするには、次のコードを使用する。
import java.util.Date;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Test {
    public static void main( String[] args ) throws Exception {
        //アノテーションされたPOJOをインスタンス化
        Message message = new Message( 1, new Date(), "yamazaki", "Hello World" );

        //マーシャラーを取得。
        Marshaller marshaller = JAXBContext.newInstance( Message.class ).createMarshaller();

        //標準出力へマーシャリング
        marshaller.marshal( message, System.out );
    }
}
結果:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><message><comment>Hello World</comment><id>1</id><name>yamazaki</name><postDate>2010-02-18T00:46:47.366+09:00</postDate></message>
一行でコンパクトに表示される。改行を入れたい場合は、マーシャラーに次のプロパティをセットする。
...
        //マーシャラーを取得。
        Marshaller marshaller = JAXBContext.newInstance( Message.class ).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
...
すると、見やすくなる。 結果:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<message>
    <comment>Hello World</comment>
    <id>1</id>
    <name>yamazaki</name>
    <postDate>2010-02-18T00:52:24.009+09:00</postDate>
</message>
素晴らしい。 次に、上記のMessageというオブジェクトのリストを扱いたい。 期待する結果は、次のようなもの。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<messages>
    <message>
        <comment>test comment #1</comment>
        <id>1</id>
        <name>noname</name>
        <postDate>2010-02-18T00:55:18.359+09:00</postDate>
    </message>
    <message>
        <comment>test comment #2</comment>
        <id>2</id>
        <name>noname</name>
        <postDate>2010-02-18T00:55:18.359+09:00</postDate>
    </message>
    <message>
        <comment>test comment #3</comment>
        <id>3</id>
        <name>noname</name>
        <postDate>2010-02-18T00:55:18.359+09:00</postDate>
    </message>
</messages>
ちょっと、工夫を要した。残念ながら、Listオブジェクトとして直接、XMLにマップする方法は発見できなかったので、次のラッパーのようなクラスを作ることにした。
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType( value=XmlAccessType.FIELD)
@XmlRootElement(name="messages")
public class MessageList {

    @XmlElement( name="message" )
    private List<Message> messages;
    
    public MessageList() {
        this.messages = new ArrayList<Message>();
    }

    public List<Message> getMessages() {
        return messages;
    }

    public void setMessages(List<Message> messages) {
        this.messages = messages;
    }
}
テスト:
public class MessageUnit {
    public static void main( String[] args ) throws Exception {

        MessageList messages = new MessageList();
        messages.getMessages().add(new Message(1, "test comment #1"));
        messages.getMessages().add(new Message(2, "test comment #2"));
        messages.getMessages().add(new Message(3, "test comment #3"));

        Marshaller marshaller = JAXBContext.newInstance( MessageList.class ).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

        marshaller.marshal( messages, System.out );
    }
}
これで、期待する結果が得られる。 POJOを必要な時にXMLで取得できるのは極めて強力で便利である。 オブジェクトを10000個作成してマーシャリングし、標準出力を捨てると、実行時間は、以下のとおりだった。 コード:
public class MessageUnit {
    public static void main( String[] args ) throws Exception {

        MessageList messages = new MessageList();
        
        //10000個のMessageオブジェクトを追加
        for( int i = 0; i < 10000; i++  ) {
            messages.getMessages().add( new Message( i, "comment #" + i ) );
        }
        Marshaller marshaller = JAXBContext.newInstance( MessageList.class ).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal( messages, System.out );
    }
}
# time java restwitter.MessageUnit > /dev/null 実行時間: real 0m0.724s user 0m0.502s sys 0m0.087s # 10万個で次の結果。 実行時間: real 0m2.277s user 0m1.783s sys 0m0.175s AMD Athron64 2.4GHz 3800+ (Single Core) 一世代前のCPU。 速度的にも、悪くない。