簡易JSONシリアライザを作ってみた
JavaFXではJSONとXMLを扱うAPIが用意されていますが、読み込みしか出来ない上にStAXと同じような実装で、少々面倒な所があります。今後、使いやすいAPIが出てくるかもしれませんが、現状でちょっとしたJSONを読み込んだり書き出したりする目的で簡易シリアライザ/デシリアライザを実装してみました。
JSONSerializer
シリアライズする場合は次のようにして使います。
import package jp.sunflower.javafx.data.json. JSONSerializer; class Foo { public var name:String; public var price; } var array = ["a", 2.0, 3, Foo {name:"hoge", price:[1,2]}]; var jsonStr = JSONSerializer { fxObject: array }.write(storage);
書き出しはStorageが便利ですが、OutputStreamの対応と文字列出力も対応しています。これで簡易設定などは簡単にローカルストレージに保存できますね。
ソースコードとサンプル。
JSONDeserializer
デシリアライズは次のようにして行います。
class Foo { public var name:String; public var age:Integer; public var scores:Integer[]; override public function toString():String { "{name}[{age}] {scores}"; } } var obj= JSONDeserializer { source: '\{"name": "Jhon", "age": 22, "scores": [24, 20] \}, {"name": "Mike", "age": 27, "scores": [21, 17] \}' className: "jp.sunflower.javafx.data.json.JSONDeserializerTest.Foo" }.parse(); println(obj);
sourceには文字列のほか、InputStreamやStorageでも対応しています。これで簡易設定などを簡単にローカルストレージやサーバから読み込む事ができるでしょう。
尚、現状のバージョンではマッピング先のクラスは1種類しか指定できません。通常は商品のリストであったり、設定クラスだったりとほとんどの状況で1種類のクラスで対応できると思います。なので複数は必要があれば対応しようかな程度です。
ソースコードとサンプル。
実装の小話
実装に関してはシリアライザはリフレクションAPIを使いつつ単純に吐き出しているだけです。エスケープ文字など不完全な所はありますが、使う段階でやろーかなと思っています。
デシリアライザはPullParserを使用しつつリフレクションAPIで自然な形に変換しています。あまり複雑なJSONには対応していないので、いじめないでくださいw
リフレクションAPI
JavaFXのリフレクションAPIは少々癖が強いです。使うのはオブジェクトを動的に生成する時となりますが、シーケンスを扱う場合が少々面倒です。
切り分けるポイントはそのシーケンスがメンバ変数にリフレクションAPIで代入するのか、それとも生のシーケンスを生成したいのか、それによってやり方が異なる事です。
まず、メンバ変数に代入する場合などは、他の変数でもそうなのですが、FXObjectとその派生クラスを使います。
var objectValue = classType.allocate(); objectValue.initVar("param1", fxValue); // FXObjectSequenceなどラッパー objectValue.initVar("param2", context.mirrorOf(10)); // 生のオブジェクトなどはFXContextからラッパーを生成
シーケンスの場合、FXContextのmakeSequenceBuilderなどを使用してラッパーを作ります。
ところが、生のシーケンス(例:Integer[])を作成しようとしてもSequenceBuilderからは生成できません。分かってしまえば単純なのですが、シーケンスを作成するようなJavaFXのスクリプトを用意すれば良いだけです。
// listの中はIntegerやStringなど function toSequence(list:ArrayList):Object[] { for (item in list) { item; } }
逆にこのようにして作成したシーケンスはリフレクションAPIを使った変数の代入(初期化)に使用できません。
尚、インスタンスのあるJavaFXのオブジェクトに関する操作については、Javaのクラスをラップしているだけですから、通常のリフレクションのようにgetClass()からゴニョゴニョすればOKです。