如何利用Jackson序列化忽略指定类型的属性详解
作者:隔叶黄莺 发布时间:2023-11-16 09:01:27
前言
本文准确来讲是探讨如何用 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"}
这最后一种方式是本篇写作行将结束时找到并验证的,所以不写出来,不进行梳理可能永远只会第一种方法。
链接:
Jackson Ignore Properties on Marshalling
How do I exclude fields with Jackson not using annotations
来源:https://yanbin.blog/jackson-ignore-specified-field-type/
![](https://www.aspxhome.com/images/zang.png)
![](https://www.aspxhome.com/images/jiucuo.png)
猜你喜欢
- 今天来了一个问题:软键盘无法弹出。分析后是因为系统判断当前有外接硬键盘,就会隐藏软键盘。但实际情况并不是这么简单,该问题只有在特定条件下偶现
- 重写 equals()方法 和 hashCode()方法最近看了学习了集合的简单的知识,碰到了讲解 Set 的部分,感觉很好奇,这里对于 S
- 前言应用系统需要通过Cache来缓存不经常改变得数据来提高系统性能和增加系统吞吐量,避免直接访问数据库等低速存储系统。缓存的数据通常存放在访
- 一. spring配置文件:application.xml<?xml version="1.0" encoding
- 前言空间分配要点有:一是空间分配的连续性;二是动态内存申请;三是防止程序执行中出现异常错误。提示:开始讲解了嗷~后续会根据精力持续更新嗷!!
- 使用开源项目JAVAE 进行视频格式转换JAVAE简介:JAVE (Java音频视频编码器)库是ffmpeg项目的Java包装器。开发人员可
- 注解从java5开始加入这一特性,发展到现在已然是遍地开花,在很多框架中得到了广泛的使用,用来简化程序中的配置。那充满争议的类型注解究竟是什
- 在使用JDBC的时候,数据库据连接是非常宝贵的资源。为了复用这些资源,可以将连接保存在一个队列中。当需要的时候可以从队列中取出未使用的连接。
- 采用继承Thead类实现多线程:优势:编写简单,如果需要访问当前线程,只需使用this即可,无需使用Thead.currentThread(
- 1.定义Token的注解,需要Token校验的接口,方法上加上此注解import java.lang.annotation.ElementT
- 使用foreach循环的坑我们首先看一段MyBatis中使用foreach循环的sql:SELECT * FROM table where
- /** * 三角数字: * 比达哥斯拉领导下的古希腊数学家发现了一个有趣的数字序列1, 3, 6, 10, 15, 21,... *
- Object(四大方法):文章干货满满,耐性看完~~何为Object?首先先来看看官方对Object的介绍:在这里附上Java官方的查阅工具
- 随着目前微信越来越火,所以研究微信的人也就越来越多,这不前一段时间,我们公司就让我做一个微信公众号中问卷调查发红包功能,经过一段时间的研究,
- 昨天写了一篇Redis布隆过滤器相关的命令的文章,今天来说一说springboot中如何简单在代码中使用布隆过滤器吧。目前市面上也有好几种实
- 本文实例为大家分享了java GUI学生图书管理的具体代码,供大家参考,具体内容如下- mysql数据库建表:1.book表 2.bs借书记
- 前言古语有云:道为术之灵,术为道之体;以道统术,以术得道。其中:“道”指“规律、道理、理论”,“术”指“方法、技巧、技术”。意思是:“道”是
- 本文实例讲述了Android中SeekBar和RatingBar用法。分享给大家供大家参考,具体如下:什么是SeekBar?可以拖动的进度条
- 线程中断机制提供了一种方法,用于将线程从阻塞等待中唤醒,尝试打断目标线程的现有处理流程,使之响应新的命令。Java 留给开发者这一自由,我们
- 仅供学习交流,禁止商业用途。如侵害利益,联系必删!前言最近一位小伙伴钟爱二次元文化,于是找到半次元这个app,但是很快他就遇到了问题。一、案