簡易JSONシリアライザを作ってみた

JavaFXではJSONXMLを扱う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です。