もはや若干やけくそ感もあるLagomを試してみる第12回です。今回はLagomにおけるJSONのシリアライズ/デシリアライスを見ていきたいと思います。
ここまで見てきたようにLagomはHTTPやWebScoketなど外部とのやりとりにはJSONを使用しますが、コマンドやイベントといった内部のアクター間のメッセージのやり取りにもJSONを使うことが推奨されており、Jsonable
というマーカインターフェースを実装していればJSONにシリアライズ/デシリアライズしてくれるようです。また、大きなメッセージの場合は代わりにCompressedJsonable
インターフェースを実装することで圧縮を行うこともできるようです。
LagomのJSON変換はJacksonが用いられており、デフォルトでは以下のモジュールが有効になっているようです。
# The Jackson JSON serializer will register these modules.
# It is also possible to use jackson-modules = ["*"] to dynamically
# find and register all modules in the classpath.
jackson-modules = [
"com.fasterxml.jackson.module.paramnames.ParameterNamesModule",
"com.fasterxml.jackson.datatype.jdk8.Jdk8Module",
"com.fasterxml.jackson.datatype.jsr310.JavaTimeModule",
"com.fasterxml.jackson.datatype.pcollections.PCollectionsModule",
"com.fasterxml.jackson.datatype.guava.GuavaModule"]
加えてJavaオブジェクトとJSONの変換を行うだけでなく、JSONのスキーマ変更時の差分を吸収するための機能も提供しています。
プロパティの削除は特に気にせずフィールドを削除すればいいのですが、追加する場合でなおかつ初期値が必要な場合は@Value.Default
アノテーション付きのメソッドを定義しておけばいいようです。
@Value.Immutable
@ImmutableStyle
@JsonDeserialize(as = ItemAdded.class)
public interface AbstractItemAdded extends Jsonable {
...
@Value.Default
default String getNote() {
return "";
}
}
フィールドのリネームや構造の変更など、複雑なマイグレーションの場合はJSONをバージョニングしておき、バージョン番号を見て任意の変換処理を走らせることができます。
この場合、以下のようにJacksonJsonMigration
を継承したマイグレーションクラスを作成する必要があります。
public class ItemAddedMigration extends JacksonJsonMigration {
@Override
public int currentVersion() {
return 2;
}
@Override
public JsonNode transform(int fromVersion, JsonNode json) {
ObjectNode root = (ObjectNode) json;
if (fromVersion <= 1) {
root.set("itemId", root.get("productId"));
root.remove("productId");
}
return root;
}
}
そしてapplication.conf
でJSONに変換するクラスとマイグレーションクラスを紐付けます。
lagom.serialization.json.migrations {
"com.myservice.event.ItemAdded" = "com.myservice.event.ItemAddedMigration"
}
バージョンはアクターのメッセージのマニフェストに格納されているようです。Lagomのシリアライズ/デシリアライズの仕組みはAkkaのシリアライズ機構をうまく活用しているように見えますね。