SimpleとJAXBの使いどころ
前回、JavaクラスとXMLを簡単に相互変換するためのライブラリとして"Simple"を取り上げたが、良く考えないでもJAXB実装を使えば同じようにアノテーションをベースにした相互変換ができる。
じゃあ、そもそもSimpleなんて使う意味が有るのか?とツッコまれそうなので、SimpleとJAXBとの使い分けというか、使いどころの違いについて思っていることをまとめて見る。まあ、JAXB(特にアノテーションが利用可能に成ったJAXB 2.0)についてはあんまり使用した経験が無いので、参考程度に。
JAXBを使うべきシチュエーション
Simpleを使ったほうが便利なシチュエーション
さっくり簡単に作りたい場合
例えば、アプリケーション内部で使用する(通常エンドユーザーには触らせないような)XML設定ファイル等の厳密なスキーマ定義が要求されない場合。Simpleを使用することで、さっくり簡単にJavaとXMLの相互変換を実装可能。JAXBは確かにアノテーションでの定義が可能なものの、把握すべきアノテーションが結構ある(参照)ため、さっくり簡単にという訳にはいかない(まあ、慣れで解決する問題では有ると思うけど)。
XMLでポリモーフィズムを表現したい場合
今回のキモ。
Simpleでは、抽象クラスやインターフェイスなどに対してもアノテーションでの定義が可能なため、XMLでJavaのポリモーフィズムを定義するのが非常に楽。
例えばantのビルド定義ファイルのように、XMLファイルに設定された要素をJavaクラスとして解釈し、上から順に実行していくような例をつくってみる。
先ずは、下記のようなXMLファイルに設定されるクラスが実装すべきインターフェイスを作成する。
Gachapin.java
package example; public interface Gachapin { /** * ガチャピンチャレンジを実行します。 * */ public void execute(); }
次に、Gachapinを実装する以下のクラスを作成してみる。
StuntPerson.java(抽象クラス)
package example; import org.simpleframework.xml.Attribute; /** * 中の人を表す抽象クラスです。 * */ public abstract class StuntPerson implements Gachapin{ /** * スタントパーソンの本名 * */ @Attribute String name; public abstract void execute(); }
抽象クラスにアノテーションをつけた場合でも、実装クラスに反映されることに注目。
以下、実装クラス。
SkyDiver.java
package example; public class SkyDiver extends StuntPerson { @Override public void execute() { // doSomethings.. System.out.println("スカイダイブします。(中の人は" + name + "です)"); } }
F1Driver.java
package example; public class F1Driver extends StuntPerson { @Override public void execute() { // doSomethings.. System.out.println("F1レースに出ます。(中の人は" + name + "です)"); } }
Karateka.java
package example; public class Karateka extends StuntPerson { @Override public void execute() { // doSomethings.. System.out.println("空手します。(中の人は" + name + "です)"); } }
また、XMLから上記のGachapin実装クラスの定義を読み込み、順次executeメソッドを実行するクラスもつくってみる。
GachapinChallenge.java
package example; import java.io.File; import java.util.List; import org.simpleframework.xml.ElementList; import org.simpleframework.xml.Root; import org.simpleframework.xml.Serializer; import org.simpleframework.xml.core.Persister; @Root public class GachapinChallenge { @ElementList(inline=true, entry="gachapin") private List<Gachapin> list = null; public static void main(String[] args){ Serializer serializer = new Persister(); File file = new File("GachapinChallenge.xml"); try { GachapinChallenge myself = serializer.read(GachapinChallenge.class, file, true); //Gachapin実装クラスの読み込み for(Gachapin gachapin : myself.list){ gachapin.execute(); } } catch (Exception e) { e.printStackTrace(); } } }
最後に下記のようなXML(GachapinChallenge.xml)を用意し、gachapin要素のclass属性にGachapinインターフェイスの実装クラスを指定する。
<?xml version="1.0" encoding="UTF-8"?> <GachapinChallenge> <gachapin class="example.F1Driver" name="スタント太郎"/> <gachapin class="example.Karateka" name="スタント次郎"/> <gachapin class="example.SkyDiver" name="スタント三郎"/> </GachapinChallenge>
class属性を明示した場合、Simple内部でClass.forNameを使用してGachapinの実装クラスが呼び出される。Gachapinを実装していないクラスが指定された場合にはエラーとなり、デシリアライズに失敗する。
GachapinChallengeの実行結果はこんな感じ。
F1レースに出ます。(中の人はスタント太郎です)
空手します。(中の人はスタント次郎です)
スカイダイブします。(中の人はスタント三郎です)
もしガチャピンチャレンジに新しくスキージャンプを加えたいとしたら
- Gachapinを実装するSkiJumperクラスを新規作成し、クラスファイルをクラスパスに追加(厳密に言うと、アプリケーション起動中にクラスをロードするにはClassLoaderを使用する必要があるけど、今回はバッチ実行を想定するため無視)
- XMLを以下のように修正
<?xml version="1.0" encoding="UTF-8"?> <GachapinChallenge> <gachapin class="example.F1Driver" name="スタント太郎"/> <gachapin class="example.Karateka" name="スタント次郎"/> <gachapin class="example.SkyDiver" name="スタント三郎"/> <!-- 下記の要素を新規追加 --> <gachapin class="example.SkiJumper" name="スタント四郎"/> </GachapinChallenge>
これだけで済む。
まあ、何の変哲もないようにみえるけど・・・これをJAXBのアノテーションで表現出来るかというと結構難しい。JAXBでは、XMLではなくコード内のアノテーションに実装クラスについての情報を明記する必要がある。
JAXBを使っている場合にガチャピンチャレンジへスキージャンプを加えたいとしたら、上記の手順の他に、SkiJumperクラスをXmlSeeAlsoアノテーションに追記して再度コンパイルする必要が有るっぽい。
XmlElementRefアノテーションを使ってポリモーフィズムを表現する方法もあるようだが(参照)こちらもやはり、各実装クラスをアノテーションに埋め込んでいるため、実装クラスを追加した場合にはクラスファイルの更新が必要だろう。
というか、実装クラスを一つ一つ明示してアノテーション(つまりはコード)に含めなければならないという時点でポリモーフィズムもへったくれも無くなってるのは気のせいだろうか?