前言

專注于為中小企業提供網站制作、成都網站制作服務,電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業萬榮免費做網站提供優質的服務。我們立足成都,凝聚了一批互聯網行業人才,有力地推動了成百上千家企業的穩健成長,幫助中小企業通過網站建設實現規模擴充和轉變。
本文準確來講是探討如何用 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 對象,解決的辦法有三
它們的實現分別如下
忽略序列化指定類型的屬性
先定義一個標注了 @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序列化忽略指定類型的屬性詳解
網站路徑:http://www.yijiale78.com/article48/pchcep.html
成都網站建設公司_創新互聯,為您提供品牌網站設計、虛擬主機、外貿建站、、品牌網站制作、定制開發
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯