Sunday, October 20, 2013

複数の要素と複数の属性 - JAXB 入門的な何か

きょうは JAXB (Java Architecture for XML Binding) のことについて少し書き留めておこうと思います. 全然大したことを書こうというつもりはなく, ただ単に XML って何? 的な状態の者が JAXB クラスを触り始めたところで気づいたことを 2 点ばかりメモ書きしておこうと思った, 程度のことです.

JAXB は, 自分でマーシャリングやアンマーシャリングの処理を書いてみるようにすれば, いろいろとおもしろいことができそうな気がしていますが, そこまで追究する余裕があるだけのキャリアも能力もありませんので, 誰でもお手軽に始められる JAXB クラスのメソッドを使った場合の話に限定したいと思います.

ある要素が複数ある場合の話

たとえば, 次のようなクラスがあるとします.

package tt4cs.jaxb;

import javax.xml.bind.annotation.XmlElement;

public class Wrestler {
    
    @XmlElement
    private String name;
    
    private Wrestler() {}
    
    public Wrestler(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return this.name;
    }
    
}

このクラスと JAXB を使って, 蝶野正洋選手を XML にマーシャリングすることを考えてみます.

        Wrestler chono = new Wrestler("蝶野正洋");
        JAXB.marshal(chono, System.out);

この場合, 標準出力には次のような結果を得ます.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<wrestler>
    <name>蝶野正洋</name>
</wrestler>

特におかしいところはないと思います.

しかし, 複数の名前を持ったレスラーもいます. たとえば, 平田淳嗣選手です. 地味なんですが, いわば映画の名脇役的な立ち位置で舞台の演出に貢献しつづけてきた努力家であり, わたしは嫌いじゃなかったりします. 近年, 「おまえ平田だろう」 と野次られたり 「しょっぱい試合をしてすみません」 とコメントしたりするのが往年の新日本プロレスファンの心の琴線に触れるようになり, いい感じに歳をとったんだなあと憧れすら覚えます. その平田選手を XML で書くと, たとえば次のようになるでしょうか.

<?xml version="1.0"?>
<wrestler>
    <name>平田淳嗣</name>
    <name>ストロング・マシーン1号</name>
    <name>スーパー・ストロング・マシーン</name>
    <name>魔界1号</name>
    <name>スーパー・ストロング・魔神</name>
    <name>スーパー・ストロング・マシン</name>
</wrestler>

この XML をアンマーシャリングするとどうなるか, 以下のコードで試してみます.

        InputStream in;
        // ...
        Wrestler hirata = JAXB.unmarshal(in, Wrestler.class);
        System.out.println(hirata);

これの結果は, 次のようになります.

スーパー・ストロング・マシン

Wrestler クラスでは, 単一の String 型のメンバーしか <name/> 要素に対応していないので, 同じ名前の要素が複数あった場合にどれがバインドされるのだろうか, もしくはエラーとなるのだろうか, という点が気になるところでした. 結果を見ると, 複数の <name/> 要素のうち, いちばん最後にあらわれたもの (つまり 「スーパー・ストロング・マシン」) が採用されたらしい, ということがわかります.

もちろん, Wrestler クラスに対応するスキーマ定義 (xsd) をあらかじめきちんと用意しておき, 要素の数に食い違いが発生しないように事前に調整しておけばよいだけの話でしょうが, もし要素の数が 1 個だけなのかそれとも複数個なのかという食い違いが発生したら, とりあえずいちばん最後にあらわれた要素がバインドされる, ということのようです. ただし, これが JAXB の仕様なのか, それとも JDK に付属している JAXB の標準実装がたまたまそうなっているだけなのか, という点については確認していませんが.

ある属性の値が複数ある場合の話

たしか, XML においては, ひとつの要素に同じ名前の属性を複数持たせることはできなかったと思います. しかし, 属性の値を空白文字で区切ることで, ある属性の値を複数持たせることはできます.

まず, 次のように, ProgBand クラスというものが用意されているとします.

package tt4cs.jaxb;

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

public class ProgBand {
    
    @XmlAttribute(name="formed-year")
    private String formedYear;
    
    @XmlElement
    private String name;
    
    private ProgBand() {}
    
    public ProgBand(String formedYear, String name) {
        this.formedYear = formedYear;
        this.name = name;
    }
    
    @Override
    public String toString() {
        return String.format("%s (%s)", this.name, this.formedYear);
    }
    
}

結成した年をあらわず属性を Integer 型ではなく String 型にしてしまいましたが, とりあえず見逃してください.

そのうえで, 次のような XML を用意してみましょうか.

<?xml version="1.0"?>
<prog-band formed-year="1970">
    <name>Emerson, Lake &amp; Palmer</name>
</prog-band>

エマーソン・レイク・アンド・パーマー (ELP) といえば, その代表作のひとつ 「タルカス」 が, 昨年の NHK 大河ドラマ 「平清盛」 の主要な BGM にもなりました. 音響効果としてのインパクトという点では, かつて驚異的な高視聴率を記録した 「独眼流政宗」 や 「武田信玄」 のそれらにも引けを取らないと思うのですが, さほど世間の反響は得られなかったようですね. いまは, 平成的な没個性や事大主義がもてはやされる時代です. もはや, 昭和的な不確実なるものを懐古することすら忌避される時代なのでしょうか. 残念な気がしなくもなかったりしています.

さて, 以下のように, この XML をアンマーシャリングしてみます.

        InputStream in;
        // ...
        ProgBand elp = JAXB.unmarshal(in, ProgBand.class);
        System.out.println(elp);

その結果は次のとおりです.

Emerson, Lake & Palmer (1970)

期待どおりです.

ところで, プログレの歴史は離合集散, 合従連衡の歴史でもあり, 同一のバンドが何度も結成と解散を繰り返すことが常態でした. エマーソン・レイク・アンド・パーマーもその例に漏れず, 1970 年に結成, 1979 年にいったん解散するも, 1991 年には再結成しました. そこで, 先ほどの XML は, 次のように書き直す必要がありそうです.

<?xml version="1.0"?>
<prog-band formed-year="1970 1991">
    <name>Emerson, Lake &amp; Palmer</name>
</prog-band>

ここで, ProgBand クラスのほうを変更せずに, この XML をアンマーシャリングすると, 結果は次のようになってしまいます.

Emerson, Lake & Palmer (1970 1991)

この段階では, formedYear の値は "1970 1991" です. formed-year 属性が複数の値を持っていることなど, まだ ProgBand クラスにとっては知ったこっちゃないということなのだと思います.

そこで, ProgBand クラスを, 少し書き直すことにしてみます.

package tt4cs.jaxb;

import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;

public class ProgBand {
    
    @XmlAttribute(name="formed-year")
    private List<Integer> formedYear;
    
    @XmlElement
    private String name;
    
    private ProgBand() {}
    
    public ProgBand(List<Integer> formedYear, String name) {
        this.formedYear = formedYear;
        this.name = name;
    }
    
    @Override
    public String toString() {
        return String.format("%s %s", this.name, this.formedYear);
    }
    
}

おもな変更点は, formedYear の型を String から List<Integer> に変更したこと, toString() メソッドを微修正したこと, といったところでしょうか.

あらためて, 先ほどの XML をアンマーシャリングしてみますと, 今度は次のような結果を得ることになります.

Emerson, Lake & Palmer [1970, 1991]

すなわち, "1970 1991" という単一の値ではなく, 19701991 という複数の値として取り扱っていることを示唆しています.

まとめ

特にまとめらしいまとめはないのですが, JAXB を使い始めた段階における, 複数の要素や属性の取り扱いはどうなるのかな, という点についてのメモ書きは, 今夜のところは以上です. おしまい.

No comments:

Post a Comment