前言
專注于為中小企業(yè)提供網(wǎng)站制作、成都網(wǎng)站制作服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)萬榮免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了成百上千家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
本文準(zhǔn)確來講是探討如何用 Jackson 來序列化 Apache avro 對(duì)象,因?yàn)楹?jiǎn)單用 Jackson 來序列化 Apache avro 對(duì)象會(huì)報(bào)錯(cuò)。原因是序列化 Schema getSchema() 時(shí)會(huì)報(bào)錯(cuò),后面會(huì)講到,需要序列化時(shí)忽略該屬性。那么能不能在 getSchema() 上加上 @JsonIgnore 來忽略該屬性呢?原理上是通的。不過手工修改的 avsc 生成的 Java 文件隨時(shí)會(huì)因?yàn)橹匦戮幾g而還原,所以不太具有實(shí)際可操作性,當(dāng)然通過定制編譯 avsc 用的模板文件來加入 @JsonIgnore 是另一回事。
由于不能在要忽略的字段上添加 JsonIgnore 來控制,而如果我們明確了要忽略的字段類型的話,是能夠定制 Jackson 的 ObjectMapper 來屏蔽某個(gè)特定的類型。來看下面序列化 Apache avro 對(duì)象的例子:
假設(shè)我們有一個(gè) Apache 的 Schema 文件 user.avsc, 內(nèi)容如下:
{ "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 源文件,當(dāng)我們?cè)噲D對(duì)類型的對(duì)象用 Jackson 進(jìn)行序列化時(shí)
ObjectMapper objectMapper = new ObjectMapper() ; User user = User.newBuilder().setName("Yanbin").setAddress("Chicago").build(); System.out.println(objectMapper.writeValueAsString(user));
收到異常(關(guān)鍵信息)
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)
從上面的錯(cuò)誤可以定位到 Jackson 的試圖序列化 User 對(duì)象的
public org.apache.avro.Schema getSchema() { return SCHEMA$; }
而 org.apache.avro.Schema 中的 getValueType() 直接拋出異常拒絕被歸化
public Schema getValueType() { throw new AvroRuntimeException("Not a map: "+this); }
因此,要實(shí)現(xiàn)序列化 Apache avro 對(duì)象,解決的辦法有三
它們的實(shí)現(xiàn)分別如下
忽略序列化指定類型的屬性
先定義一個(gè)標(biāo)注了 @JsonIgnoreType 的注解
@JsonIgnoreType @interface IgnoreAvroSchemaField { }
序列化 Apache avro 對(duì)象前給 ObjectMapp 加一個(gè) 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 對(duì)象就能被正常序列化了,輸出為
{"name":"Yanbin","address":"Chicago"}
這樣 getSchema() 返回的類型,或另何對(duì)象中有 org.apache.avro.Schema 類型的屬性都會(huì)在序列化時(shí)忽略掉
定制 Schema 屬的輸出內(nèi)容
對(duì)于 Schema 類型的屬性,除了前面采取堵的方式,還可以因利疏導(dǎo),即定制 Schema 屬性值的輸出內(nèi)容
定制化 Schema 序列化方式
class AvroSchemaSerializer extends JsonSerializer{ @Override public void serialize(Schema value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeString(value.getFullName()); //直接輸出當(dāng)前 Apache avro 對(duì)象的全限類名 } }
給 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));
序列化后產(chǎn)生的輸出如下
{"name":"Yanbin","address":"Chicago","schema":"cc.unmi.data.User"}
如果在 AvroSchemaSerializer 把 jgen.writeString(value.getFullName()) 替換如下
jgen.writeString(value.toString());
并且序列化后對(duì)內(nèi)容進(jìn)行格式化輸出
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\"]}]}" }
指定特定對(duì)象的屬性名進(jìn)行過濾
從語(yǔ)義上除了 Ignore 外,F(xiàn)ilter 也像是干這事的,可以嘗試過下面的方式, 分兩步走
定義一個(gè)帶 @JsonFilter 的注解,也是不顯示注解到任何類
@JsonFilter("filter out apache avro schema field") //字符串值要與下面 addFilter("xxx") 保持一致 class PropertyFilterMixIn {}
給 ObjectMapper 設(shè)置 filter
ObjectMapper objectMapper = new ObjectMapper() ; objectMapper.addMixIn(SpecificRecordBase.class, PropertyFilterMixIn.class); //對(duì) SpecificRecordBase 類型的對(duì)象應(yīng)用 FilterProvider filterProvider = new SimpleFilterProvider() //對(duì) 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"}
這最后一種方式是本篇寫作行將結(jié)束時(shí)找到并驗(yàn)證的,所以不寫出來,不進(jìn)行梳理可能永遠(yuǎn)只會(huì)第一種方法。
鏈接:
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)創(chuàng)新互聯(lián)的支持。