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