何謂JSON
JSON,全名是JavaScript Object Notation,是一種輕量級的資料交換格式,而從名稱可以得知他是從JavaScript中衍生出來的,而其中包含的格式只有2種-物件(Object)和陣列(Array),下面講一下這兩者的差異。
物件(Object)
在JavaScript中,物件的表示法是用大括號{}將資料包起來,而其中的資料則是鍵-值對(key-value pair),每個鍵一定都是字串,而值可以是數值、字串、布林或者是null,鍵-值對之間用逗號","做區隔,範例如下
{
"name" : "John",
"age" : 18,
"handsome" : true,
"rich" : false,
"girlFriend" : null
}
在上面的範例中建立了一個JSON Object,名字叫John,年齡18歲,很帥但不富有,沒有女朋友(純屬範例,叫John的不要生氣😅);而JSON Object的特性是,其中的各個key-value pair是不需要按照順序給的,所以就算先定義年齡再定義名字,還是可以視作為同樣的JSON Object;而因為資料都是一個key對到一個value,因此可以透過呼叫這個物件裡面的Key(比如"name")得到在這個物件中對應的值("John")。
陣列(Array)
JavaScript中陣列的表示法是用中括號[]包起來,裡面的資料只有值,可以用的類型跟Object一樣,有數值、字串、布林或者null,每個值之間同樣用逗號做區隔,舉例如下
[
1,
"demo",
true,
null
]
在這個範例中建立了一個JSON Array,與JSON Object不同,JSON Array值的順序是不能變動的,因為每個值有對應到一個索引(index),所以如果呼叫這個陣列中的第0項,是可以得到1這個值的。
而物件和陣列是可以包含彼此的,比如
[
{
"name":"Ram",
"email":"Ram12@testmail.com",
"sports":[
"basketball",
"soccer"
]
},
{
"name":"Bob",
"email":"bob32@testmail.com",
"sports":[
"baseball",
"swimming"
]
}
]
上面的範例中,在一個JSON Array中包含了兩個JSON Object,而每個Object有name、email、sports這3個鍵,其中sports的值是另一個JSON Array。
Android中的JSON轉換
開頭提到,在Android中有內建支援JSON的轉換,因此這邊先講一下怎麼透過內建的API來轉換JSON。
假設有一個data class用來儲存學生的數學和英文成績
如果有一個學生Tom,數學85分,英文90分,當要透過JSON格式將Tom的成績上傳時,可以透過org.json這個package來將data class轉換成JSON字串來上傳
這時如果把jsonStr打印出來,可以得到這樣一個字串
"{\"name\":\"Tom\",\"math\":85,\"english\":90}"
這時就可以將這筆資料上傳了;反過來說,如果透過網路得到了Mary這名學生資料的字串,要將他轉換成Student data class則是這樣做
先將JSON字串轉換成JSONObject,再透過每個key從JSONObject中撈資料出來,建立新的Student實體,最後可以得到一個Student實體
Student(name=Mary, math=95, english=76)
看起來這樣就完成了不是嗎?注意在上面的例子中,因為Student只有3樣資料,所以在得到JSON資料並轉換成Student時沒甚麼大問題,但試想如果今天data class裡面包了10幾樣數據呢?這時候透過key取得value的這個步驟就需要在解析JSON字串時重複10幾次,不但很耗時間、造成code不簡潔、甚至如果取得目標數值的類別錯誤時又需要去看到底是哪個值取錯,選擇正確的方法;為了解決這些問題,Gson就是一個不錯的工具。
使用Gson來做JSON轉換
首先將Gson Library加入APP層級的Dependencies
implementation 'com.google.code.gson:gson:2.8.6'
當要用到JSON轉換的時候,只要建立一個新的Gson實體,就可以透過這個實體來幫忙轉換
回到先前的例子,假設要在Student這個data class跟JSON格式之間做轉換,那上面這段程式碼就會變成
跟上面使用內建的json package相比,使用Gson是不是相對簡單很多呢。
@SerializedName
有時候從網路上得到的JSON資料和我們自訂的data class欄位名稱不一樣,如果直接轉換的話會出錯,這時候就可以透過SerializedName這個註解來幫忙,在Gson文件中對於這個註解是這樣解釋的
An annotation that indicates this member should be serialized to JSON with the provided name value as its field name.
也就是說,當有設置SerializedName註解,Gson在做轉換的時候會將特定參數的key轉換成設置的名稱,直接舉例來看,上面Student data class中有一個參數name,但網路上的資料不是叫name而是叫studentName,那就可以在Student data class中加上註解
當要把Student這個data class轉換成JSONObject時,本來叫做name的參數會轉換成studentName這個欄位,反過來說,當得到的JSONObject中有studentName這個欄位時,在轉換中會對應到Student中的name參數;而其他沒有特別註解的則是本來叫甚麼,轉換後還是叫甚麼。
結論
透過Gson,可以快速的幫助我們處理網路資料和本地data class的轉換,但Gson對於Kotlin其實沒有做到完全支援(Kotlin不是Google親兒子嗎🤔),比如在Kotlin最被常拿來做為賣點之一的空安全檢驗,Gson就沒有支援,也因此有其他的Library也被開發出來作為更適合Kotlin使用的替代方案,比如身為Gson開發者之一的Jesse Wilson開發出的Moshi,作者更在Reddit論壇上講了使用Kotlin開發時,為什麼要用Moshi取代Gson的原因(有興趣的朋友可以到這裡看看),因此隨著Kotlin越來越被廣泛使用,或許這篇文章也會變成時代的眼淚吧😅
參考資料
Google Developer Documentation -JSON
《Android》『JSON & GSON』- JSON 的基本程式語法教學
Gson User Guide(Github)