標記の件について、というわけでもないのですが、JSONPの実装ライブラリーを調べていた所、Gensonなるライブラリーがあるのを見つけました。
謳い文句としては
- 簡単に使えてしかもパワフル
- 他のJavaエコシステムとも協調性がある
- 小さくて速い
- 速度ではJacksonにはかなわないけど、バイナリーサイズがJacksonよりも小さい
- 速度ではGsonに余裕で勝てるし、バイナリーサイズがGsonと遜色ない
といったところです。
まあ、言い換えれば中途半端ということですが、作者のモティベーションを借りると、痒いところに手が届くJSONのライブラリーということらしいです。
使い方
maven/gradle - dependencies
mavenの人はこれを追加します。
<dependency> <groupId>com.owlike</groupId> <artifactId>genson</artifactId> <version>1.3</version> </dependency>
gradleの人はこれを追加します。
dependencies {
compile 'com.owlike:genson:1.3'
}
まずはJSONにシリアライズ
こんなクラスを用意します。
public class RunRecord implements Serializable { private Integer metres; private Date date; // setter/getter省略 @Override public String toString() { return new StringJoiner(", ", "RunRecord:[", "]") .add("metres: [" + (metres == null ? "null" : metres) + "]") .add("date: [" + (date == null ? "null" : SimpleDateFormat.getDateTimeInstance() .format(date)) + "]") .toString(); } }
おもむろにこんなテストクラスを用意します。
なお、System.out
がグチャグチャになるのが嫌なので、slf4jとlogback使っています。
public static class UseDateAsTimeStamp { private static final Logger LOGGER = LoggerFactory.getLogger(UseDateAsTimeStamp.class); private static final ZoneId ASIA_TOKYO = ZoneId.of("Asia/Tokyo"); private Genson genson; private Date now; @Rule public TestName testName = new TestName(); @Before public void setup() { genson = new GensonBuilder() .useDateAsTimestamp(true) .create(); LocalDateTime current = LocalDateTime.now(ASIA_TOKYO); Instant instant = current.toInstant(ASIA_TOKYO.getRules().getOffset(current)); now = Date.from(instant); } @Test public void serializeWithTimestamp() { RunRecord rec = new RunRecord(1000, now); String json = genson.serialize(rec); LOGGER.debug("{} -> {}", testName.getMethodName(), json); } }
実行するとこんな感じです。
serializeWithTimestamp -> {"date":1437934805279,"metres":1000}
まあ、至って普通のJSONが吐き出されていますね。
上記のサンプルではGenson
のインスタンスを生成する際に、
genson = new GensonBuilder() .useDateAsTimestamp(true) .create();
と指定しており、Date
型がlong
の値で出力されるようになっています。
【2015/07/27 8:10 追記】なおこのuseDataAsTimestamp
はデフォルトではtrue
に設定されてます。下のDateFormat
を設定した場合でも、useDataAsTimestamp
がtrue
の場合にはuseDataAsTimestamp
が優先されます。【追記終わり】
「いやじゃ、儂はDate
には特定のフォーマットがいいんじゃ」という人は、Genson
のインスタンスを生成する際に次のように指定します。
genson = new GensonBuilder() .useDateAsTimestamp(false) .useDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")) .create();
このようにGenson
のインスタンスを生成した場合には、出力はこんな感じになります。
serializeWithISO8601 -> {"date":"2015-07-27T03:20:05.204+09:00","metres":1000}
まあ、これも至って普通のJSONです。
JSONからデシリアライズ
これもわりと簡単です。
public static class DeserializeWithTimeStamp { private static final Logger LOGGER = LoggerFactory.getLogger(DeserializeWithTimeStamp.class); private Genson genson; @Rule public TestName testName = new TestName(); @Before public void setup() { genson = new GensonBuilder() .useDateAsTimestamp(true) .create(); } @Test public void deserializeWithTimestamp() { String json = "{\"date\":1437934296467,\"metres\":200}"; RunRecord rec = genson.deserialize(json, RunRecord.class); LOGGER.debug("{} -> {}", testName.getMethodName(), rec); } }
これを実行すると、次のように出力されます。
deserializeWithTimestamp -> RunRecord:[metres: [200], date: [2015/07/27 3:11:36]]
これも至って普通にPOJOになりました。
「いやじゃ、儂は特定のフォーマットがいいんじゃ」という人は(ry。
JSON Arrayのデシリアライズ
JSON Arrayのデシリアライズも型を指定して行うことができます。
こんな感じです。
@Test public void deserializeArray() { String json = "[" + "{\"date\":\"2015-07-26T23:57:54.124+09:00\",\"metres\":1000}" + "," + "{\"date\":\"2015-07-27T00:08:11.748+09:00\",\"metres\":1000}" + "]"; List<RunRecord> recs = genson.deserialize(json, new GenericType<List<RunRecord>>() {}); LOGGER.debug("{} -> {}", testName.getMethodName(), recs); }
実行結果はこんな感じ。
deserializeArray -> [ RunRecord:[metres: [1000], date: [2015/07/26 23:57:54]], RunRecord:[metres: [1000], date: [2015/07/27 0:08:11]]]
なお、実行結果は読みやすくするために勝手に改行入れてます。
GenericType
というクラスのジェネリクスに変換したい型を指定すれば、あとはよしなにやってくれます。なお、GenericType
クラスのインスタンス化の後に{}
があるのはGenericType
クラスが抽象クラスだからなのですが、必要そうなメソッドはすべて実装されているので、特に何かメソッドを実装する必要はありません(魔改造したいなら別だけど、特に改造するところが見当たらない)。
え?Jacksonの方が簡単?
あー、そうですね、Jacksonの方が簡単ですね(棒)
作者曰く、
他のライブラリーって機能が足りないとか、機能が豊富すぎて、自分好みにカスタマイズするの面倒じゃけぇ、このライブラリーでその辺を解決したんよ
Android
Androidのことは特に書いてなかったですね。
配布されたjarのMANIFESTを読んだところ、コンパイルしたJDKのバージョンが1.7.0_65でした。また、pomの依存を見たところ、jersey-common
とかjavax.servlet-api
とかasm-commons
とかspring-core
とかresteasy-jaxrs
とかありましたけど、全部optional
要素がtrue
になっていたので、特に依存ライブラリーは必要ないようです。だから、多分Androidでも使えるんじゃないかな。まあ、Androiderの人はGsonかJsonPullParser使うだろうから、Gensonを使うことはないだろうけど…
Scala
あっ(試すの忘れた…
Jersey
Jerseyでも試そうと思って、今流行りのpayara-microでやることにしましたが、こちらはだいぶハマったので別の機会に…
おわり