Day 10:Gson 资料解析

本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 UdemyYoutube 频道。另外,想追踪更多相关技术资讯,欢迎到 脸书粉丝专页 按赞追踪喔~

程序码范例

范例名称:Gson 资料解析并印出药局名称
开发人员:HKT (侯光灿)
程序语言:Kotlin
开发环境:Android Studio 4.1.1 & Android 11 & Kotlin 1.4.21
授权范围:使用时必须注明出处且不得为商业目的之使用
范例下载点:点我下载

JSON 资料在解析时,较为耗手机效能、处理也较慢,所以在解析资料时,业界普遍会透过 Gson 来解析 JSON 资料格式。Gson 是 Google 开发的开源 Library。这一节课程,我们将来介绍,如何使用 Gson 来解析资料。

添加 Gson 依赖库 (dependencies)

在 GRADLE (Module) 层级 dependencies 内加入:

implementation 'com.google.code.gson:gson:2.8.6'

资料转换

JSON 资料转换成 Gson 是透过 Gson 提供的 fromJson(String json, Class classOfT) 这个方法。其中,fromJson 第一个栏位,要带入的即是我们透过 Okhttp 获取到的资料,第二个栏位,需带入我们自己定义的类别。

//pharmaciesData 为 Okhttp 获取到的资料
val pharmaciesData = response.body?.string()

//PharmacyInfo 是我们自定义类别
val pharmacyInfo = Gson().fromJson(pharmaciesData, PharmacyInfo::class.java)

PharmacyInfo

JSON 资料转换成 Gson,仍须注意的是资料层次概念以 口罩资料 为例,若我们要取得最外层资料,如:「 “type”: “FeatureCollection”」,我们可以写成这样:

class PharmacyInfo(
    @SerializedName("type")
    val type: String
)

SerializedName 里面要填的是,真实资料中的名称,我们要获取 type 所以填 type,所以写成「@SerializedName("type")」以此类推。而如果你不喜欢,原本资料定义的名称,你可以自定义常数值名称,例如,想改成 my_type,就可以写成这样:

class PharmacyInfo(
    @SerializedName("type")
    val my_type: String
)

而如果我们想要印出资料,可以写成这样:

Log.d("HKT", "my_type: ${pharmacyInfo.my_type}")

输出结果

my_type: FeatureCollection

而在原本 JSON 资料解析里,如果我们没有多加 has 或 isNull 来判断栏位资料是否存在,会直接喷出例外错误。但是如果是 GSON,并不用多加判断,只是因为没有此笔栏位资料会回传 null。例如,我们资料并没有 typeeeeee 但我们写成这样:

class PharmacyInfo(
    @SerializedName("typeeeeee")
    val my_type: String
)

输出结果

my_type: null

阵列与物件属性的资料转换

如果我们要获取每间药局名称,刚开始学的的同学最容易犯的错误,会写成这样:

//错误范例
class PharmacyInfo(
    @SerializedName("name")
    val name: String
)

这样的结果,GSON 并不会喷出例外错误,只是会得到 null 。这个问题出在资料层次问题,必须清楚的跟电脑说你要获取哪一层资料,上方的写法,电脑会误以为你要获取取第一层,但实际你是要获取 features 「阵列」里面的 properties 「物件」里面的 name 值。所以应该改成:

class PharmacyInfo(
    @SerializedName("features")
    val features: List<Feature>
)

class Feature(
    @SerializedName("properties")
    val property: Property
)

class Property(
    @SerializedName("name")
    val name: String
)

层次中最需要注意的是,大括号 object {} 与中括号 array [],层次概念。了解之後,我们就可以透过 for 回圈,取出每一间药局名称。

val pharmaciesData = response.body?.string()
val pharmacyInfo = Gson().fromJson(pharmaciesData, PharmacyInfo::class.java)

for (i in pharmacyInfo.features) {
    Log.d("HKT", "name: ${i.property.name}")
}

输出结果

name: 中美药局
name: 新东洋药局
name: 辰好药局
name: 杏安药局
name: 明皇药局
name: 全国大药局
name: 政德药局
name: 嘉方药局
name: 庆丰综合药局
...
...
...

KT 的偷呷步

安装外挂套件:「JSON To Kotlin Class」,快速将 JSON 资料转换成 Class。

完整口罩资料类别定义

透过外挂套件的方式,快速帮我们产生对应的资料类别结构,并将原本分散的档案,刻意全部集中到「PharmacyInfo.kt」档案中,若采用外挂套件产生个别分开档案也可,看公司文化或个人喜好。

package com.thishkt.pharmacydemo.data

data class PharmacyInfo(
    val features: List<Feature>,
    val type: String
)

data class Feature(
    val geometry: Geometry,
    val properties: Properties,
    val type: String
)

data class Geometry(
    val coordinates: List<Double>,
    val type: String
)

data class Properties(
    val address: String,
    val available: String,
    val county: String,
    val cunli: String,
    val custom_note: String,
    val id: String,
    val mask_adult: Int,
    val mask_child: Int,
    val name: String,
    val note: String,
    val phone: String,
    val service_periods: String,
    val town: String,
    val updated: String,
    val website: String
)

参考资料

HKT 线上教室
https://tw-hkt.blogspot.com/

Freepik
https://www.freepik.com/

Gson User Guide
https://github.com/google/gson


那今天【iThome 铁人赛】就介绍到这边罗~

顺带一提,KT 线上教室,脸书粉丝团,会不定期发布相关资讯,不想错过最新资讯,不要忘记来按赞,追踪喔!也欢迎大家将这篇文章分享给更多人喔。

我们明天再见罗!!!掰掰~


<<:  D-20. 预设更改DBMS 、bundle指令 、Gemfile && Reverse String II && III

>>:  day 2 - 先看清楚目标的样子再动手

Day 5 hook的前奏 useState

这篇是在讲React的测试,所以就拿个几天篇幅来讲hooks,React在v16.8之後开始支援ho...

[DAY19] 跟 Vue.js 认识的30天 - Vue 自定义指令(`directive`)

在粗浅的看过这一章时,觉得 Vue 真的有好多功能啊,目前的我似乎还是没办法很熟用 Vue 的每项语...

【D5】期货资讯:期交所&Open data

前言 《世说新语》提到:「覆巢之下,复有完卵乎?」放在交易市场也是如此。当整体趋势向下,再好的公司也...

资安学习路上-picoCTF 解题(Reverse)1

Reverse 1.Transformation:考unicode 载下来的档案,看起来编码错误 用...

[Tableau Public] day 9:分析前先处理一下原始资料吧

第九天,打完疫苗已经22小时,目前只有接种处抬手会酸痛,目前还没感受到其他副作用。不过头已经开始有点...