您好,登錄后才能下訂單哦!
本篇內容介紹了“Gson的介紹和使用方法”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在正式介紹 Gson 之前,我們可以先從官方的wiki看下 Gson 的描述,了解它是什么?
Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object。
從描述可以看出,Gson 是用于將 Java 對象與 JSON格式字符串數據相互轉換的 Java 庫。它起初在Google 內部廣泛使用在 Android 平臺 和 Java 服務端上。2008 年開源之后,成為了谷歌又一個被廣泛使用的開源框架,截止目前*(2019.09.08)* 在GitHub 上已有1W6 多星,相同作用的類庫還有 Spring Framework 中集成的 Jackson,以及阿里開源的 fastjson等。
在特性方面,Gson 提供簡易的API fromJson/toJson
來實現 Java 與 JSON 之間的轉換,并且能生成緊湊,可讀的 JSON 字符串輸出,還支持復雜對象轉換和豐富的自定義表示,足以滿足在日常開發中我們絕大部分的 JSON 數據處理需求。
我們通常將對象與JSON字符串間的轉換稱之為序列化和反序列化(Serialization/Deserialization)。將 對象轉化成 JSON字符串的過程稱為序列化,將JSON 字符串轉化成對象的過程稱為反序列化。
使用 Gson 框架進行序列化與反序列操作,都離不開 com.google.gson.Gson
對象,它也是 Gson 框架的關鍵對象,提供的公共 API 具備了多種序列化和反序列方式。
Gson 對象的創建主要有兩種方式:
使用 new 關鍵字直接創建:Gson gson = new Gson()
由 GsonBuilder 對象構建:Gson gson = new GsonBuilder().create()
通常情況下,上面兩種方式創建的 Gson 對象在進行序列化與反序列操作時行為都是一樣的,但是第二種方式構建 Gson 對象時,允許進行額外的行為定制,比如格式化 JSON 字符串的輸出內容,是否序列化 null 值等等。
我們可以通過下面的例子來看下通過上述兩種方式序列化 Java 對象的不同效果:
public class ResultTest { @Test void test_serialization() { Gson gson = new Gson(); Result result = new Result(200, "成功", null); String json = gson.toJson(result); System.out.println("json is " + json); Gson buildedGson = new GsonBuilder().setPrettyPrinting().serializeNulls().create(); String buildedJson = buildedGson.toJson(result); System.out.println("buildedJson is " + buildedJson); } class Result { private int code; private String message; private Object data; public Result(int code, String message, Object data) { this.code = code; this.message = message; this.data = data; } } }
運行該測試用例,在控制臺可以看到如下日志輸出:
從結果可以看出,默認的 Gson 對象行為序列化對象時會將 null
值的字段忽略,而 com.google.gson.GsonBuilder#serializeNulls
方法將允許 Gson 對象序列化 null
字段;并且正常序列化后的 JSON 字符串是緊湊格式,節省字符串內存,使用 com.google.gson.GsonBuilder#setPrettyPrinting
方法之后最終輸出的 JSON 字符串是更易讀的格式。當然除了這兩個方法,GsonBuilder 還提供了許多定制序列化和反序列化行為的API,我們將后面的內容進一步講解。
除了上述將自定義類的對象轉換成 JSON 的方式之外,還可以使用 Gson 框架提供的 JsonObject 構建普通對象,然后使用 toJson
方法生成 JSON 字符串,在原測試類中補充下方測試類,并運行查看效果如下
@Test void test_jsonObject_serialization() { Gson gson = new Gson(); JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("code", 400); jsonObject.addProperty("message", "參數錯誤"); String toJson = gson.toJson(jsonObject); String exceptedJson = "{\"code\":400,\"message\":\"參數錯誤\"}"; Assertions.assertEquals(exceptedJson, toJson); //true }
JsonObject 使用 addProperty(property,value)
方法只能用來添加 String,Number,Boolean,Character這四類數據, 因為內部是調用 com.google.gson.JsonObject#add
, 將 value 封裝成了 JsonPrimitive 對象,然后保存到了內部自定義的 LinkedTreeMap
集合變量 members 中;如果需要在 JsonObject 對象上添加其他對象時,就需要直接使用 add(String property, JsonElement value)
方法添加一個 JsonElement 對象。這里的 JsonElement 是一個抽象類,JsonObject 和 JsonPrimitive 都繼承了JsonElement,所以我們最終通過另外的 JsonObject 對象來作為原 JsonObject 上的屬性對象:
Gson gson = new Gson(); JsonObject jsonObject = new JsonObject(); //... JsonObject nestJsonObject = new JsonObject(); nestJsonObject.addProperty("username", "one"); nestJsonObject.addProperty("score", 99); jsonObject.add("data", nestJsonObject); String toJson2 = gson.toJson(jsonObject); System.out.println(toJson2); // {"code":400,"message":"參數錯誤","data":{"username":"one","score":99}}
現在我們再來看下 JSON 反序列化成 Java 對象用法,這里主要使用方法是 com.google.gson.Gson#fromJson
,它最基礎的用法就是 fromJson(String json, Class<T> classOfT)
,嘗試將 JSON 字符串轉為指定 Class 的對象,如果轉換失敗,就會拋出 JsonSyntaxException
異常。我們可以在原來代碼上新增一個測試用例,運行看下效果:
@Test void test_deserialization() { String json = "{\"code\":400,\"message\":\"參數錯誤\"}"; Result result = new Gson().fromJson(json, Result.class); Assertions.assertEquals(400, result.code); // true Assertions.assertEquals("參數錯誤", result.message); // true }
除了將JSON 字符串序列化為自定義的Java 對象之外,我們還可以轉為 Map 集合,Gson 提供了對 Map 集合的轉換,使用起來也十分簡單:
@Test void test_map() { String jsonString = "{'employee.name':'one','employee.salary':10}"; Gson gson = new Gson(); Map map = gson.fromJson(jsonString, Map.class); assertEquals(2, map.size()); assertEquals("one", map.get("employee.name")); assertEquals(Double.class, map.get("employee.name").getClass()); }
需要注意的是轉換后的 Map 對象真實類型并不是我們經常用的 HashMap,而是 Gson 自定義集合LinkedTreeMap
,它實現Map 接口來存儲鍵值對,在新增和刪除上實現上進行了優化,并且將存儲鍵值對的順序作為遍歷順序,也就是先存入的先被遍歷到。除此之外,JSON 字符串里的數值型數據都會轉轉換為 Double 類型,而 true/false
數據被會被轉換成 Boolean 類型,具體判斷依據可以參考 com.google.gson.internal.bind.ObjectTypeAdapter#read
方法的實現。
當我們正對 JSON 數據進行數組轉換時,類似普通對象轉換的方式即可, toJson
方法直接使用轉為 JSON 數據,fromJson
指定數組類型轉換為對應類型的數組。
@Test void test_array() { Gson gson = new Gson(); int[] ints = {1, 2, 3, 4, 5}; String[] strings = {"abc", "def", "ghi"}; String s = gson.toJson(ints);// [1,2,3,4,5] assertEquals("[1,2,3,4,5]", s); // true String s1 = gson.toJson(strings);// ["abc", "def", "ghi"] assertEquals("[\"abc\",\"def\",\"ghi\"]", s1); String[] strings1 = gson.fromJson(s1, String[].class); assertEquals(strings.length, strings1.length); // true assertEquals(strings[0], strings1[0]); // true int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); assertEquals(1, ints2[0]); // true assertEquals(5, ints2[4]); // true }
要將 List 數據轉換為 JSON數據,使用 Gson 的方式與處理 Array 數據一樣;這里主要講的是將JSON 數據轉為 List 對象的操作略有不同,要將一個 JSON 數組數據轉換為一個自定義類的List 時,我們按照原來的寫法如下:
@Test public void givenJsonString_whenIncorrectDeserializing() { Gson gson = new Gson(); String inputString = "[{\"id\":1,\"name\":\"one\"},{\"id\":2,\"name\":\"two\"}]"; List<Person> outputList = gson.fromJson(inputString, List.class); outputList.get(0).getId(); }
但是不幸的是,運行這段代碼后會拋出 ClassCastException
異常,具體描述如下:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.one.learn.Person ...
從上述描述中我們可以知道執行 fromJson
之后,反序列化后得到的 List 元素類型為 LinkedTreeMap,而不是 Person,所以以 Person 對象方式訪問 id 屬性時就會拋出 ClassCastException
異常。那又該如何處理呢, 我們需要調用 Gson 的 另外一個 fromJson
方法:fromJson(String json, Type typeOfT)
,先看下使用方式
@Test public void givenJsonString_whenCorrectDeserializing_() { Gson gson = new Gson(); String inputString = "[{\"id\":1,\"name\":\"one\"},{\"id\":2,\"name\":\"two\"}]"; Type type = new TypeToken<List<Person>>(){}.getType(); List<Person> outputList = gson.fromJson(inputString, type); int id = outputList.get(0).getId(); assertEquals(1, id); // true assertEquals("one", outputList.get(0).getName()); // true }
這個方法中的 Type 對象通過 TypeToken 對象的 getType
方法獲取到,就是 TypeToken 對象所關聯的泛型類型。而這里 TypeToken 是 Gson 為了支持泛型而引入的類,來解決 Java 無法提供泛型類型表示的問題,由于 TypeToken 的構造方法是protected
修飾的,無法直接構造,使用就需要寫成new TypeToken<List<String>>() {}.getType()
形式。
接觸了 Gson 基本的使用之后,我們接著進一步學習 Gson 的其他用法。
上節內容簡單接觸了 Gson 對泛型的支持,接下來用代碼來展示下它的強大之處,首先我們將上文的 Result 類調整下接受泛型參數:
class Result<T> { private int code; private String message; private T data; public Result(int code, String message, T data) { this.code = code; this.message = message; this.data = data; } }
然后對一個有內嵌對象的 JSON字符串進行解析成 Result<User> 對象,示例代碼如下:
@Test void test_genric_object() { String json = "{\"code\":200,\"message\":\"操作成功\",\"data\":{\"username\": \"one\",\"avater\": \"image.jpg\"" + "}}"; Type type = new TypeToken<Result<User>>(){}.getType(); Result<User> result = new Gson().fromJson(json, type); Assertions.assertEquals(200, result.code); Assertions.assertEquals("one", result.data.getUsername()); Assertions.assertEquals("image.jpg", result.data.getAvater()); } class User { private String username; private String avater; public String getUsername() { return username; } public String getAvater() { return avater; } }
利用 TypeToken 對象獲取具體泛型類型 Result<User> , 然后在 fromJson
方法中傳入就會根據對應類型的執行反序列化操作。
如果我們要對Java 對象的某些字段進行特殊處理,比如隱藏某些字段的序列化,對字段的數據格式化處理等,我們可以通過實現 JsonSerializer 接口,對序列化邏輯進行自定義。例如,我們需要對 Date 類型屬性進行特定格式的處理,可以聲明 DateSerializer 類實現如下:
class DateSerializer implements JsonSerializer<Date> { SimpleDateFormat dateTime = new SimpleDateFormat("yyyy-MM-dd"); @Override public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(dateTime.format(src)); } }
然后在構建 Gson 對象前,利用 GsonBuilder 將 DateSerializer 實例進行注冊,使用方式如下:
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateSerializer()).create();
這樣一來,一旦遇到要序列化 Date 類型的字段時,都會通過自定義的 serialize
方法將日期以 yyyy-MM-dd
格式進行輸出,如下方的示例代碼:
@Test void test_dateSerializer() { MyObject myObject = new MyObject(new Date(), "one"); Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateSerializer()).create(); String json = gson.toJson(myObject); String exceptedJson = "{\"date\":\"2019-09-08\",\"name\":\"one\"}"; Assertions.assertEquals(exceptedJson, json); // true } class MyObject { private Date date; private String name; public MyObject(Date date, String name) { this.date = date; this.name = name; } public MyObject() { } }
與自定義序列化實現方式類似,想要自定義反序列化邏輯,就需要同樣要實現一個叫 JsonDeserializer 的接口,進行自定義反序列化邏輯的實現。比如現在有個 JSON 字符串內容為 {"CODE": 400, "MESSAGE": "參數錯誤"}
,需要被反序列化為前文提到的 Result 對象,由于字段名不一樣,為了實現對應的轉換,就需要自定義 ResultDeserializer 類,具體實現如下:
class ResultDeserializer implements JsonDeserializer<Result> { @Override public Result deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject object = json.getAsJsonObject(); Result<Object> result = new Result<>(object.getAsJsonPrimitive("CODE").getAsInt(),object.getAsJsonPrimitive("MESSAGE").getAsString(), null); return result; } }
接下來就是利用 GsonBuilder 注冊 ResultDeserializer 實例,生成對應的 Gson 對象,并進行反序列化操作:
@Test void test_resultDeserializer() { //language=JSON String json = "{\"CODE\": 400,\"MESSAGE\": \"參數錯誤\"}"; Gson gson = new GsonBuilder().registerTypeAdapter(Result.class, new ResultDeserializer()) .create(); Result result = gson.fromJson(json, Result.class); Assertions.assertEquals(400, result.code); // true Assertions.assertEquals("參數錯誤", result.message); // true }
Gson 除了提供一些 API 供開發者使用之外,還有一些具有特性的注解可以使用,接下來就介紹在 Gson 中最常用的注解。
這個注解只能用在字段上,作用就是注明對應的字段是否將在序列化或者反序列化時暴露出來,有兩個屬性 serialize
和 deserialize
,默認都為 true。當給一個字段加上 注解@Expose(serialize = true, deserialize = false)
,則表示了該字段盡在序列化時可見,在反序列化時會忽略賦值。需要額外注意的一點是,@Expose 注解只有在用 GsonBuilder 方式構建 Gson 時有限,并且構建前必須調用 excludeFieldsWithoutExposeAnnotation
方法,下面是具體的使用示例:
@Test void test_expose() { MySubClass subclass = new MySubClass(42L, "the answer", "Verbose field not to serialize"); MyClass source = new MyClass(1L, "foo", "bar", subclass); Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); String s = gson.toJson(source); System.out.println(s); // {"id":1,"name":"foo","subclass":{"id":42,"description":"the answer","otherVerboseInfo":"Verbose field not to serialize"}} } @Data @AllArgsConstructor class MyClass { private long id; @Expose(serialize = false, deserialize = true) private String name; private transient String other; @Expose private MySubClass subclass; } @Data @AllArgsConstructor class MySubClass { @Expose private long id; @Expose private String description; @Expose private String otherVerboseInfo; }
在 Gson 中
transient
關鍵字修飾的字段默認不會被序列化和反序列化,這個行為是與 Java 原生的序列化和反序列化操作一致的。
該注解用于標記對應字段或者類型的版本,讓 Gson 可以指定版本號進行序列化和反序列化操作。當Web服務上的 JSON 數據對應的類存在多個版本的字段時,這個注解就十分有用。
同樣地,該注解只針對使用 GsonBuilder
方式構建的 Gson 對象,并且使用 setVersion
方法指明版本號時有效,只解析對象中對應版本的字段,下面為具體示例:
public class VersioningSupportTest { @Test void test() { VersionedClass versionedObject = new VersionedClass(); Gson gson = new GsonBuilder().setVersion(1.0).create(); String jsonOutput = gson.toJson(versionedObject); System.out.println(jsonOutput); // {"newField":"new","field":"old"} } } class VersionedClass { @Since(1.1) private final String newerField; @Since(1.0) private final String newField; private final String field; public VersionedClass() { this.newerField = "newer"; this.newField = "new"; this.field = "old"; } }
這個注解使用起來比較簡單,也很有用。@SerializedName 指定了成員字段被序列化和反序列化時所采用的名稱下面是具體使用方式:
public class JSONFieldNamingSupportTest { private class SomeObject { @SerializedName("custom_naming") private final String someField; private final String someOtherField; public SomeObject(String a, String b) { this.someField = a; this.someOtherField = b; } } @Test void test() { SomeObject someObject = new SomeObject("first", "second"); String jsonRepresentation = gson.toJson(someObject); System.out.println(jsonRepresentation); // {"custom_naming":"first","someOtherField":"second"} SomeObject someObject1 = gson.fromJson(jsonRepresentation, SomeObject.class); System.out.println(someObject1); // SomeObject{someField='first', someOtherField='second'} } }
不同于上面的注解,@JsonAdapter
只作用于類上,主要作用就是代替 GsonBuilder.registerTypeAdapter
方法的執行,直接通過 @JsonAdapter(aClass.class)
方式指定 JsonDeserializer 對象或者 JsonSerializer 對象,可以起到相同的想過,并且優先級比GsonBuilder.registerTypeAdapter
的優先級更高,由于只是將 registerTypeAdapter
方法執行簡化成了注解方法,這里就不再演示,直接在前文自定義反序列化一節的 Result 類上使用就可以看到效果。
本文主要學習總結了 Gson 框架的序列化和反序列操作使用方式,以及介紹了 Gson 多種特性用法,希望對處理 JSON 數據感到頭疼的小伙伴有所幫助。
“Gson的介紹和使用方法”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。