利用Jackson解決Json序列化和反序列化問題
前言
在前后端分離的應(yīng)用中,前端往往需要向后端發(fā)送命令請求,并將請求中的數(shù)據(jù)以Json格式傳遞。后端需要將Json格式的數(shù)據(jù)反序列化成Java對象,以便進行處理。但是,在實際應(yīng)用中,我們會遇到一些挑戰(zhàn),比如多態(tài)類型處理的需求,以及需要將多個API壓縮成一個API等。本文將介紹如何使用Jackson庫解決這些問題。
Jackson庫簡介
Jackson庫概述
Jackson是一個用于處理Json數(shù)據(jù)的Java庫,由FasterXML開發(fā)和維護。它提供了一系列功能,包括Json序列化和反序列化、Json樹模型、流式API等。
Jackson主要特性
- 支持Java對象與Json之間的轉(zhuǎn)換
- 支持Json樹模型
- 支持流式API
- 支持注解,靈活地控制序列化和反序列化過程
- 支持多態(tài)類型處理
JsonTypeInfo和JsonSubTypes的基本概念
JsonTypeInfo的作用與使用場景
在進行Json序列化和反序列化時,我們需要考慮Java對象的類型信息,以便正確地將Json數(shù)據(jù)轉(zhuǎn)換成Java對象。JsonTypeInfo提供了一種機制,用于在序列化和反序列化時處理Java對象的類型信息。
JsonSubTypes的作用與使用場景
JsonSubTypes是JsonTypeInfo的一個子注解,用于指定Java類的子類。當我們需要序列化和反序列化一個包含多態(tài)類型的Java對象時,我們可以使用@JsonTypeInfo和@JsonSubTypes注解來指定子類的類型信息。
實際應(yīng)用案例
代碼示例:動物園
假設(shè)我們有一個動物園,里面有各種動物,包括狗、貓、魚等。我們需要將動物園中的動物序列化成Json格式,并將其發(fā)送到前端。我們定義一個Animal類作為父類,然后定義三個子類:Dog、Cat和Fish。具體代碼如下:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat"), @JsonSubTypes.Type(value = Fish.class, name = "fish") }) public abstract class Animal { private String name; public Animal(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Dog extends Animal { private String breed; public Dog(String name, String breed) { super(name); this.breed = breed; } public String getBreed() { return breed; } public void setBreed(String breed) { this.breed = breed; } } public class Cat extends Animal { private String color; public Cat(String name, String color) { super(name); this.color = color; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } } public class Fish extends Animal { private String species; public Fish(String name, String species) { super(name); this.species = species; } public String getSpecies() { return species; } public void setSpecies(String species) { this.species = species; } }
從父類到子類的序列化和反序列化
現(xiàn)在我們需要將動物園中的所有動物序列化成Json格式。我們可以使用ObjectMapper類的writeValueAsString()方法來實現(xiàn)。具體代碼如下:
List<Animal> animals = new ArrayList<>(); animals.add(new Dog("旺財", "金毛")); animals.add(new Cat("小花", "黑白")); animals.add(new Fish("尼莫", "小丑魚")); ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(animals);
反之,我們可以使用ObjectMapper類的readValue()方法將Json數(shù)據(jù)反序列化成Java對象。具體代碼如下:
List<Animal> animals = objectMapper.readValue(json, new TypeReference<List<Animal>>() {});
自定義類型信息屬性
我們可以通過@JsonTypeInfo注解的property屬性來自定義類型信息的屬性名。例如,我們將property屬性的值設(shè)置為"animalType",則Json數(shù)據(jù)中的類型信息屬性名也會變?yōu)?quot;animalType"。具體代碼如下:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "animalType") @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat"), @JsonSubTypes.Type(value = Fish.class, name = "fish") }) public abstract class Animal { ... }
好的,以下是使用markdown的翻譯和例子:
JsonTypeInfo類
JsonTypeInfo
類是一個注解,用于配置在JSON序列化和反序列化中使用類型信息的詳細信息,以保留有關(guān)對象實例的實際類的信息。這對于多態(tài)類型是必需的,并且還可能需要鏈接抽象聲明類型和匹配的具體實現(xiàn)。
參數(shù)方法
JsonTypeInfo
類的參數(shù)方法如下:
use
:定義如何使用類型信息。有三個選項:Id.CLASS
,Id.NAME
和Id.MINIMAL_CLASS
。默認值為Id.CLASS
。
include
:定義類型信息的包含方式。有四個選項:As.PROPERTY
,As.WRAPPER_OBJECT
,As.EXISTING_PROPERTY
和As.EXTERNAL_PROPERTY
。默認值為As.PROPERTY
。
property
:定義包含類型信息的屬性的名稱。默認值為"$type"
。
例子
以下是使用JsonTypeInfo
類的一些例子:
// 將Java類名("com.myempl.ImplClass")包含在JSON屬性"class"中 @JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY, property = "class") // 將邏輯類型名稱(在實現(xiàn)類中定義)作為包裝器包含;2個注釋 @JsonTypeInfo(use = Id.NAME, include = As.WRAPPER_OBJECT) @JsonSubTypes({ @JsonSubTypes.Type(value = com.myemp.Impl1.class, name = "impl1"), @JsonSubTypes.Type(value = com.myempl.Impl2.class, name = "impl2") })
在第一個例子中,use
參數(shù)設(shè)置為Id.CLASS
,這意味著使用Java類名作為類型信息。include
參數(shù)設(shè)置為As.PROPERTY
,這意味著類型信息將包含在JSON屬性"class"
中。property
參數(shù)設(shè)置為"class"
,這是包含類型信息的屬性的名稱。
在第二個例子中,use
參數(shù)設(shè)置為Id.NAME
,這意味著使用邏輯類型名稱作為類型信息。include
參數(shù)設(shè)置為As.WRAPPER_OBJECT
,這意味著類型信息將作為包裝器包含。@JsonSubTypes
注解用于指定實現(xiàn)類,并將它們的邏輯類型名稱與之關(guān)聯(lián)。
JsonSubTypes Annotation
@JsonSubTypes
是一個用于與 @JsonTypeInfo
一起使用的注解,用于指定可序列化的多態(tài)類型的子類型,并將邏輯名稱與 JSON 內(nèi)容中使用的名稱關(guān)聯(lián)起來。
注意,僅注釋屬性或基本類型不會啟用多態(tài)類型處理:此外,需要使用 @JsonTypeInfo
或等效注釋(例如啟用所謂的“默認鍵入”)注釋,并且僅在這種情況下才使用子類型信息。
可用的元素
@JsonSubTypes
注解具有以下元素:
元素 | 類型 | 描述 |
---|---|---|
value | Type[] | 注釋類型的子類型(注釋類或關(guān)聯(lián)到注釋方法的屬性值類型)。這些將被遞歸檢查,以便可以僅通過包含直接子類型來定義類型 |
@Type | 注解 | 子類型的定義和可選名稱。如果未定義名稱(空字符串將被忽略),類型的類將被檢查是否有 @JsonTypeName 注解;如果也缺少或為空,則將通過類型 id 機制構(gòu)造默認名稱。默認名稱通?;陬惷?/td> |
Type
@Type
是 @JsonSubTypes
中的一個注解,用于聲明子類型。
@Type
注解具有以下元素:
元素 | 類型 | 描述 |
---|---|---|
value | Class<?> | 子類型的類 |
name | String | 類型標識符所使用的邏輯類型名稱,如果定義了,則為空字符串。除非 names 定義為非空。默認情況下使用,如果類沒有 @JsonTypeName 注解的話,會使用默認名稱構(gòu)造類型 id 機制 |
names | String[] | (可選)用于類的類型標識符的邏輯類型名稱:如果應(yīng)將多個類型名稱與同一類型關(guān)聯(lián),則使用 |
使用示例
下面是一個示例,展示了如何使用 @JsonSubTypes
注解:
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type" ) @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat") }) public abstract class Animal { // ... }
在上面的示例中,Animal
是一個抽象類,它被注釋為可以有子類型。@JsonTypeInfo
注解指定了要使用名稱作為類型標識符,并將類型標識符作為 JSON 屬性的一部分包含在 JSON 中。@JsonSubTypes
注解指定了 Dog
和 Cat
作為 Animal
的子類型,分別使用名稱 "dog" 和 "cat" 作為它們的類型標識符。
高級用法
解決循環(huán)引用問題
在進行Json序列化時,如果Java對象之間存在循環(huán)引用,就會導(dǎo)致序列化失敗。為了解決這個問題,我們可以使用@JsonIdentityInfo注解來指定對象的標識信息。具體代碼如下:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Employee { private int id; private String name; private Employee manager; public Employee(int id, String name, Employee manager) { this.id = id; this.name = name; this.manager = manager; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Employee getManager() { return manager; } public void setManager(Employee manager) { this.manager = manager; } }
自定義類型解析器
有時候,我們需要對一些特殊類型的數(shù)據(jù)進行特殊處理,比如將日期字符串轉(zhuǎn)換成Date對象。我們可以使用JsonDeserializer和JsonSerializer來自定義類型的解析和序列化過程。具體代碼如下:
public class DateDeserializer extends JsonDeserializer<Date> { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { String dateStr = jsonParser.getText(); try { return dateFormat.parse(dateStr); } catch (ParseException e) { throw new RuntimeException(e); } } } public class DateSerializer extends JsonSerializer<Date> { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { jsonGenerator.writeString(dateFormat.format(date)); } } @JsonDeserialize(using = DateDeserializer.class) @JsonSerialize(using = DateSerializer.class) public class Event { private String name; private Date date; public Event(String name, Date date) { this.name = name; this.date = date; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } }
使用@JsonTypeName自定義類型名稱
有時候,我們希望在Json數(shù)據(jù)中使用自定義的類型名稱,而不是Java類的名稱。可以使用@JsonTypeName注解來實現(xiàn)。具體代碼如下:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat"), @JsonSubTypes.Type(value = Fish.class, name = "fish") }) public abstract class Animal { ... } @JsonTypeName("dog") public class Dog extends Animal { ... }
性能優(yōu)化
避免重復(fù)解析
在進行Json序列化和反序列化時,我們可能會重復(fù)解析同一個Json數(shù)據(jù)。為了提高性能,我們可以使用ObjectMapper類的readTree()方法將Json數(shù)據(jù)解析成JsonNode對象,并在需要的時候?qū)ζ溥M行操作。具體代碼如下:
JsonNode root = objectMapper.readTree(json); JsonNode nameNode = root.path("name"); String name = nameNode.asText();
使用緩存機制
在進行Json序列化和反序列化時,我們可以使用緩存機制來提高性能。Jackson庫提供了一個ObjectReader類,它可以緩存Json數(shù)據(jù)的解析結(jié)果,并在下次解析相同的Json數(shù)據(jù)時直接返回緩存結(jié)果,從而避免重復(fù)解析。具體代碼如下:
ObjectReader reader = objectMapper.readerFor(new TypeReference<List<Animal>>() {}); List<Animal> animals = reader.readValue(json);
注意事項與最佳實踐
處理不同版本的數(shù)據(jù)結(jié)構(gòu)
在進行Json序列化和反序列化時,我們需要考慮到數(shù)據(jù)結(jié)構(gòu)的版本變化。如果數(shù)據(jù)結(jié)構(gòu)發(fā)生變化,我們需要進行相應(yīng)的兼容處理,以確保新版本的程序能夠正確地處理舊版本的數(shù)據(jù)。為了處理不同版本的數(shù)據(jù)結(jié)構(gòu),我們可以使用@JsonCreator和@JsonValue注解來自定義對象的構(gòu)造函數(shù)和toString()方法。
使用@JsonSubTypes的局限性
使用@JsonSubTypes注解時,需要注意它的局限性。它只能用于序列化和反序列化Java對象,無法用于處理基本類型和數(shù)組類型。
安全性考慮
在進行Json序列化和反序列化時,我們需要考慮到安全性問題。由于Json數(shù)據(jù)是開放的,存在被惡意篡改和注入的風險。為了防止這種風險,我們可以使用@JsonInclude注解來控制序列化時包含的屬性,以及使用@JsonCreator和@JsonValue注解來限制反序列化的輸入。此外,我們還可以使用JsonNode對象來手動解析Json數(shù)據(jù),以避免使用反序列化機制。
以上就是利用Jackson解決Json序列化和反序列化問題的詳細內(nèi)容,更多關(guān)于Jackson Json序列化與反序列化的資料請關(guān)注腳本之家其它相關(guān)文章!
- Jackson使用示例-Bean、XML、Json之間相互轉(zhuǎn)換
- 一篇文章了解Jackson注解@JsonFormat及失效解決辦法
- Java中對象?和?json?互轉(zhuǎn)四種方式?json-lib、Gson、FastJson、Jackson
- Java利用Jackson輕松處理JSON序列化與反序列化
- Jackson中json格式的字符串與對象的互相轉(zhuǎn)換方式
- 如何自定義Jackson序列化?@JsonSerialize
- JSON中fastjson、jackson、gson如何選擇
- jackson 如何將實體轉(zhuǎn)json json字符串轉(zhuǎn)實體
- 使用jackson實現(xiàn)對象json之間的相互轉(zhuǎn)換(spring boot)
- 使用Jackson-json解析一個嵌套的json字符串
- Jackson庫進行JSON?序列化時遇到了無限遞歸(Infinite?Recursion)的問題及解決方案
相關(guān)文章
Mybatis工具類JdbcTypeInterceptor運行時自動添加jdbcType屬性
今天小編就為大家分享一篇關(guān)于Mybatis工具類JdbcTypeInterceptor運行時自動添加jdbcType屬性,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12jdk17+springboot使用webservice的踩坑實戰(zhàn)記錄
這篇文章主要給大家介紹了關(guān)于jdk17+springboot使用webservice踩坑的相關(guān)資料,網(wǎng)上很多教程是基于jdk8的,所以很多在17上面跑不起來,折騰兩天,直接給答案,需要的朋友可以參考下2024-01-01spring中12種@Transactional的失效場景(小結(jié))
日常我們進行業(yè)務(wù)開發(fā)時,基本上使用的都是聲明式事務(wù),即為使用@Transactional注解的方式,本文主要介紹了spring中12種@Transactional的失效場景,感興趣的小伙伴們可以參考一下2022-01-01Java并發(fā)工具之Exchanger線程間交換數(shù)據(jù)詳解
這篇文章主要介紹了Java并發(fā)工具之Exchanger線程間交換數(shù)據(jù)詳解,Exchanger是一個用于線程間協(xié)作的工具類,Exchanger用于進行線程間的數(shù)據(jù)交 換,它提供一個同步點,在這個同步點,兩個線程可以交換彼此的數(shù)據(jù),需要的朋友可以參考下2023-12-12