XStreamを使う
一時期お世話になったXStreamについて紹介したいと思う。
XStreamは、オブジェクトをXMLにシリアライズしたり、逆にデシリアライズするシンプルなXML操作ライブラリである。
XStreamは標準ではkXML2という軽量で高速なXML parserを使用する。
しかし依存関係を作りたくない場合はJavaのDOM parser(javax.xml.parsers.DocumentBuilder)や、
Java6から追加されたStreaming API for XML、通称 StAXも利用できる(javax.xml.stream.XMLStreamReader)。
※公式チュートリアルではxstream-[version].jar と kxml2-min-[version].jarをクラスパスに入れること、とあるが、
それではExceptionが発生してしまうので、kxml2-min-[version].jarの代わりに kxml2-[version].jarを使えばいい。
それについては別記事とした。
理解しやすいエイリアスを使ったシリアライズ・デシリアライズを実際にやってみる。
■シリアライズする
○課題XML
作りたいXMLはxstream-distribution-1.4.1-src.zipに入っていた"settings-template.xml"にする。
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright (C) 2006, 2007, 2010 XStream committers. All rights reserved. The software in this package is published under the terms of the BSD style license a copy of which has been included with this distribution in the LICENSE.txt file. Created on 30. July 2006 by Mauro Talevi --> <settings> <servers> <server> <id>codehaus-nexus-snapshots</id> <username>your-xircles-id</username> <password>your-xircles-pwd</password> </server> <server> <id>codehaus-nexus-staging</id> <username>your-xircles-id</username> <password>your-xircles-pwd</password> </server> <server> <id>codehaus.org</id> <username></username> <password></password> </server> </servers> </settings>
○モデルを考える
問題のXMLはsettingsの中にserversという項目があり、serversは複数のserverで構成されている。
モデルとして作成するのはList型のserversを持つsettingsと、3つの項目を持つserverになる。
Settings.java
package xml.xstream.step1; import java.util.ArrayList; import java.util.List; public class Settings { private List<Server> servers = new ArrayList<>(); public void add(Server srv){ servers.add(srv); } public List<Server> get(){ return servers; } @Override public String toString(){ StringBuilder builder = new StringBuilder(); for(Server server : servers) builder.append(server+"\n"); return builder.toString(); } }
Server.java
package xml.xstream.step1; public class Server { private String id; private String username; private String password; public Server(String id,String username, String password){ this.id = id; this.username = username; this.password = password; } @Override public String toString(){ return "id:"+id+", username:"+username+", password:"+password; } }
※コンソール出力しやすいようにtoString()をOverrideしている。
○シンプルなテスト(シリアライズ)
まずは簡単にシリアライズできることを確認する。
シリアライズはXStream#toXML(Object)で簡単にできる。
public class SerializingXML { public static void main(String[] args) { Settings settings = new Settings(); settings.add(new Server("codehaus-nexus-snapshots", "your-xircles-id", "your-xircles-pwd")); settings.add(new Server("codehaus-nexus-staging", "your-xircles-id", "your-xircles-pwd")); settings.add(new Server("codehaus.org", "", "")); XStream xstream = new XStream(); System.out.println(xstream.toXML(settings)); } }
処理結果
<xml.xstream.step1.Settings> <servers> <xml.xstream.step1.Server> <id>codehaus-nexus-snapshots</id> <username>your-xircles-id</username> <password>your-xircles-pwd</password> </xml.xstream.step1.Server> <xml.xstream.step1.Server> <id>codehaus-nexus-staging</id> <username>your-xircles-id</username> <password>your-xircles-pwd</password> </xml.xstream.step1.Server> <xml.xstream.step1.Server> <id>codehaus.org</id> <username></username> <password></password> </xml.xstream.step1.Server> </servers> </xml.xstream.step1.Settings>
○Class エイリアス
しかしこのままでは"xml.xstream.step1.Server"などクラス名そのままTag名になってしまっている。
これをClassエイリアスを使って任意のTag名へ変える必要がある。
XStream xstream = new XStream(); xstream.alias("settings", Settings.class); xstream.alias("server", Server.class);
処理結果
<settings> <servers> <server> <id>codehaus-nexus-snapshots</id> <username>your-xircles-id</username> <password>your-xircles-pwd</password> </server> ... </servers> </settings>
Tag名が変わった。
目標としてはこれで完成した。
○暗黙のコレクション
しかしこれだけでは物足りないのでImplicit Collections(暗黙的コレクション)も使ってみる。
この機能は問題で言う所の"servers"に対する機能となる。
"servers"はモデルでも定義している通り、List型の構造である。
"servers"Tagを無くすことができる。
xstream.addImplicitCollection(Settings.class, "servers");
処理結果
<settings> <server> <id>codehaus-nexus-snapshots</id> <username>your-xircles-id</username> <password>your-xircles-pwd</password> </server> <server> ... </server> <server> ... </server> </settings>
ちなみに、ver1.3まではList以外の配列やMapは使用できなかった。
ver1.4より上記2つも利用できるようになったようだ。
○Field エイリアス
ここでsettingsへファイル名の項目を追加することにする。
Settings.java へ下記を追加
private String configName; public Settings(String confName){ this.configName = confName; }
そしてmainはこうなる
public class SerializingXML { public static void main(String[] args) { Settings settings = new Settings("settings-template.xml"); settings.add(new Server("codehaus-nexus-snapshots", "your-xircles-id", "your-xircles-pwd")); settings.add(new Server("codehaus-nexus-staging", "your-xircles-id", "your-xircles-pwd")); settings.add(new Server("codehaus.org", "", "")); XStream xstream = new XStream(); xstream.alias("settings", Settings.class); xstream.alias("server", Server.class); xstream.addImplicitCollection(Settings.class, "servers"); System.out.println(xstream.toXML(settings)); } }
処理結果
<settings> <configName>settings-template.xml</configName> <server> ... </server> </settings>
しかし、Tag名を"configName"ではなく"config"としたいため、
Fieldエイリアスを使う。
mainに下記を追加
xstream.aliasField("config", Settings.class, "configName");
処理結果
<settings> <config>settings-template.xml</config> <server> ... </server> </settings>
ちゃんと"config"になった。
○Attribute エイリアス
XMLにはValueだけでなく、Attribute(属性)値も存在する。
ここではさきほど追加した"config"を"settings"の属性としたい。
xstream.useAttributeFor(Settings.class, "configName");
処理結果
<settings config="settings-template.xml"> ... </settings>
しっかり属性となった。
簡単なXMLならばここまでの機能を使って作成することができるだろう。
また、ここではSettingsクラスのFieldであるconfigNameを属性に変換したが、
モデル自体を属性としたい場合は独自のConverterを利用することもできる。
この機能についてはまた次の機会に紹介することとする。
■デシリアライズする
今度は逆にXMLファイルをオブジェクトとして持ってみる。
今まで作ってきたモデルを使う。
問題のXMLへconfig属性を追加する。
<settings config="settings-hogehoge.xml">
main
public static void main(String[] args) { Path fullpath = FileSystems.getDefault().getPath( "src","xml","xstream","step1","settings-template.xml"); File full = fullpath.toFile(); XStream xstream = new XStream(); xstream.alias("settings", Settings.class); xstream.alias("server", Server.class); xstream.aliasAttribute(Settings.class, "configName", "config"); Settings settings = (Settings) xstream.fromXML(full); System.out.println("servers.size="+settings.get().size()); System.out.println("settings.configName="+settings.getConfigName()); System.out.println(settings); System.out.println(xstream.toXML(settings)); }
処理結果
servers.size=3 settings.configName=settings-hogehoge.xml id:codehaus-nexus-snapshots, username:your-xircles-id, password:your-xircles-pwd id:codehaus-nexus-staging, username:your-xircles-id, password:your-xircles-pwd id:codehaus.org, username:, password: <settings config="settings-hogehoge.xml"> <servers> <server> <id>codehaus-nexus-snapshots</id> <username>your-xircles-id</username> <password>your-xircles-pwd</password> </server> <server> <id>codehaus-nexus-staging</id> <username>your-xircles-id</username> <password>your-xircles-pwd</password> </server> <server> <id>codehaus.org</id> <username></username> <password></password> </server> </servers> </settings>
いろいろと端折るが、特筆すべきはaliasAttributeだろう。
属性を読み込む際は、aliasAttributeをしなくてはならない。
長かった紹介もここでおしまい。
お疲れ様でした。