顯示具有 JSON 標籤的文章。 顯示所有文章
顯示具有 JSON 標籤的文章。 顯示所有文章

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  {
                
            }



2016年7月28日 星期四

[Objective-C] JSONModel的使用方法

JSON是相當常見的資料交換格式,透過內建的NSJSONSerialization可以將JSON字串轉換成NSDictionary,在透過Key將Value讀出。

但通常我們不會直接使用這個NSDictionary,而是會在創建一個屬於此JSON資料的Model,使用Model來存取相關的屬性。

NSDictionary轉成Model在傳統的做法必須一層一層解析,程式碼較難維護,且因為人為的疏失可能會輸入錯誤的Key值,而導致一些不可預期的錯誤。

假設目前有個JSON資料如下











你可能會先創一個儲存這些資料的Class,例如



你一開始拿到的原始資料應該會是字串格式,透過NSJSONSerialization轉成NSDictionary,最後在把NSDictionary裡面的資料塞到你剛才創建的Class之中,你的程式碼看起來會像這個樣子。



你必須小心的確認Key有沒有打錯,還要依照不同的資料型態來做處理,若是多層的會寫許多的迴圈才有辦法將所有的JSON資料轉換成你建立的Model。

你可以透過一些第三方套件來完成這些事情,其中一個就是JSONModel。

https://github.com/jsonmodel/jsonmodel

直接下載或者使用CocoaPods將此套件加入到你的專案之中。

把你要儲存資料的Class,繼承JSONModel。



接著你只要將JSON字串塞到初始化之中,他就會將值塞到對應的屬性之中了。



除了字串以外,當然也可以使用字典來初始化(initWithDictionary)。

假如JSON有些屬性是不一定會回傳的,你要在你的屬性之中加入Optional。



這樣在你的JSON資料只有這些時,就不會發生問題。








此外,若是你這個屬性與JSON資料並不相關時,你可以在屬性之中加入Ignore。

JSON裡面有陣列,或者是又有其他的物件,也是可以處理的。

假設你的JSON資料如下。
















JSON資料中有個food的陣列,而陣列內容是另一個JSON物件,這時就要做特別的處理了。

任何是Array型態的Class,你必須在創建一個與該Class名稱一致的protocol,然後放到NSArray時,在NSArray裡面指定的資料型態是該protocol,而不是Class。

此時你的程式碼會像是這個樣子。



接著就可以使用JSONModel來塞入資料了



要拿出Food的內容,可以像上面的程式碼一樣,指定一個變數來存取,

但是有些人可能會想說怎麼不用 . 來存取,因為是資料型態是protocol,所以沒辦法用 . 來存取。

可是你可以使用類似方法的方式來存取屬性。



如此一來大部分的JSON資料應該都可以轉換成物件了,只是要注意如果是Array時的狀況即可,還有你創建的屬性名稱,必須與JSON的Key是一致的。

2014年12月23日 星期二

[Objective-C] 利用NSData dataWithContentsOfURL取得JSON文件

JSON是一種輕量級的資料交換格式,我們時常會需要從網路下載並解析JSON文件有許多方式可以完成此目標,在此說明其中一種。

網路上有許多JSON文件可以免費利用,這個範例是使用台北市政府資料開放平台提供JSON。

臺北市健康服務中心
資料集描述:各區健康服務中心
主要欄位說明:健康服務中心名稱、網址、行政區、地址、電話

假設我們要取出名稱及地址,顯示在TableView之中。

宣告一個靜態字串變數,內容為JSON文件內容的網址,以利未來使用。
static NSString * const KJSON = @"http://data.taipei.gov.tw/opendata/apply/json/RkRDNjk0QzUtNzdGRC00ODFCLUJBNDktNEJCMUVCMDc3ODFE";

在TableView之中,加入一個Bar Button Item並設定一個Action名稱為getJsonAction











- (IBAction)getJsonAction:(UIBarButtonItem *)sender {}

在設定一個全域型的NSMutableArray來儲存JSON
NSMutableArray *jsondata;
當我們按下此按鈕後,就下載JSON文件並顯示於TableView之中
NSURL *url = [NSURL URLWithString:KJSON];
//設定URL路徑
NSError *error;
//創建錯誤訊息物件
使用NSData中的類別方法
+ (instancetype)dataWithContentsOfURL:(NSURL *)aURL options:(NSDataReadingOptions)mask error:(NSError **)errorPtr
此方法是同步傳輸,如果網路狀態很差的話,會給使用者很差的體驗,因為會卡住整個程式很久。

參數說明
dataWithContentsOfURL: 丟入一個NSURL

options:設定NSDataReadingOptions,有三種狀態可以選
NSDataReadingMappedIfSafe 會儲存到虛擬記憶體中,如果安全且可行 
NSDataReadingUncached 只會讀取,不會儲存到記憶體中,較快速
NSDataReadingMappedAlways 不管安不安全,只要能存,就存到記憶體中
error:錯誤訊息
NSData *data = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error];
//我們丟入URL,狀態選擇只讀不存 在加入判斷式,確定無誤在存入jsondata之中
利用NSJSONSerialization的類別方法
+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error
參數說明
data:丟入要轉換的data物件
options:NSJSONReadingOptions
NSJSONReadingMutableContainers 回傳的JSON之中的Dictionary或Array,都是可變(mutable)的
NSJSONReadingMutableLeaves 回傳的JSON之中的子節點若是String,則會回傳可變(mutable)的
NSJSONReadingAllowFragmentsJSON 頂層元素如果不是Array或Dictionary,也能解析此JSON
最後在把TableView的DataSource設定即可
執行結果