依赖
要使用Jackson序列化,必须在项目中添加以下依赖项:
介绍
您可以在"序列化"部分中找到有关Akka序列化的概念。 本节描述如何使用Jackson序列化应用程序特定的消息、持久性事件和快照。
Jackson支持基于文本的JSON和二进制格式。
在许多情况下,Jackson可以对普通类进行序列化而无需任何其他提示,但是有时需要使用注释来指定如何将对象转换为JSON/字节。
用法
要为某个类启用Jackson序列化,您需要在序列化绑定配置中配置它或它的一个超类。 通常,您将为此创建一个标记器接口,并让消息实现该接口。
然后,在序列化绑定中将标记器接口的类名称配置为:jackson-json或jackson-cbor
一个好的约定是将标记接口命名为CborSerializable或JsonSerializable。在本文档中,我们使用MySerializable来明确表明Akka不提供标记接口本身。
这是Jackson理解结构的基础类所需要的。下面介绍了需要注释的几种情况。
请注意,只有顶级类或其标记接口必须在serialization-bindings定义,而不是它在成员字段中引用的嵌套类。
注意
添加-parameters Java编译器选项,以供ParameterNamesModule使用。它减少了某些注释。
安全
出于安全原因,不允许将Jackson序列化程序绑定到开放式类型,可能是序列化小工具的目标对象,例如:
- java.lang.Object
- java.io.Serializable
- java.util.Comparable。
Jackson数据绑定定义的可能序列化小工具类的拒绝列表,需要检查并禁止反序列化。
警告
不要使用@JsonTypeInfo(use = Id.CLASS)或ObjectMapper.enableDefaultTyping,因为使用多态类型会带来安全隐患。
格式
支持以下格式,如上所述,您选择在序列化绑定配置中使用哪种格式。
- jackson-json-基于普通文本的JSON
- jackson-cbor-二进制CBOR数据格式
二进制格式比JSON格式更紧凑,性能稍好。
注解
单参数构造函数
您可能会遇到如下异常:
MismatchedInputException:无法构造`...`的实例(尽管存在至少一个Creator):无法从Object值反序列化(没有基于委托或基于属性的Creator)
这可能是因为该类的构造函数带有单个参数,例如:
可以通过添加@JsonCreator或@JsonProperty注解来解决:
或者
如Jackson文档中所述,使用JsonCreator.Mode.PROPERTIES配置ParameterNamesModule。
多态类型
多态类型是某个基本类型具有多个替代实现的情况。 如果嵌套字段或集合是多态类型,则必须使用@JsonTypeInfo和@JsonSubTypes注解列出该类型的具体实现。
例:
如果您尚未定义注释,则会看到如下异常:
InvalidDefinitionException:无法构造`...`的实例(不存在任何创建者,如默认构造一样):抽象类型需要映射到具体类型,具有自定义反序列化器或包含其他类型信息
请注意,对于顶级类,这不是必需的,但对于其中的字段,则需要。在此示例中,在动物园内部使用动物,该动物作为消息发送或保留。如果动物是独立发送或持久保存的,则不需要注释,因为被序列化的是具体的子类Lion或Elephant。
在使用这些注解指定允许的子类时,类名将不包括在序列化表示中,这对于防止反序列化时加载恶意序列化小工具很重要。
警告
不要使用@JsonTypeInfo(use=Id.CLASS)或ObjectMapper.enableDefaultTyping,因为使用多态类型会带来安全隐患。
架构演变
在使用事件溯源以及进行滚动更新时,架构演变成为开发应用程序的重要方面。需求以及我们对业务领域的理解可能会(并且将会)随时间而变化。
Jackson序列化器提供了一种在反序列化期间执行JSON树模型转换的方法。对于文本格式和二进制格式,它以相同的方式工作。
我们将研究几种有关类如何演变的方案。
移除字段
无需迁移任何代码即可删除字段。 Jackson序列化程序将忽略该类中不存在的属性。
新增字段
无需迁移任何代码即可添加可选字段。默认值为Optional.empty。
旧类:
具有新的可选Discount属性和具有默认值的note字段的新类:
假设我们要拥有一个没有默认值的必填字段discount:
要添加新的必填字段,我们必须使用JacksonMigration类并在迁移代码中设置默认值。
这是添加discount字段的迁移类的样子:
覆盖currentVersion方法以定义当前(最新)版本的版本号。 当不使用任何迁移时,第一个版本始终为1。只要执行不带迁移代码就无法向后兼容的更改,请增加此版本号。
使用transform方法将旧的JSON结构转换为新的JSON结构。 JsonNode是可变的,因此您可以添加和删除字段,或更改值。 请注意,您必须转换为特定的子类,例如ObjectNode和ArrayNode才能访问更改器。
迁移类必须在配置文件中定义:
note字段可能也做同样的事情,在ItemAddedMigration中添加默认值""。
重命名字段
假设我们要在上一个示例中将productId字段重命名为itemId。
迁移代码如下:
结构变化
以类似的方式,我们可以进行任意的结构更改。
旧类:
新类
Address 类
迁移代码如下:
重命名类
也可以重命名该类。 例如,让我们将OrderAdded重命名为OrderPlaced。
旧类:
新类
迁移代码如下:
请注意,重写transformClassName方法,定义新的类名称。
必须使用旧的类名作为键来配置这种类型的迁移。可以删除实际的类。
从序列化绑定中删除
当某个类不再用于序列化时,可以将其从序列化绑定中删除,但仍要允许反序列化,则必须在allowed-class-prefix配置中列出该类。例如,在通过序列化更改进行滚动更新期间,或在读取旧的存储数据时,此功能很有用。从Jackson序列化程序更改为另一个序列化程序(例如Protobuf)从而更改序列化绑定时,也可以使用它,但是仍然可以使用Jackson来反序列化旧数据。
这是类名称或类名称前缀的列表。
Jackson模块
默认情况下,以下Jackson模块是启用的:
您可以修改配置akka.serialization.jackson.jackson-modules以启用其他模块。
ParameterNamesModule要求启用-parameters Java编译器选项。
压缩
JSON可能很冗长,对于大型消息,压缩大型有效负载可能会有所帮助。 对于jackson-json绑定,默认配置为:
支持的压缩算法为:gzip,lz4。使用"关闭"禁用压缩。 Gzip通常比lz4慢。大于compress-larger-than属性的消息将被压缩。
可以通过将算法属性设置为关闭来禁用压缩。它仍然能够解压缩序列化时压缩的有效载荷,例如如果更改此配置。
对于jackson-cbor和自定义绑定,默认情况下禁用jackson-json压缩,但可以采用与上述配置相同的方式启用,但用绑定名称替换jackson-json(例如jackson-cbor)。
附加配置
每个绑定的配置
默认情况下,在akka.serialization.jackson中定义了Jackson序列化程序及其ObjectMappers的配置。可以在akka.serialization.jackson.
也可以定义多个绑定并为它们使用不同的配置。例如,远程消息和持久事件的不同设置。
无清单序列化
当使用Jackson序列化程序进行持久化时,考虑到标准类名存储在清单中,这会导致大量磁盘和IO浪费,尤其是在事件较小的情况下。为解决此问题,可以关闭"type-in-manifest"标志,这将导致清单中不显示类名。
反序列化时,Jackson序列化程序将使用在反序列化类型中定义的类型(如果存在),否则它将寻找一个序列化绑定类,并使用该序列化绑定类。为了使此功能有用,通常单个类型必须是多态类型,并且所有类型信息都必须反序列化为JSON消息中包含的各种子类型。
例如,在切换序列化程序时,如果按照此处所述进行滚动更新,则可能会有一段时间没有为该类型声明序列化绑定。在这种情况下,必须使用反序列化类型配置属性来指定应使用哪种类型对消息进行反序列化。
由于此配置只能应用于单个根类型,因此通常只希望将其应用于每个绑定配置,而不是常规的jackson-json或jackson-cbor配置。
请注意,Akka远程处理已经实现了清单压缩,因此,此优化对通过远程处理发送的消息不会产生重大影响。 仅适用于为其他目的而序列化的消息,例如持久性或分布式数据。
附加功能
其他Jackson序列化功能可以在配置中启用/禁用。 除了在Akka的默认配置中更改的以下内容外,还使用了Jackson的默认值。
日期/时间格式
默认情况下,WRITE_DATES_AS_TIMESTAMPS和WRITE_DURATIONS_AS_TIMESTAMPS被禁用,这意味着日期/时间字段以ISO-8601(rfc3339)yyyy-MM-dd'T'HH:mm:ss.SSSZZ格式而不是数字数组进行序列化。 这对于互操作性更好,但速度较慢。 如果您不需要ISO格式即可与外部系统进行互操作,则可以更改以下配置,以更好地显示日期/时间字段。
Jackson仍然可以反序列化其他格式,而无需考虑此设置。