Jackson 序列化忽略指定类型的属性

本文准确来讲是探讨如何用 Jackson 来序列化 Apache avro 对象,因为简单用 Jackson 来序列化 Apache avro 对象会报错。原因是序列化 Schema getSchema() 时会报错,后面会讲到,需要序列化时忽略该属性。那么能不能在 getSchema() 上加上 @JsonIgnore 来忽略该属性呢?原理上是通的。不过手工修改的 avsc 生成的 Java 文件随时会因为重新编译而还原,所以不太具有实际可操作性,当然通过定制编译 avsc 用的模板文件来加入 @JsonIgnore 是另一回事。

由于不能在要忽略的字段上添加 JsonIgnore 来控制,而如果我们明确了要忽略的字段类型的话,是能够定制 Jackson 的  ObjectMapper  来屏蔽某个特定的类型。来看下面序列化 Apache avro 对象的例子:

假设我们有一个 Apache 的 Schema 文件 user.avsc, 内容如下:

编译用 avro-tools compile schema user.avsc . 生成 cc.unmi.data.User.java 源文件,当我们试图对类型的对象用 Jackson 进行序列化时

收到异常(关键信息)

Caused by: org.apache.avro.AvroRuntimeException: Not a map: {"type":"record","name":"User","namespace":"cc.unmi.data","fields":[{"name":"name","type":"string"},{"name":"address","type":["string","null"]}]}
    at org.apache.avro.Schema.getValueType(Schema.java:294)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:664)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:689)

从上面的错误可以定位到 Jackson 的试图序列化 User 对象的

org.apache.avro.Schema 中的 getValueType() 直接抛出异常拒绝被归化

因此,要实现序列化 Apache avro 对象,解决的办法有三

  1. 凡是 org.apache.avro.Schema 的属性不被序列化(Schema 输出确实用处不大)
  2. 或对于org.apache.avro.Schema 类型的属性定制序列化,比如输出为完整类名,或 Schema 定义的文本内容
  3. 再来一个,对 SpecificRecordBase 类型的 schema 名称的属性进行忽略(avro 类型继承自 SpecificRecordBase)

它们的实现分别如下

忽略序列化指定类型的属性

先定义一个标注了 @JsonIgnoreType 的注解

序列化 Apache avro 对象前给 ObjectMapp 加一个 mixin

有了上面高度行的代码,这儿的 Apache avro User 对象就能被正常序列化了,输出为

{"name":"Yanbin","address":"Chicago"}

这样 getSchema() 返回的类型,或另何对象中有 org.apache.avro.Schema 类型的属性都会在序列化时忽略掉

定制 Schema 属的输出内容

对于 Schema 类型的属性,除了前面采取堵的方式,还可以因利疏导,即定制 Schema 属性值的输出内容

定制化 Schema 序列化方式

给 ObjectMapper 加上定制的序列化器

序列化后产生的输出如下

{"name":"Yanbin","address":"Chicago","schema":"cc.unmi.data.User"}

如果在 AvroSchemaSerializer 把 jgen.writeString(value.getFullName()) 替换如下

并且序列化后对内容进行格式化输出

{
    "name" : "Yanbin",
    "address" : "Chicago",
    "schema" : "{\"type\":\"record\",\"name\":\"User\",\"namespace\":\"cc.unmi.data\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"address\",\"type\":[\"string\",\"null\"]}]}"
}

如果 JSON 中仍有 schema 属性的话,在反序列化该属性时会出错,因为类型不一致了,解决办法是要为反序列化用的 ObjectMapper 设置

指定特定对象的属性名进行过滤

从语义上除了 Ignore 外,Filter 也像是干这事的,可以尝试过下面的方式, 分两步走

定义一个带 @JsonFilter 的注解,也是不显示注解到任何类

给 ObjectMapper 设置 filter

输出效果没有意外,也能避免序列化 schema 属性

{"name":"Yanbin","address":"Chicago"}

这最后一种方式是本篇写作行将结束时找到并验证的,所以不写出来,不进行梳理可能永远只会第一种方法。

链接:

  1. Jackson Ignore Properties on Marshalling
  2. How do I exclude fields with Jackson not using annotations

类别: Java/JEE. 标签: , , . 阅读(234). 订阅评论. TrackBack.

Leave a Reply

avatar