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

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

JAXB を使ってオブジェクトを marshal するために必要な2つの忘れてはいけないことをメモしておきます。ちょっとしたことですが、もうハマりたくないので。。。

icatch-marshal

目次

  1. デフォルトコンストラクタは必須
    • デフォルトコンストラクタを定義していないサンプル
    • デフォルトコンストラクタを定義したサンプル
  2. setter メソッドがないと marshal されない
    • setter を定義していないサンプルクラス
    • setter を定義したサンプルクラス
  3. まとめ
  4. 検証プログラムを Github に登録しました
  5. その他の JAXB に関する記事

1. デフォルトコンストラクタは必須

ソースコードを追いかけてはいませんが、JAXB は marshal するクラスのオブジェクトを生成する際にデフォルトコンストラクタを使っているようです。

デフォルトコンストラクタを定義していないサンプル

なので、次の Book クラスのようにデフォルトコンストラクタを定義していないと javax.xml.bind.DataBindingException が発生してしまいます。

public class Book {
    
    /**
     * タイトル。
     */
    private String title;
    
    /**
     * 出版社。
     */
    private String publisher;

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }
}

例えば、次のテストケースがあったとして。

public class BookTest {
    
    private Book sut;

    @Before
    public void setUp() throws Exception {
        sut = new Book("本のタイトル", "本の出版社");
    }

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

テストを実行すると、次のようなスタックトレースが出力されます(長いので抜粋しています)。

javax.xml.bind.DataBindingException: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
net.tomoyamkung.jaxb.model.Book does not have a no-arg default constructor.
    this problem is related to the following location:
        at net.tomoyamkung.jaxb.model.Book

    at javax.xml.bind.JAXB._marshal(JAXB.java:549)
    at javax.xml.bind.JAXB.marshal(JAXB.java:407)
    at net.tomoyamkung.jaxb.model.BookTest.JAXBでmarshalする(BookTest.java:19)
Caused by: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
net.tomoyamkung.jaxb.model.Book does not have a no-arg default constructor.
    this problem is related to the following location:
        at net.tomoyamkung.jaxb.model.Book

    ... 26 more

デフォルトコンストラクタを定義したサンプル

デフォルトコンストラクタを定義した Book クラス。

public class Book {
    
    /**
     * タイトル。
     */
    private String title;
    
    /**
     * 出版社。
     */
    private String publisher;
    
    /**
     * デフォルトコンストラクタが無いと marshal 時に javax.xml.bind.DataBindingException が発生してしまう。
     */
    public Book() {}

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }
}

この Book クラスで先ほどのテストケースを実行すると、marshal が実行され、次の内容が標準出力に出力されます。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book>
    <publisher>本の出版社</publisher>
    
</book>

2. setter メソッドがないと marshal されない

フィールドのスコープを private にした場合、getter メソッドはとにかく setter メソッドも定義しないと marshal されません。
雰囲気的に getter メソッドがあればよさそうですが setter も必要です。

setter を定義していないサンプルクラス

簡単なサンプルを作成しました。

public class BookWithNoSettter {

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

    public BookWithNoSettter() {}

    public BookWithNoSettter(String title, String publisher) {
        this.title = title;
        this.publisher = publisher;
    }

    public String getTitle() {
        return title;
    }

    public String getPublisher() {
        return publisher;
    }
}

例えば、次のテストケースがあったとして。

public class BookWithNoSetterTest {
    
    private BookWithNoSettter sut;

    @Before
    public void setUp() throws Exception {
        sut = new BookWithNoSettter("本のタイトル", "本の出版社");
    }

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

このテストケースを実行すると、marshal は実行されますが、フィールドの値が標準出力に出力されません。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bookWithNoSettter/>

setter を定義したサンプルクラス

先ほどの BookWithNoSettter クラスに setter メソッドを定義しました。

public class BookWithNoSettter {

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

    public BookWithNoSettter() {}

    public BookWithNoSettter(String title, String publisher) {
        this.title = title;
        this.publisher = publisher;
    }

    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;
    }
}

テストケースを実行すると、marshal が実行され、フィールドの値も標準出力に出力されるようになりました。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bookWithNoSettter>
    <publisher>本の出版社</publisher>
    
</bookWithNoSettter>

3. まとめ

1番目の「デフォルトコンストラクタは必須」は実行してみるとすぐに気が付きますが、2番目の「setter メソッドがないと marshal されない」はちょっと気が付きにくいかもしれません。ボクはこれでずいぶん時間をムダにしました。。。

ちなみにフィールドのスコープを public にしておけば、アクセサメソッドが不要になるので今回のようにハマらずに済むかと思います。
コーディング規約的に public フィールドが許されるのであれば、marshal 用のクラスを定義してフィールドのスコープを public にするというのが無難かもしれません。

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

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

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

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

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

Googleアドセンス用(PC)

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

関連記事

icatch-jaxb-marshal2

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

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

記事を読む

medium_8179595608

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

JAXB を使ってリストに格納されているオブジェクトを marshal する際に付与すると便利な @

記事を読む

Googleアドセンス用(PC)

Message

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


三 + 3 =

次の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 ↑