利用 Swift 4 的 JSONDecoder 和 Codable 解析 JSON 和生成自訂型別資料

抓取網路上的 JSON 資料並不是太困難的事,但是如果想要解析它,甚至是把它變成方便 App 使用的自訂型別,卻需要寫許多程式碼才能實現。因此從前一些解析 JSON 和將 JSON 變成自訂型別的第三方套件大受歡迎,不過在 Swift 4,有了 JSONDecoder 和 Codable,這些第三方套件可以通通丟掉了 ! (ps: JSONDecoder 要 iOS 7 以上才支援)

接下來,我們就以從 iTunes 搜尋彼得潘最近常聽的歌,薛之謙的歌曲當例子,API 的網址為

https://itunes.apple.com/search?term=薛之謙&media=music

後台回傳的資料截圖如下:

1 定義自訂型別 SongResults。

struct SongResults: Codable {
    struct Song: Codable {
        var artistName: String
        var trackName: String
        var collectionName: String?
        var previewUrl: URL
        var artworkUrl100: URL
        var trackPrice: Double?
        var releaseDate: Date
        var isStreamable: Bool?
    }
    var resultCount: Int
    var results: [Song]
}

(1) 為了到時候能從 JSON 轉換成 SongResults,要讓它遵從 protocol Codable。

(2) SongResults 裡的 property 名稱要和後台回傳的 JSON 資料對應。原本 JSON 的第一層是 resultCount & count,所以我們宣告這兩個 property,而 results 又是一個 Array,所以我們再定義 struct Song ,它的 property 將是當初 array results 裡 dictionary 的 key。

(3) 自訂型別裡的 property 可以少訂,不能多訂。你有寫到的欄位,一定要可以對應到 JSON 欄位,否則將轉換失敗。

(4) property 型別錯誤將轉換失敗,比方 JSON 裡 trackPrice 的型別是 Double,但你卻將 property 的型別宣告為 Int。

(5) JSON 裡有些欄位不會每一筆資料都有,比方不是每一首歌都可以買,所以不一定有 trackPrice,因此我們將 trackPrice 設為 optional。如果沒有設為 optional,到時候沒有 trackPrice 的資料將無法轉換。

(6) property 的型別除了常見的 Array, Int, String, Float, Double, Bool,也可以用 URL 和 Date,到時候網址直接變成 URL,時間直接變成 Date,更方便 App 的開發。

2 抓取 JSON 資料,將它變成自訂型別 SongResults。

if let urlStr = "https://itunes.apple.com/search?term=薛之謙&media=music".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: urlStr) {
    let task = URLSession.shared.dataTask(with: url) { (data, response , error) in
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .iso8601
        if let data = data, let songResults = try?  
decoder.decode(SongResults.self, from: data)
        {
            for song in songResults.results {
                print(song)
            }
        } else {
            print("error")
        }
    }
    task.resume()
}

利用 JSONDecoder 的 function decode 將 Data 型別的 JSON 資料變成 SongResults 型別資料。

其中 dateDecodingStrategy 特別設為 .iso8601,因為時間有以下多種不同格式 :

public enum DateDecodingStrategy {
/// Defer to `Date` for decoding. This is the default strategy.
case deferredToDate
/// Decode the `Date` as a UNIX timestamp from a JSON number.
case secondsSince1970
/// Decode the `Date` as UNIX millisecond timestamp from a JSON number.
case millisecondsSince1970
/// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
case iso8601
/// Decode the `Date` as a string parsed by the given formatter.
case formatted(DateFormatter)
/// Decode the `Date` as a custom value decoded by the given closure.
case custom((Decoder) throws -> Date)
}

如果將生成的 songResults.results 顯示到表格上,即可顯示以下的畫面。

關於 JSONDecoder 和 Codable 的相關說明,有興趣的朋友,可進一步參考以下連結