(2/26更新)
(12/22更新 @Jsonはフィールド直接指定になりました)
Slim3 JSON機能のドキュメントを書き始めました。まだ非公式なものですが、ひと通り書き終わったら公式へのマージを提案する予定です。でも、公式は英語なんですよね。どうしたものか。まぁ、いきなり下手な英語で書くより、まずは日本語でちゃんと書いたほうが良いはず。
というわけで、下記ドキュメントの草案です。若干改行が変ですが、evernoteに書きなぐってexportしたものを貼っつけてるためだと思います。これにあとJSON入出力のカスタマイズ方法を書いて一段落とする予定です。(書きました!)
概要
Slim3のJSON機能は、モデル(@org.slim3.datastore.Modelアノテーションが付加されたクラス)のJSON変換機能を提供します。JSON変換は、org.slim3.datastore.ModelMeta<M>に定義される以下のメソッドを呼び出すことにより行われます。
モデルのJSON文字列への変換:
- String modelToJson(Object model);
- String modelToJson(Object model, int maxDepth);
- String modelsToJson(Object[] models);
- String modelsToJson(Object[] models, int maxDepth);
- M jsonToModel(String json);
- M jsonToModel(String json, int maxDepth);
- M[] jsonToModels(String json);
- M[] jsonToModels(String json, int maxDepth);
例えばTestModelというモデルに対してJSON変換機能を使用するコードは、次のようになります。
TestModel m = new TestModel();
m.setValue(1);
String json = TestModelMeta.get().modelToJson(m);
System.out.println(json); // {"value": 1}
TestModel m2 = TestModelMeta.get().jsonToModel(json);
Assert.assertEquals(m.getValue(), m2.getValue());
JSON変換の制御
フィールドにJSONアノテーション(@org.slim3.datastore.json.JSON)を指定することで、JSON変換の振る舞いを変更できます。以下にJSONアノテーションで指定できるパラメータを示します。
| パラメータ名 | 型 | デフォルト | 説明 |
| ignore | boolean | false | このアトリビュートを無視します。JSON出力に含まれず、JSON入力時にも読み込まれません。 |
| ignoreNull | boolean | true | アトリビュートがnullの場合、何も出力しません。falseの場合は、"null"が出力されます。 |
| alias | String | 空文字列 | アトリビュートのJSON文字列内での名前を指定します。 |
| coder | Class<? extends JsonCoder> | Default.class | JSON入出力を行うJsonCoderのクラスを指定します。ここに独自のクラスを指定することで、JSON入出力のカスタマイズを行うことができます(後述)。 |
例えばプロパティを無視したり、エイリアスを設定したりする場合、以下のように記述します。
@Model
class TestModel{
...
@Json(ignore=true)
private String ignoredValue;
@Json(alise="foo")
private String bar;
}対応する値型
Slim3のJSON変換機能は、以下の値型に対応します。また、後述するカスタマイズを行えば、これ以外の型に対応したり、入出力方法を変更する事ができます。
| Javaクラス | JSON表現 | 例 |
| java.lang.String, com.google.appengine.api.datastore.Text | 文字列。cipher=trueの場合、JSON内では暗号化されます。 | "text":"hello" |
| byte[], com.google.appengine.api.datastore.ShortBlob, com.google.appengine.api.datastore.Blob | バイト列のBase64文字列。 | "blob":"mMB4qZAgtBKJq0d1LBGTCA==" |
| boolean, java.lang.Boolean | ブール値(true or false)。 | "value":true |
| short, java.lang.Short, int, java.lang.Integer, long, java.lang.Long | 整数。 | "value":100 |
| float, java.lang.Float, double, java.lang.Double | 小数。 | "value":1.0 |
| java.util.Date | 整数(Date.getTime()が返す値)。 | "value":10233400 |
| java.lang.Enum | 文字列(Enum.name()が返す値)。 | "value":"MONDAY" |
| com.google.appengine.api.users.User | ネストしたJSON文字列(User.getEmail()等の値をJSONエンコード)。 | "user":{"authDomain":"authDomain","email":"user@test.com"} |
| com.google.appengine.api.datastore.Key | 文字列(KeyFactory.keyToString(Key)の実行結果)。 | "key":"aglzbGltMy1nZW5yCwsSBHRlc3QY6AcM" |
| com.google.appengine.api.datastore.Category | 文字列(Category.getCategory()の値)。 | "category":"partOfSpeech" |
| com.google.appengine.api.datastore.Email | 文字列(Email.getEmail()の値)。 | "mail":"user@domain.tld" |
| com.google.appengine.api.datastore.GeoPt | ネストしたJSON文字列(GeoPt.getLatitude()及びGeoPt.getLongitude()の値をJSONエンコード)。 | "geopt":{"latitude":10.0,"longitude":10.0} |
| com.google.appengine.api.datastore.IMHandle | ネストしたJSON文字列(IMHandle.getAddress()及びIMHandle.getProtocol()の値をJSONエンコード)。 | "handle":{"address":"handle","protocol":"xmpp"} |
| com.google.appengine.api.datastore.Link | 文字列(Link.getValue()の値)。 | "link":"linkValue" |
| com.google.appengine.api.datastore.PhoneNumber | 文字列(PhoneNumber.getNumber()の値)。 | "phone":"000-000-000" |
| com.google.appengine.api.datastore.PostalAddress | 文字列(PostalAddress.getAddress()の値)。 | "address":"Tokyo, Japan" |
| com.google.appengine.api.datastore.Rating | 整数(Rating.getRating()の値)。 | "rating":100 |
| com.google.appengine.api.blobstore.BlobKey | 文字列(BlobKey.getKeyString()の値)。 | "blobkey":"Q3PqkweYlb4iWpp0BVw" |
| org.slim3.datastore.ModelRef<M> | 文字列(モデルのkeyに対してKeyFactory.keyToString(key)した結果)。 | "ref":{"key":"lskfo2ijalefkwejfwlke",value:100} |
また上記サポートする型のコレクションにも対応しています。対応するコレクションクラスはjava.util.List, java.util.Set, java.util.SortedSetで、JSON文字列からモデルを作成する際は、それぞれjava.util.ArrayList、java.util.HashSet、java.util.TreeSetをnewして要素を追加したものがセットされます。
ModelRef<M>の展開
デフォルトではJSONアノテーションのcoderパラメータにorg.slim3.datastore.json.Defaultが指定されたものとして扱われ、このクラスは、ModelRefを参照先のモデルのキーに変換します。参照先のモデルの内容を展開したい場合、org.slim3.datastore.json.Expandedを指定して下さい。このクラスは、参照先のモデルの内容を展開し、そのモデルがさらにModelRefを持っている場合も展開します。展開のネストの深さは、modelToJsonメソッドやjsonToModelメソッドの、maxDepth引数で制限できます。
以下にコード例を示します。
@Model
class TestModel{
...
@Json(coder=Expanded.class)
private ModelRef<TestModel> ref = new ModelRef<TestModel>(TestModel.class);
}
...
TestModel m = new TestModel();
TestModelMeta.get().modelToJson(m, 3); // 3階層まで展開
JSON入出力のカスタマイズ
JSONアノテーションのcoderパラメータにJSON入出力を行うクラスを指定することにより、JSON変換の振る舞いを変更することができます。デフォルトではorg.slim3.datastore.json.Defaultが指定されており、slim3が対応するデータ型のJSON入出力が行われます。slim3ではもう一つ、org.slim3.datastore.json.Expandedが用意されていて、これをModelRef<M>型のフィールドに対して使用すると、ModelRef<M>が参照しているオブジェクトも展開します。
slim3が対応していないデータ型を使う場合や入出力方法を変更したい場合は、org.slim3.datastore.json.Defaultから継承したクラスを作成し、encode/decodeメソッドをオーバーライドして下さい。
下記にjava.awt.Pointの入出力をサポートするクラスの例を示します。
class CustomCoder extends org.slim3.datastore.json.Default{
public void encode(JsonWriter writer, Object value){
if(value instanceof java.awt.Point){
java.awt.Point pt = (java.awt.Point)value;
writer.beginObject();
writer.writeValueProperty("x", pt.x);
writer.writeValueProperty("y", pt.y);
writer.endObject();
} else{
super.encode(writer, value);
}
public <T> T decode(JsonReader reader, T defaultValue, Class<T> clazz){
if(java.awt.Point.isAssignableFrom(clazz)){
try{
int x = Integer.parseInt(reader.readProperty("x");
int y = Integer.parseInt(reader.readProperty("y");
return clazz.cast(new Point(x, y));
} catch(NumberFormatException e){
}
}
return defaultValue;
}
} java.awt.PointクラスのフィールドをJSON入出力の対象にするには、上記のCustomCoderをcoderパラメータに指定します。
import java.awt.Point;
@Model
class TestModel{
public Key getKey(){
return key;
}
public void setKey(Key key){
this.key = key;
}
public Point getPoint(){
return point;
}
public void setPoint(Point point){
this.point = point;
}
@Attribute(primariKey=true)
private Key key;
@Attribute(persistent=false)
@Json(coder=CustomCoder.class)
private Point point;
} 上記のように指定すると、java.awt.Point型のpointフィールドのJSON入出力がおこなえるようになります。
TestModel m = new TestModel();
m.setPoint(new Point(10, 20));
String json = TestModelMeta.get().modelToJson(m); // {"point":{"x":10,"y":20}}
TestModel m2 = TestModelMeta.get().jsonToModel(json);
Assert.assertEquals(m.x, m2.x);
Assert.assertEquals(m.y, m2.y);