iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 3
0
自我挑戰組

一天一蘋果,Bug 遠離我。系列 第 3

Day 03: 試著解析 JSON 的檔案吧!

使用 Decodable 方式來解析我們的 JSON 檔案。
https://ithelp.ithome.com.tw/upload/images/20181018/20107701PYiBJ7euPA.png
天瓏資訊圖書在好想工作室書架上的書籍清單 API

前言:

應該會有人好奇想說 -

這麼快就串接資料了? App 的 UI 和介面呢?/images/emoticon/emoticon09.gif

因為前面也有說過了,我們這個 APP 會以 「 展示工作室現有書籍 」 為核心功能出發,其他功能則會由開發這個 APP 中慢慢發想,我們需要了解 「 資料結構是如何? 」、「 有什麼資訊可以拿來利用?」,所以我們才需要優先串接資料來啟動整個專案的運作 ?
在這次教學我們會使用 Swift中的 Decodable Protocol 來解析從 API 中獲取的 JSON 格式資料,並將其轉換成我們所定義的結構,讓我們開始吧。


#定義 Decodable 結構

首先我們看一下我們 API 的資料:

Bookshelf

https://ithelp.ithome.com.tw/upload/images/20181018/20107701haTBtqS2i8.png

「 … 我看了三小?亂碼?黑人問號?」

第一次看到 JSON 檔的時候,以為是一長串的亂碼資訊,我要怎麼分析它?
別怕。首先,我們可以將它複製到線上 JSON Editor 網站,或是額外安裝插件來讓整個的 JSON 的資料看起來可讀性高一點。

https://ithelp.ithome.com.tw/upload/images/20181018/201077017G0t1zFi2P.png

經由 JSON Editor 整理過後的 JSON 資料

使用 JSON Editor 整理過後的資料結構,就非常易讀了。那為什麼我們需要先了解它的結構呢?因為我們使用 Decodable 方式來解析 JSON 檔的時候,我們在定義其結構的時候就要等同於( 或是少於 )JSON 上的資料結構。

「 那定義的結構長怎麼樣子 ??」
『 … 恩 ?,簡單來說就跟 JSON 上一樣啦 ~ ? 』

定義的結構的型別( Type)也必須與 JSON 上的類型一樣,若是其中有不同的地方( 無論是 key 或型別對不上 )則無法解析成功。

因為我們這個是「 書架 」的結構,所以這邊來創建一個叫做 Bookshelf 的 struct,並且讓他遵循 Decodable,根據上面的 JSON 來定義它的結構:

https://ithelp.ithome.com.tw/upload/images/20181018/20107701fmQYsUdFGN.png

這邊容許我註解掉 list,賣個關子 ?。

這時候你應該會好奇那註解掉的 list 這個 Array 的型別要怎麼跟 JSON 上一樣呢?其實就如同我們定義 Bookshelf 的方式一樣,我們需要額外創建一個 struct 作為它的型別。

https://ithelp.ithome.com.tw/upload/images/20181018/20107701X1pQeEHZRP.png

左圖為原始 JSON 樣式,右圖為 JSON Editor 編輯過。

這邊我們 list 裡面放的是每本「 書籍 」的資料,所以我們來創建一個叫做 Book 的 struct 並且根據 list 裡面的結構來定義它,別忘了這個結構也需要遵循 Decodable 這個協議:

這邊要注意,JSON Editor 上面的格式是轉換過的格式,我們定義時要以原本 API 的輸出 JSON 的格式為主,所以這邊 Book 的屬性都為都會定義成字串。

https://ithelp.ithome.com.tw/upload/images/20181018/20107701zoPthRkQO8.png

#額外補充:

假設你不確定某個屬性是否存在或是該屬性可能會傳遞一個 nil or null,你可以在型別後面加上 "?" ,使其變成 optional type。
如果你不想要其中一項的屬性值,那麼不輸入該屬性即可,在解析時會自動忽略它。

定義好 Book 結構之後,別忘了把上面的 list 裡面的型別改為 [ Book ] 呦!

https://ithelp.ithome.com.tw/upload/images/20181018/20107701Xy6Xhd5TCv.png

你應該會想說,為什麼我最後都改成 String? 原因是:
因為我們沒辦法( 麻煩 )去確定 list 裡面每本書的值是否都存在。
為了防止它哪天傳了一個 nil or null 給我。

所以這邊的資料型態全部才都設為 Optional Type,有備無患。
當然假如接到一個空的 JSON 就爆炸啦 ?~


#從 API 中獲取資料

接著定義好了我們就需要編寫一個方法來抓 API 上的資料了,這邊我們會使用 URLSession 的 datatask 來獲取網路上的資料:

func getBookshelfData() {
    Bookshelf.getBookshelfFromString(from: bookshelfUrl) { (data) in
        do {
            // 創建一個 JSONDecoder 實例來解析我們的 data
            //若解析成功則會將其轉換成我們所定義的 Decodable 的結構。
            let decoder = JSONDecoder()
            let json = try decoder.decode(Bookshelf.self, from: data)
            print(json)
            // TODO: 獲取 Bookshelf 資料後的操作
        } catch {
            // 若格式錯誤則會印出下列錯誤訊息。
            /* The data couldn’t be read because it isn’t in the correct format. */
            print(error.localizedDescription)
        }
    }
}

接著我們到 ViewController 上來定義我們的獲取資料後的方法,並且試著印出我們的解析後的 json 查看是否正確:

func getBookshelfFromString(from urlString: String, completeion: @escaping (Data) -> Void) {
    // 判斷 urlString 是否能被轉成 Url,若無法則 return,不繼續後面的操作。
    guard let url = URL(string: urlString) else { return }
    
    // 使用 URLSession.shared.data(with: url) 來獲取網址中的數據。
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        // 如果 error 存在,印出錯誤訊息後,不接續後面操作。
        if error != nil {
            print(error!.localizedDescription)
            return
        }
        // 檢查 data 是否存在,若是存在透過 compeletion 這個逃逸閉包(escaping clourse)將 data 傳遞出去。若是不存在則 return。                          
        guard let data = data else { return }
        completeion(data)
                                           
        }.resume() // 別忘了加上 resume() 才會開始運作。
}

恩… 我想應該很完美的收到了吧~???

滿滿的書籍資料
books


後記:

那我們這次的教學就到這邊結束了,希望大家對於使用 Decodable 方式來解析 JSON 有一定的幫助,當然 Codable 還有許多特殊的用法,但是這種最基本的處理方式一定對能夠讓你輕鬆的就能將資料解析成你所需要的結構,各位就是著接個 API 來玩看看吧!


上一篇
Day 02: 來做一些基本的設置吧!
下一篇
Day 04: 使用第三方程式庫來幫幫忙!
系列文
一天一蘋果,Bug 遠離我。30

尚未有邦友留言

立即登入留言