リストに格納されているオブジェクトを marshal する場合に付与すると便利な @XmlElementWrapper アノテーション

公開日: : 最終更新日:2014/01/26 JAXB ,

JAXB を使ってリストに格納されているオブジェクトを marshal する際に付与すると便利な @XmlElementWrapper@XmlElement アノテーションをメモしておきます。

medium_8179595608

目次

1. アノテーションを付与しない場合の marshal

アノテーションを付与しない状態のサンプルプログラムを書いていきます。

1-1. Book クラス

まずはリストに格納される側の Book クラスになります。
次のフィールドを持ったクラスです。

  • タイトル
  • 出版社
  • ページ数
public class Book {

    /**
     * タイトル。
     */
    private String title;
    
    /**
     * 出版社。
     */
    private String publisher;

    /**
     * ページ数。
     */
    private int numberOfPages;

    /**
     * デフォルトコンストラクタが無いと marshal 時に javax.xml.bind.DataBindingException が発生してしまう。
     */
    public Book() {}

    public Book(String title, String publisher, int numberOfPages) {
        this.title = title;
        this.publisher = publisher;
        this.numberOfPages = numberOfPages;
    }

    public String getTitle() {
        return title;
    }

    /**
     * setter を定義しないと marshal することはできるが、このフィールドの値が出力されない。
     * 
     * @param title
     */
    public void setTitle(String title) {
        this.title = title;
    }

    public String getPublisher() {
        return publisher;
    }

    /**
     * setter を定義しないと marshal することはできるが、このフィールドの値が出力されない。
     * 
     * @param publisher
     */
    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }

    /**
     * <code>@XmlElement</code> で要素名を指定する。
     * 
     * @return
     */
    @XmlElement(name = "number_of_pages")
    public int getNumberOfPages() {
        return numberOfPages;
    }

    /**
     * setter を定義しないと marshal することはできるが、このフィールドの値が出力されない。
     * 
     * @param numberOfPages
     */
    public void setNumberOfPages(int numberOfPages) {
        this.numberOfPages = numberOfPages;
    }
}

1-2. Bookshelf クラス

次は Book クラスを格納する側の Bookshelf クラス(本棚)クラスです。
本を格納するリストをフィールドに持ちます。

public class Bookshelf {
    
    private List<Book> books;
    
    public Bookshelf() {
        books = new ArrayList<Book>();
    }

    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }
    
    public void add(Book book) {
        books.add(book);
    }

}

1-3. marshal した結果

この BookshelfBook を3つ格納させて marshal してみます。
そのコードは次の通りです。

public class BookshelfTest {
    
    private Bookshelf sut;

    @Before
    public void setUp() throws Exception {
        sut = new Bookshelf();
        sut.add(new Book("本のタイトル1", "本の出版社1", 208));
        sut.add(new Book("本のタイトル2", "本の出版社2", 2080));
        sut.add(new Book("本のタイトル3", "本の出版社3", 20800));
    }

    @Test
    public void JAXBでmarshalする() throws Exception {
        JAXB.marshal(sut, System.out);
    }

}

JAXBでmarshalする の実行結果は次の通りになります。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bookshelf>
    <books>
        <number_of_pages>208</number_of_pages>
        <publisher>本の出版社1</publisher>
        
    </books>
    <books>
        <number_of_pages>2080</number_of_pages>
        <publisher>本の出版社2</publisher>
        
    </books>
    <books>
        <number_of_pages>20800</number_of_pages>
        <publisher>本の出版社3</publisher>
        
    </books>
</bookshelf>

一見これで問題ないかなと思うところですが、Book クラスのフィールドは book タグで囲みたいですし、book タグは1つの books タグで囲むほうが自然な気がします。
具体的には ↓ のような出力です。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bookshelf>
    <books>
        <book>
            <number_of_pages>208</number_of_pages>
            <publisher>本の出版社1</publisher>
            
        </book>
        <book>
            <number_of_pages>2080</number_of_pages>
            <publisher>本の出版社2</publisher>
            
        </book>
        <book>
            <number_of_pages>20800</number_of_pages>
            <publisher>本の出版社3</publisher>
            
        </book>
    </books>
</bookshelf>

このように出力を変更する方法を順を追って説明していきます。

2. @XmlElementWrapper アノテーションを付与した場合の marshal

まずは @XmlElementWrapper アノテーションを Bookshelf@getBooks メソッドに付与します。
@XmlElementWrapper アノテーションを付与することによって、Book クラスのフィールドがタグに囲まれます。

2-1. Bookshelf クラス

@XmlElementWrapper アノテーションを Bookshelf@getBooks メソッドに付与した Bookshelf クラスは次のとおりです。

public class Bookshelf {
    
    private List<Book> books;
    
    public Bookshelf() {
        books = new ArrayList<Book>();
    }

    @XmlElementWrapper
    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }
    
    public void add(Book book) {
        books.add(book);
    }

}

2-2. marshal した結果

この状態で先ほどの BookshelfTest#JAXBでmarshalする を実行すると、出力は次のように変わります。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bookshelf>
    <books>
        <books>
            <number_of_pages>208</number_of_pages>
            <publisher>本の出版社1</publisher>
            
        </books>
        <books>
            <number_of_pages>2080</number_of_pages>
            <publisher>本の出版社2</publisher>
            
        </books>
        <books>
            <number_of_pages>20800</number_of_pages>
            <publisher>本の出版社3</publisher>
            
        </books>
    </books>
</bookshelf>

Book クラスのフィールドが books タグに囲まれました。
Book クラスのオブジェクトを格納するリストの変数名が “books” なので、タグも “books” になります。

このままだと books タグが入れ子になっており気持ち悪いので、これを任意のタグ名に変更します。
それには @XmlElement を使います。

3. @XmlElement アノテーションを付与した場合の marshal

@XmlElement を使い、出力されるタグを任意のものに変更します。

3-1. Bookshelf クラス

“book” タグで囲みたいので、Bookshelf クラスのコードは次のようになります。

public class Bookshelf {
    
    private List<Book> books;
    
    public Bookshelf() {
        books = new ArrayList<Book>();
    }

    @XmlElementWrapper
    @XmlElement(name = "book")
    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }
    
    public void add(Book book) {
        books.add(book);
    }

}

3-2. marshal した結果

三度 BookshelfTest#JAXBでmarshalする を実行すると、出力は次のように変わります。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bookshelf>
    <books>
        <book>
            <number_of_pages>208</number_of_pages>
            <publisher>本の出版社1</publisher>
            
        </book>
        <book>
            <number_of_pages>2080</number_of_pages>
            <publisher>本の出版社2</publisher>
            
        </book>
        <book>
            <number_of_pages>20800</number_of_pages>
            <publisher>本の出版社3</publisher>
            
        </book>
    </books>
</bookshelf>

最初の出力結果に比べて自然な XML になりました。

4. 検証プログラムを Github に登録しました

今回の記事で使用したサンプルプログラムを Github に登録しました。

jaxb-sample/src/main/java/net/tomoyamkung/jaxb/sample03 パッケージに登録されているプログラムになります。

よかったら参考にしてみてください。

5. その他の JAXB に関する記事

JAXB に関する記事は次の通りです。
気になる記事があったらぜひチェックしてみてください!

Googleアドセンス用(PC)

  • このエントリーをはてなブックマークに追加
  • follow us in feedly

関連記事

icatch-marshal

JAXB を使ってオブジェクトを marshal するために忘れてはいけない2つのこと

JAXB を使ってオブジェクトを marshal するために必要な2つの忘れてはいけないことをメモし

記事を読む

icatch-jaxb-marshal2

JAXB を使ってオブジェクトを marshal する際に要素名を指定する方法

JAXB を使ってオブジェクトを marshal する際に要素名を変更したい場合があるかと思います。

記事を読む

Googleアドセンス用(PC)

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


8 × = 三十 二

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Googleアドセンス用(PC)

icatch-jersey_multi_pathparams
Jerseyの@PathParamはスラッシュの間に複数指定できる

http://hoge-api/user/{id}.{format}

icatch-vagrant_box_customize
VagrantのBoxファイルをカスタマイズして独自のBoxファイルを作成する

配布されている Vagrant の Box ファイルを使って検証環境を

icatch-2015-006-1
バリデーションチェックにJava8のOptionalを使ってスマートに書く(自分比)

Web アプリのバリデーションチェックにアノテーションを使うことが増え

icatch-2015-005-1
ユニットテストの偏りを防ぐ命名規則の付け方

ユニットテスト名に以下の命名規則を付けるようにして二ヶ月ぐらい経った。

icatch-2015-004-1
Vagrantで起動したCentOS上のOctopressをホストOSから確認する設定

タイトルの通りだが、Vagrant を使って起動した CentOS に

→もっと見る

PAGE TOP ↑