如何利用Jackson序列化忽略指定类型的属性详解
前言
本文准确来讲是探讨如何用Jackson来序列化Apacheavro对象,因为简单用Jackson来序列化Apacheavro对象会报错。原因是序列化SchemagetSchema()时会报错,后面会讲到,需要序列化时忽略该属性。那么能不能在getSchema()上加上@JsonIgnore来忽略该属性呢?原理上是通的。不过手工修改的avsc生成的Java文件随时会因为重新编译而还原,所以不太具有实际可操作性,当然通过定制编译avsc用的模板文件来加入@JsonIgnore是另一回事。
由于不能在要忽略的字段上添加JsonIgnore来控制,而如果我们明确了要忽略的字段类型的话,是能够定制Jackson的 ObjectMapper 来屏蔽某个特定的类型。来看下面序列化Apacheavro对象的例子:
假设我们有一个Apache的Schema文件user.avsc,内容如下:
{ "namespace":"cc.unmi.data", "type":"record", "name":"User", "fields":[ {"name":"name","type":"string"}, {"name":"address","type":["string","null"]} ] }
编译用avro-toolscompileschemauser.avsc.生成cc.unmi.data.User.java源文件,当我们试图对类型的对象用Jackson进行序列化时
ObjectMapperobjectMapper=newObjectMapper(); Useruser=User.newBuilder().setName("Yanbin").setAddress("Chicago").build(); System.out.println(objectMapper.writeValueAsString(user));
收到异常(关键信息)
Causedby:org.apache.avro.AvroRuntimeException:Notamap:{"type":"record","name":"User","namespace":"cc.unmi.data","fields":[{"name":"name","type":"string"},{"name":"address","type":["string","null"]}]}
atorg.apache.avro.Schema.getValueType(Schema.java:294)
atcom.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:664)
atcom.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:689)
从上面的错误可以定位到Jackson的试图序列化User对象的
publicorg.apache.avro.SchemagetSchema(){returnSCHEMA$;}
而org.apache.avro.Schema中的getValueType()直接抛出异常拒绝被归化
publicSchemagetValueType(){ thrownewAvroRuntimeException("Notamap:"+this); }
因此,要实现序列化Apacheavro对象,解决的办法有三
- 凡是org.apache.avro.Schema的属性不被序列化(Schema输出确实用处不大)
- 或对于org.apache.avro.Schema类型的属性定制序列化,比如输出为完整类名,或Schema定义的文本内容
- 再来一个,对SpecificRecordBase类型的schema名称的属性进行忽略(avro类型继承自SpecificRecordBase)
它们的实现分别如下
忽略序列化指定类型的属性
先定义一个标注了@JsonIgnoreType的注解
@JsonIgnoreType @interfaceIgnoreAvroSchemaField{ }
序列化Apacheavro对象前给ObjectMapp加一个mixin
ObjectMapperobjectMapper=newObjectMapper(); objectMapper.addMixIn(Schema.class,IgnoreAvroSchemaField.class); Useruser=User.newBuilder().setName("Yanbin").setAddress("Chicago").build(); System.out.println(objectMapper.writeValueAsString(user));
有了上面高度行的代码,这儿的ApacheavroUser对象就能被正常序列化了,输出为
{"name":"Yanbin","address":"Chicago"}
这样getSchema()返回的类型,或另何对象中有org.apache.avro.Schema类型的属性都会在序列化时忽略掉
定制Schema属的输出内容
对于Schema类型的属性,除了前面采取堵的方式,还可以因利疏导,即定制Schema属性值的输出内容
定制化Schema序列化方式
classAvroSchemaSerializerextendsJsonSerializer{ @Override publicvoidserialize(Schemavalue,JsonGeneratorjgen,SerializerProviderprovider)throwsIOException{ jgen.writeString(value.getFullName());//直接输出当前Apacheavro对象的全限类名 } }
给ObjectMapper加上定制的序列化器
ObjectMapperobjectMapper=newObjectMapper(); SimpleModulesimpleModule=newSimpleModule("SimpleModule",Version.unknownVersion()); simpleModule.addSerializer(Schema.class,newAvroSchemaSerializer()); objectMapper.registerModule(simpleModule); Useruser=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("filteroutapacheavroschemafield")//字符串值要与下面addFilter("xxx")保持一致 classPropertyFilterMixIn{}
给ObjectMapper设置filter
ObjectMapperobjectMapper=newObjectMapper(); objectMapper.addMixIn(SpecificRecordBase.class,PropertyFilterMixIn.class);//对SpecificRecordBase类型的对象应用 FilterProviderfilterProvider=newSimpleFilterProvider()//对SpecificRecordBase类型(如User)的名为"schema"属性屏蔽 .addFilter("filteroutapacheavroschemafield",SimpleBeanPropertyFilter.serializeAllExcept("schema")); objectMapper.setFilterProvider(filterProvider); Useruser=User.newBuilder().setName("Yanbin").setAddress("Chicago").build(); System.out.println(objectMapper.writeValueAsString(user));
输出效果没有意外,也能避免序列化schema属性
{"name":"Yanbin","address":"Chicago"}
这最后一种方式是本篇写作行将结束时找到并验证的,所以不写出来,不进行梳理可能永远只会第一种方法。
链接:
- JacksonIgnorePropertiesonMarshalling
- HowdoIexcludefieldswithJacksonnotusingannotations
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。