如何利用Jackson序列化忽略指定类型的属性详解

 更新时间:2018年11月01日 11:07:15   作者:隔叶黄莺  
这篇文章主要给大家介绍了关于如何利用Jackson序列化忽略指定类型的属性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

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

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

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

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

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

ObjectMapper objectMapper = new ObjectMapper() ;
User user = User.newBuilder().setName("Yanbin").setAddress("Chicago").build();
System.out.println(objectMapper.writeValueAsString(user));

收到异常(关键信息)

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 对象的

 public org.apache.avro.Schema getSchema() { return SCHEMA$; }

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

 public Schema getValueType() {
 throw new AvroRuntimeException("Not a map: "+this);
 }

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

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

它们的实现分别如下

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

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

@JsonIgnoreType
@interface IgnoreAvroSchemaField {
}

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

ObjectMapper objectMapper = new ObjectMapper() ;
objectMapper.addMixIn(Schema.class, IgnoreAvroSchemaField.class);
 
User user = User.newBuilder().setName("Yanbin").setAddress("Chicago").build();
System.out.println(objectMapper.writeValueAsString(user));

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

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

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

定制 Schema 属的输出内容

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

定制化 Schema 序列化方式

class AvroSchemaSerializer extends JsonSerializer<Schema> {
 
 @Override
 public void serialize(Schema value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
 jgen.writeString(value.getFullName()); //直接输出当前 Apache avro 对象的全限类名
 }
}

给 ObjectMapper 加上定制的序列化器

ObjectMapper objectMapper = new ObjectMapper() ;
SimpleModule simpleModule = new SimpleModule("SimpleModule", Version.unknownVersion());
simpleModule.addSerializer(Schema.class, new AvroSchemaSerializer());
objectMapper.registerModule(simpleModule);
 
User user = User.newBuilder().setName("Yanbin").setAddress("Chicago").build();
System.out.println(objectMapper.writeValueAsString(user));

序列化后产生的输出如下

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

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

 jgen.writeString(value.toString());

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

 System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user));
{
 "name" : "Yanbin",
 "address" : "Chicago",
 "schema" : "{\"type\":\"record\",\"name\":\"User\",\"namespace\":\"cc.unmi.data\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"address\",\"type\":[\"string\",\"null\"]}]}"
}

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

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

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

@JsonFilter("filter out apache avro schema field") //字符串值要与下面 addFilter("xxx") 保持一致
class PropertyFilterMixIn {}

给 ObjectMapper 设置 filter

 ObjectMapper objectMapper = new ObjectMapper() ;
 objectMapper.addMixIn(SpecificRecordBase.class, PropertyFilterMixIn.class); //对 SpecificRecordBase 类型的对象应用
 FilterProvider filterProvider = new SimpleFilterProvider() //对 SpecificRecordBase 类型(如 User) 的名为 "schema" 属性屏蔽
  .addFilter("filter out apache avro schema field", SimpleBeanPropertyFilter.serializeAllExcept("schema"));
 objectMapper.setFilterProvider(filterProvider);
 
 User user = User.newBuilder().setName("Yanbin").setAddress("Chicago").build();
 System.out.println(objectMapper.writeValueAsString(user));

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

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

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

链接:

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

相关文章

  • Java中List排序的三种实现方法实例

    Java中List排序的三种实现方法实例

    其实Java针对数组和List的排序都有实现,对数组而言你可以直接使用Arrays.sort,对于List和Vector而言,你可以使用Collections.sort方法,下面这篇文章主要给大家介绍了关于Java中List排序的三种实现方法,需要的朋友可以参考下
    2021-12-12
  • SpringApplicationRunListener监听器源码详解

    SpringApplicationRunListener监听器源码详解

    这篇文章主要介绍了SpringApplicationRunListener监听器源码详解,springboot提供了两个类SpringApplicationRunListeners、SpringApplicationRunListener(EventPublishingRunListener),spring框架还提供了一个ApplicationListener接口,需要的朋友可以参考下
    2023-11-11
  • java实现文本框和文本区的输入输出

    java实现文本框和文本区的输入输出

    这篇文章主要介绍了java实现文本框和文本区的输入输出的方法和具体示例,有需要的小伙伴可以参考下。
    2015-06-06
  • java项目中的绝对路径和相对路径用法说明

    java项目中的绝对路径和相对路径用法说明

    这篇文章主要介绍了java项目中的绝对路径和相对路径用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • 一文详解Java线程中的安全策略

    一文详解Java线程中的安全策略

    Java中的线程到底有哪些安全策略呢?这篇文章将详细为大家分析一下。文中的示例代码讲解详细,对我们学习或工作有一定帮助,需要的可以参考一下
    2022-05-05
  • SpringBoot2整合Drools规则引擎及案例详解

    SpringBoot2整合Drools规则引擎及案例详解

    这篇文章主要介绍了SpringBoot2整合Drools规则引擎及案例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • MyBatis Generator配置入门

    MyBatis Generator配置入门

    本文主要介绍了MyBatis Generator配置入门,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • java 线程池封装及拒绝策略示例详解

    java 线程池封装及拒绝策略示例详解

    这篇文章主要为大家介绍了java 线程池封装及拒绝策略示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 关于spring的自定义缓存注解分析

    关于spring的自定义缓存注解分析

    这篇文章主要介绍了关于spring的自定义缓存注解分析,因为所有的key的失效时间都一样,要想实现不同的key不同的失效时间,就得需要自定义缓存注解,需要的朋友可以参考下
    2023-05-05
  • 深入理解Java编程线程池的实现原理

    深入理解Java编程线程池的实现原理

    这篇文章主要介绍了深入理解Java编程线程池的实现原理,涉及ThreadPoolExecutor类,线程池实现原理及示例等相关内容,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11

最新评论