JDK 版本 |
---|
Gson 版本 |
---|
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.7</version> </dependency>
Gson
是實現對象序列化和反序列化的利器,可是Gson
在(反)序列化枚舉時默認是根據枚舉值的名稱來實現的,若是你想要在(反)序列化枚舉時輸出本身定義的枚舉屬性,那麼此時至少有兩種選擇:java
TypeAdapter
抽象類並實現其中的抽象方法JsonSerializer
和/或JsonDeserializer
接口我一般是選擇第二種方式,即實現接口的方式來自定義(反)序列化過程。好比下面這個實例:
針對JDK1.8
新的時間API
中的LocalDate
進行自定義(反)序列化過程:git
import com.google.gson.*; import java.lang.reflect.Type; import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class LocalDateTypeAdapter implements JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> { private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @Override public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) { return null == src ? null : new JsonPrimitive(formatter.format(src)); } @Override public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { return null == json ? null : LocalDate.parse(json.getAsString(), formatter); } }
TypeAdapter
的
Gson
在序列化
LocalDate
類型時,會調用其中的
serialize
方法,此時若是
LocalDate
實例非空的話,咱們將調用
formatter.format(src)
方法將
LocalDate
實例格式化成指定形式的字符串,而後封裝成
JsonPrimitive
實例並返回
Gson
,
Gson
再將返回的
JsonPrimitive
寫入到 json 字符串中;
同理,當Gson
反序列化時遇到LocalDate
類型時,會調用其中的deserialize
方法完成反序列化。github
好了,扯完基本實現以後,如今有一個問題:那就是針對單個枚舉使用這種方式那是無可厚非的。可是當你的項目中應用到的枚舉數量比較多的時候,若是依然採用這種方式,那麼隨着而來的問題是類的增長以及出現一些小範圍的重複代碼。
要如何解決這個問題就是寫做本文的意圖了。
本文是基於JsonSerializer
和JsonDeserializer
接口來解決問題的,固然網上有針對TypeAdapter
的工廠類TypeAdapterFactory
來解決該問題,可是該解決方案有個不足之處,那就是須要修改Gson
的源碼,這裏就不講該方案了,有須要的同窗自行搜索。
第一步:一個接口,該接口定義了Gson
在(反)序列化枚舉時將調用的方法:json
public interface GsonEnum<E> { String serialize(); E deserialize(String jsonEnum); }
接口中定義接收一個泛型E
,該泛型用來表示枚舉類型,裏面有兩個抽象方法,String serialize()
方法表示將枚舉序列化成字符串,E deserialize(String jsonEnum)
表示將字符串反序列化成枚舉並返回特定的枚舉E
。ide
第二步:定義枚舉類並實現第一步中聲明的接口,這裏爲了演示效果,定義了以下兩個枚舉:
派別枚舉Faction
:測試
public enum Faction implements GsonEnum<Faction> { ABNEGATION("無私派"), AMITY("和平派"), DAUNTLESS("無畏派"), CANDOR("誠實派"), ERUDITE("博學派"); private final String name; Faction(String name) { this.name = name; } public String getName() { return name; } public static Faction parse(String faction) { switch (faction) { case "無私派": return Faction.ABNEGATION; case "和平派": return Faction.AMITY; case "無畏派": return Faction.DAUNTLESS; case "誠實派": return Faction.CANDOR; case "博學派": return Faction.ERUDITE; default: throw new IllegalArgumentException("There is not enum names with [" + faction + "] of type Faction exists! "); } } @Override public Faction deserialize(String jsonEnum) { return Faction.parse(jsonEnum); } @Override public String serialize() { return this.getName(); } }
性別枚舉Gender
:ui
public enum Gender implements GsonEnum<Gender> { MALE("男"), FEMALE("女"); private final String type; Gender(String type) { this.type = type; } public String getType() { return type; } public static Gender parse(String type) { switch (type) { case "男": return Gender.MALE; case "女": return Gender.FEMALE; default: throw new IllegalArgumentException("There is not enum names with [" + type + "] of type Gender exists! "); } } @Override public Gender deserialize(String jsonEnum) { return Gender.parse(jsonEnum); } @Override public String serialize() { return this.getType(); } }
對於這兩個枚舉內部對GsonEnum
接口的實現方法比較簡單,因此這裏就不解釋了。this
第三步:自定義Gson
(反)序列化過程的GsonEnumTypeAdapter
,先看具體代碼實現:google
public class GsonEnumTypeAdapter<E> implements JsonSerializer<E>, JsonDeserializer<E> { private final GsonEnum<E> gsonEnum; public GsonEnumTypeAdapter(GsonEnum<E> gsonEnum) { this.gsonEnum = gsonEnum; } @Override public JsonElement serialize(E src, Type typeOfSrc, JsonSerializationContext context) { if (null != src && src instanceof GsonEnum) { return new JsonPrimitive(((GsonEnum) src).serialize()); } return null; } @Override public E deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (null != json) { return gsonEnum.deserialize(json.getAsString()); } return null; } }
代碼看起來很簡單,惟一的構造器要求傳入一個GsonEnum
的實現類。
首先看一個測試用例:
(反)序列化測試時用到的類Person
:.net
public class Person implements Serializable { private String name; private Gender gender; private Faction faction; private LocalDate birthday; public Person() { } public Person(String name, Gender gender, Faction faction, LocalDate birthday) { this.name = name; this.gender = gender; this.faction = faction; this.birthday = birthday; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", gender=" + gender + ", faction=" + faction + ", birthday=" + birthday + '}'; } // 省略 getter 和 setter }
JUnit
測試實例
@Test public void testGson() throws Exception { Gson gson = new GsonBuilder() .serializeNulls() .registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter()) .registerTypeAdapter(Gender.class, new GsonEnumTypeAdapter<>(Gender.FEMALE)) .registerTypeAdapter(Faction.class, new GsonEnumTypeAdapter<>(Faction.ABNEGATION)) .create(); Person p1 = new Person("雷卡", Gender.MALE, Faction.DAUNTLESS, LocalDate.of(1994, 10, 11)); System.out.println("調用 toString 方法:\n" + p1); String jsonText = gson.toJson(p1); System.out.println("將 person 轉換成 json 字符串:\n" + jsonText); System.out.println("-------------------"); Person p2 = gson.fromJson(jsonText, Person.class); assert p2 != p1; System.out.println("根據 json 字符串生成 person 實例:\n" + p2); }
測試結果爲:
測試結果代表咱們想要的效果已經達到了,如今講一下GsonEnumTypeAdapter
的實現原理。
以Faction
爲例,首先它接收E
時咱們傳遞的是Faction
枚舉的一個枚舉值Faction.ABNEGATION
,這個枚舉值沒有任何限定,能夠是該枚舉類中的任意一個。
序列化Faction
類型時,Gson
調用其中的serialize
方法,注意此時咱們傳入的Faction.ABNEGATION
並不發揮做用,咱們直接調用的是序列化時遇到的Faction
實例(好比Faction.AMITY
,Faction.CANDOR
)的serialize
實例方法,那麼此時返回的必然是該枚舉實例的name
屬性值;
反序列化遇到Faction
類型時,Gson
調用其中的deserialize
方法,此時咱們實際上操做的最開始傳入的枚舉類型Faction.ABNEGATION
的deserialize
方法,注意此時該方法須要傳入一個字符串,恰好能夠用到JsonElement
自身提供的getAsString
方法。再看Faction
類中對deserialize
方法的實現,實際上該方法並不關注調用者是誰,它真正關心的傳入值是什麼,根據傳入的值,它能夠很快獲取獲得對應的枚舉或者拋出異常。
可能上面表述得不是很清楚,但實際上都是一些基礎的知識,即泛型,接口,枚舉的特殊性的綜合應用,多看幾遍應該就懂了,另外最好打上幾個斷點,而後debug
一遍,看整個流程是怎麼跑的,那樣基本就清晰了。
另外,爲確保GsonEnumTypeAdapter
只接收枚舉而且GsonEnum
只被枚舉類所實現,能夠添加泛型參數的邊界,以下:
GsonEnum
的類聲明
public interface GsonEnum<E extends Enum<E>>
GsonEnumTypeAdapter
的類聲明
public class GsonEnumTypeAdapter<E extends Enum<E>> implements JsonSerializer<E>, JsonDeserializer<E>
聲明瞭泛型上限爲Enum
以後能夠確保在使用該泛型時只有枚舉類可以被處理。
原創聲明:
本文爲我的原創,若有錯誤與不妥,歡迎提出!
另外,未經許可,謝絕轉載。
編寫日期:2016-08-23 |
發佈日期:2016-08-23 |