今日の役に立たない一言 - Today’s Trifle! -

古い記事ではさまざまなテーマを書いていますが、2007年以降はプログラミング関連の話がほとんどです。

JSONIC+Slim3+DatastoreでUnsupportedOperationExceptionが出るときの回避方法

エンティティクラスに com.google.appengine.api.datatore.Text型を使ったプロパティを追加したら、クライアント側からJSONを送信した時に、JSONICがUnsupportedOperationExceptionってのを出すようになった。
JSONICがDatastoreのText型なんて知らねーよ、ってことらしい。

JSONICのドキュメントを調べてみたら、このあたりに書いてあることをやればいいらしい。
JSONIC - simple json encoder/decoder for java

というわけで、JSON4Gaeってクラスを作る。パッケージは com.satoshis ってことで。

package com.satoshis;

import net.arnx.jsonic.JSON;

public class JSON4Gae extends JSON {
}

JSONをエンティティに変換するときに呼ばれるのが postparse()メソッドらしいので、それをオーバーライドしてやる。
まずは、コンソールに引数の内容を出力して、どんな引数で呼ばれているのかを確認する。

public class JSON4Gae extends JSON {
    @Override
    protected <T> T postparse(Context context, Object value, Class<? extends T> c, Type t) throws Exception {
        System.out.println("Context=" + context + ", value=" + value + ", Class=" + c + ", Type=" + t);
        return super.postparse(context, value, c, t);
    }
}

でも、クラスを作ってメソッドをオーバーライドしただけじゃ、このクラスを呼び出してくれない。デフォルトのJSONクラスを呼び出す代わりに、このクラスを呼び出すようにするために、web.xmlを修正する。processorで、自作したクラス名を書けばいいらしい。

    <servlet>
        <servlet-name>RestServlet</servlet-name>
        <servlet-class>net.arnx.jsonic.web.RESTServlet</servlet-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>
                {
                  "debug": true,
                  "mappings": {"/json/{class}.json": "com.aetalog.controller.json.${class}Controller"},
                  "processor": "com.satoshis.JSON4Gae"
                }
            </param-value>
        </init-param>
    </servlet>

サーバーを再起動して実行すると、Text型に指定したメンバ名がhogeならば、こんな感じの出力。

Context=$.hoge[null][null], value=abcdefg, Class=class com.google.appengine.api.datastore.Text, Type=class com.google.appengine.api.datastore.Text

valueの型を調べたらStringだった(当然)。
つまり、StringをTextに変換する方法がわからんってことなので、その実装を追加してやる。

public class JSON4Gae extends JSON {
    @Override
    protected <T> T postparse(Context context, Object value, Class<? extends T> c, Type t) throws Exception {
        if (Text.class.isAssignableFrom(c) && value instanceof String) {
            Text text = new Text((String)value);
            return c.cast(text);
        }
        return super.postparse(context, value, c, t);
    }
}

これで動いた。

追記:ふと思ったけど、こんなことやんなくても、Slim3JSONprocessorに指定したら動いたりする?