December 2012

JAXBとSimpleXML

XMLとJavaの架け橋として、Java 6.0からJAXBが正式に追加された。XMLからJavaオブジェクトを作成したり、Java ObjectからXMLを生成したりとJAXBはわりと便利で、私も2008年ごろにXMLベースのイベント記述言語と実行ランタイムを実装するのに使ってました。

今回androidでXMLを触る機会があったのでJAXBを久しぶりに試してみようかな、と思ったのですが、Androidではサポートしてないため、代替案として
SimpleXMLを触ってみました。サンプルが充実してるので、簡単です。SimleXMLではxsdからJavaクラスを生成みたいな機能はないみたいなので、ずぼらな私はまずJAXBでxsdからJavaクラスを生成させ、それをSimpleXMLにあうように変更しました。かなり簡単で、例えばあるクラス内にStringの変数があったとしたら

(JAXB)
@XmlAttribute(required = true)
protected String id;

(SimpleXML)
@Attribute (required=true)
protected String id;


ってな感じ。required=trueは、その要素を必ず持つ必要があるようにスキーマが定義されていれば、JAXBと同じように生成します。
また、子要素が複数あらわれるようなタグに関して、JAXBではListを使った変数が生成されますが、それも、


(JAXB)
protected List testValues;

(SimpleXML)
@ElementList (inline=true,required=false)
protected List testValues;


ってな感じです。このinline=true というのが、あるとなしだとSimpleXMLがJavaオブジェクトから生成するXMLがかなり変わって、
inline=false の場合、


<data>
<test class="java.util.ArrayList">
<test id=“hoge” value=“hoge”/>
<test id=“”hoge2 value=“”hoge2/>
</test>
</data>


このように、
<test class="java.util.ArrayList”></test>
とくっついてきちゃいます。
inline=trueにすると、


<data>
<test id=“hoge” value=“hoge”/>
<test id=“”hoge2 value=“”hoge2/>
</data>


と、(私が)意図したとおりのスキーマに従ったXMLが生成されます。
SimpleXMLでJavaオブジェクトからXMLを生成する(JAXBでいうmarshaller)のは、例えばDataクラスが定義されてるとして、


public String dataToXml(Data data){
StringWriter writer= new StringWriter();
Serializer serializer = new Persister();
try {
serializer.write(data, writer);
} catch (Exception e) {
e.printStackTrace();
}
return writer.toString();
}


みたいな感じで、簡単。
JAXBでXMLから生成したJavaクラスからSimpleXML用のJavaクラスに変更Scriptもわりと簡単に書けそうなので、あってもよさそう。

Android + XMPP

XMPP for Android について。
XMPP for Javaにはsmackがあり、Androidにはsmackにパッチをあてたasmackがあるようだけど、asmackのメンテナンス状態はよくないみたい。
普通にそこのを使ってsmackぽくプログラムを書いてくと、色んなエラーが出る感じ。
ググったところ、asmackの派生がいくつかでてるみたい。
Beem内部で使ってるasmackとか、ここにはasmackの派生のbuild environmentがあって、元々のasmackよりはよさげ。
けど、PubSubManagerクラスのgetNodeメソッドなんかを使うあたりで、"java.lang.ClassCastException: org.jivesoftware.smack.util.PacketParserUtils…" というエラーなどがでて詰んでいる人がググったら見受けられる。

きちんと動かすには、
XMPPConnectionを使う前に、

org.jivesoftware.smackx.ConfigureProviderManager.configureProviderManager();


を呼んだり、KeyStore jks implementation not found なので、bksに設定したり、

例:

config.setTruststoreType("BKS"); 

config.setTruststorePath("/system/etc/security/cacerts.bks”);


同じくProviderを明示したり、

例:

ProviderManager pm = ProviderManager.getInstance();
pm.addIQProvider("pubsub","http://jabber.org/protocol/pubsub", new org.jivesoftware.smackx.pubsub.provider.PubSubProvider());


などと、色々とsmackにはない手続きを踏ます必要がある。更に、Subscribeされたデータを読むと、データがあるはずなのにnullってなっちゃったりする。これはXmlPullParserがうまくパースしてくれてないみたいなので、強引に直す。例えば、org.jivesoftware.smackx.pubsub.provider.ItemProvider.java の中を、


if (parser.getEventType() == XmlPullParser.START_TAG) {
payloadText.append("<").append(parser.getName());
int n = parser.getAttributeCount();
for (int i = 0; i < n; i++) {
payloadText.append(" ").append(
parser.getAttributeName(i)).append("=\"")
.append(parser.getAttributeValue(i))
.append("\"");
}
if (parser.isEmptyElementTag()) {
payloadText.append("/>");
degenerated = true;
} else
payloadText.append(">");
} else if (parser.getEventType() == XmlPullParser.END_TAG) {
if (degenerated)
degenerated = false;
else
payloadText.append("");
} else if (parser.getEventType() == XmlPullParser.TEXT) {
payloadText.append(parser.getText());
}

tag = parser.next();
}


こういう感じの処理を追加。ライブラリのソースをいじるのは気が引けるけど、仕方ない。。