2020年6月29日 星期一

[iOS] JSON

JSON

JavaScript Object Notation,是輕量級的資料交換語言,與Swift的字典相當類似,使用key對應value,並且可以儲存String、Int、Double以及Array,透過JSON可以很方便的從別的平台取得資料,接著轉換成Swift程式語言可使用的格式。

{
  "a" : "a",
  "b" : 0,
  "c" : 0.5,
  "d" : [1,2,3,4,5],
  "e" : {
    "e1": "e"
  }
}

以上是一個簡單的JSON結構,大括號【 {} 】代表物件,中括號【 [] 】代表陣列,一樣是使用

雙引號代表字串,冒號的左方為key,右方為value。

所以我們使用以上的JSON,拿字串a代表key的話,會取出字串a這個value,依此類推。

通常整個JSON是一個字串,我們可以透過轉換將JSON字串轉換成JSON,舉例來說,我們先使用字串構建一個JSON。

        let jsonString = """
        {
          "a": "a",
          "b": 1,
          "c": 1.1
        }
        """

我們可以到網站上驗證我們的JSON字串是否合法


確認是一個合法的JSON字串後,我們試著將它轉換成Swift可使用的資料格式,

前面有提到JSON其實與字典十分類似,因此你可以將JSON字串轉換成字典。 

        //將JSON字串轉換成Data
        if let jsonData = jsonString.data(using: .utf8) {
            do {
                //透過JSON序列化轉換成自字典物件
                if let dictionary = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
                    print(dictionary["a"]!) //a
                    print(dictionary["b"]!) //1
                    print(dictionary["c"]!) //1.1
                }
            } catch {
                print(error.localizedDescription)
            }
        }



但是透過字典來取JSON其實還是有些麻煩,更好的作法應該是將JSON轉換成物件,不管是Class或Struct的形式均可,因此這邊說明如何將JSON轉換成物件。

首先我們必須要建立一個相同的物件格式,以上面的例子來說

struct MyJSONModel {
    var a: String
    var b: Int
    var c: Double
}

接著我們必須遵循Codable協議

struct MyJSONModel: Codable {
    var a: String
    var b: Int
    var c: Double
}

如此一來你就可以將JSON轉換成這個結構的實體

透過JSONDecoder來將JSON Data轉換成結構的實體

//將JSON字串轉換成Data
if let jsonData = jsonString.data(using: .utf8) {
    do {
        let jsonDecoder = JSONDecoder()
        let myJSONModel = try jsonDecoder.decode(MyJSONModel.self, from: jsonData)
        print(myJSONModel.a) //a
        print(myJSONModel.b) //1
        print(myJSONModel.c) //1.1
    } catch {
        print(error.localizedDescription)
    }
}

當然反過來也可以,你可以將結構實體透過JSONEncoder轉換成JSON Data

let myJSONModel = MyJSONModel(a: "AA", b: 1, c: 0.2)
let jsonEncoder = JSONEncoder()
do {
    let jsonData = try jsonEncoder.encode(myJSONModel)
} catch {
    print(error.localizedDescription)
}



有幾個點必須注意的,你的struct或class定義的屬性,一定要與JSON一致,JSON的Value是整數就必須定義成整數,如果有些時候,這個值可能不存在,就必須定義成可選型別。

變數名稱也必須與JSON的Key相符,若是不相同,則會解析失敗,

有些時候JSON的Key可能與Swift的命名有些衝突,舉例來說有個JSON格式如下

{
    "Name": "Jerry",
    "Age": 18,
}

如果我們想要使用這個JSON,且是轉換成struct或class的話,就得這樣定義

struct User: Codable {
    var Name: String
    var Age: Int
}

但是我們都知道,Swift的命名規則,常數與變數名稱應該為小寫開頭的,因此這樣的命名不是太妥當

你可以一樣將變數命名成小寫,並增加一個enum CodingKeys,且資料型態為String,實作CodingKey這個Protocol。

struct User: Codable {
    var name: String
    var age: Int
    
    
    enum CodingKeys: String, CodingKey {
        case name = "Name"
        case age = "Age"
    }
}

enum的case為你的所有變數名稱,後方則是JSON對應的Key真正的樣子,如此一來,即使JSON的Key與Swift的命名有出入,也可以更換成我們習慣的命名規則。

let jsonString = """
{
  "Name": "Jerry",
  "Age": 18
}
"""

//將JSON字串轉換成Data
if let jsonData = jsonString.data(using: .utf8) {
    do {
        let jsonDecoder = JSONDecoder()
        let user = try jsonDecoder.decode(User.self, from: jsonData)
        print(user.name) //Jerry
        print(user.age) //18
    } catch {
        print(error.localizedDescription)
    }
}

此外,你可以解析JSONDecoder所拋出的例外


            do {
                let jsonDecoder = JSONDecoder()
                let myJSONModel = try jsonDecoder.decode(JSONModel.self, from: jsonData)
            } catch DecodingError.keyNotFound(let key, let context) {

            } catch DecodingError.typeMismatch(let type, let context) {
                
            } catch DecodingError.valueNotFound(let type, let context) {
                
            } catch  {
                
            }



沒有留言:

張貼留言