もはや若干やけくそ感もある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のシリアライズ機構をうまく活用しているように見えますね。