+1

Swift - Hướng dẫn làm việc với json

JSON là một cách phổ biến để truyền dữ liệu đến và từ các dịch vụ web. Nó là đơn giản để sử dụng và con người có thể đọc được, đó là lý do tại sao nó là như vậy vô cùng phổ biến.

Hãy xem xét các đoạn JSON sau đây:

[
  {
    "person": {
      "name": "Dani",
      "age": "24"
    }
  },
  {
    "person": {
      "name": "ray",
      "age": "70"
    }
  }
]

Trong Objective-C parsing and deserializing JSON khá đơn giản.

NSArray *json = [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:nil];
NSString *age = json[0][@"person"][@"age"];
NSLog(@"Dani's age is %@", age);

trong Swift parsing and deserializing JSON có chút buồn chán hơn.

var json: Array!
do {
  json = try NSJSONSerialization.JSONObjectWithData(JSONData, options: NSJSONReadingOptions()) as? Array
} catch {
  print(error)
}

if let item = json[0] as? [String: AnyObject] {
  if let person = item["person"] as? [String: AnyObject] {
    if let age = person["age"] as? Int {
      print("Dani's age is \(age)")
    }
  }
}

Trong đoạn mã trên, mỗi đối tượng từ JSON phải được kiểm tra trước khi sử dụng nó thông qua tùy chọn bắt buộc. Điều này làm cho mã của bạn an toàn; nhưng phức tạp hơn JSON của bạn là, xấu xí hơn mã của bạn sẽ trở thành.

guard let item = json[0] as? [String: AnyObject],
  let person = item["person"] as? [String: AnyObject],
  let age = person["age"] as? Int else {
    return;
}
print("Dani's age is \(age)")

Vẫn còn quá dài dòng, không phải là nó? Nhưng làm thế nào bạn có thể làm cho nó đơn giản hơn? Trong JSON hướng dẫn này, bạn sẽ tìm hiểu một cách dễ dàng hơn để phân tích cú pháp JSON trong Swift - sử dụng một thư viện mã nguồn mở phổ biến được gọi là Gloss . nó được sử dụng bởi 25 app hàng đầu trên store mỹ. Bạn sẽ thấy nó đơn giản như với objc.

download demo tại đây:

http://www.raywenderlich.com/wp-content/uploads/2015/11/TopApps-Starter.zip

mở file Swift.playground với xcode và chạy

Các file và resource đã đc tạo sẵn cho việc parse json rồi.Hãy nhìn vào cấu trúc của nó để có được một cái nhìn tổng quan về những gì đang có:

  • Resources folder có thể được truy cập từ Swift code của bạn

    • file topapps.json chứa json cần parse
  • Source folder chứa các file dùng cho dự án :

    • App.swift đại diện cho một ứng dụng, chúng ta cần parse json tại đối tượng này
    • DataManager.swift: quản lí data từ local hay lấy về từ server. Chúng ta sẽ sử dụng các method có sẵn trong file này để lấy dữ liệu

Parsing JSON the Native Swift Way

Đầu tiên, bạn sẽ bắt đầu bằng việc phân tích cú pháp JSON cách Swift bản địa - tức là không sử dụng bất kỳ thư viện bên ngoài. Điều này sẽ giúp bạn hiểu được lợi ích của việc sử dụng một thư viện như Gloss.

Hãy phân tích các tập tin JSON được cung cấp để có được tên của các ứng dụng # 1 trên App Store!

Đầu tiên, trong khi bạn đang đi làm việc với các từ điển rất nhiều, định nghĩa một kiểu bí danh ở trên cùng của sân chơi:

typealias Payload = [String: AnyObject]

đầy đủ code sẽ là:

DataManager.getTopAppsDataFromFileWithSuccess { (data) -> Void in

  var json: Payload!

  // 1
  do {
    json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as? Payload
  } catch {
    print(error)
    XCPlaygroundPage.currentPage.finishExecution()
  }

  // 2
  guard let feed = json["feed"] as? Payload,
    let apps = feed["entry"] as? [AnyObject],
    let app = apps.first as? Payload
    else { XCPlaygroundPage.currentPage.finishExecution() }

  guard let container = app["im:name"] as? Payload,
    let name = container["label"] as? String
    else { XCPlaygroundPage.currentPage.finishExecution() }

  guard let id = app["id"] as? Payload,
    let link = id["label"] as? String
    else { XCPlaygroundPage.currentPage.finishExecution() }

  // 3
  let entry = App(name: name, link: link)
  print(entry)

  XCPlaygroundPage.currentPage.finishExecution()

}

Dưới đây là những gì đang xảy ra ở trên:

  1. Trước tiên, bạn deserialize dữ liệu sử dụng NSJSONSerialization .
  2. Bạn cần phải kiểm tra mỗi giá trị subscript trong đối tượng JSON để đảm bảo rằng nó không phải là nil . Một khi bạn chắc chắn nó có một giá trị hợp lệ, tìm kiếm các đối tượng kế tiếp. Một khi bạn đã thực hiện theo cách của bạn thông qua tất cả các kí hiệu, bạn sẽ nhận được name và link các giá trị để làm việc với. Lưu ý rằng nếu bất kỳ phần tử trong JSON là unexpected, tên ứng dụng không bao giờ được in ra. Đây là mong muốn trong trường hợp này.
  3. Bước cuối cùng là khởi tạo một App bằng cách sử dụng các giá trị của name và link và in ra màn hình console.

Save and run your storyboard; bạn sẽ nhìn thấy kết quả

App(name: "Game of War - Fire Age", link: "https://itunes.apple.com/us/app/game-of-war-fire-age/id667728512?mt=8&uo=2")

Phải mất rất nhiều mã tiết chỉ để lấy tên của các ứng dụng đầu tiên - đó là thời gian để xem làm thế nào để nó lên trong stack Gloss.

Giới thiệu về lập bản đồ đối tượng JSON

Object mapping là một kỹ thuật để chuyển các đối tượng JSON thành đối tượng native Swift. sai khi định nghĩa các model objects và tương ứng với mapping rules. Gloss sẽ thay bạn làm các công việc khó.

Các lợi ích mà nó mang lại: - code của bạn sẽ được sạch hơn, tái sử dụng và dễ dàng hơn để maintain - Bạn có thể làm việc với các đối tượng thay cho các mảng chung chung và từ điển. - Bạn có thể mở rộng các lớp mô hình để thêm chức năng bổ sung.

Phân tích json với cách của gross

Để giữ cho mọi thứ tốt đẹp và sạch sẽ, tạo ra một sân chơi mới gọi là Gloss.playground, sau đó copy topapps.json vào thư mục resoure và DataManager.swift vào Sources. Lồng ghép Gloss trong dự án của bạn Để thêm Gloss vào dự án bạn làm theo các bước sau:

  1. download file từ url sau:
https://codeload.github.com/hkellaway/Gloss/zip/master
  1. giải nén ném thư mục Gloss-master/Gloss/Gloss vào thư mục source

Nó sẽ như sau:

alt

Mapping JSON to objects

Đầu tiên, bạn phải xác định như thế nào đối tượng mô hình của bạn liên quan đến tài liệu JSON của bạn.

Đối tượng mô hình phải phù hợp với Decodeable giao thức, cho phép họ được giải mã từ JSON. Để làm như vậy, bạn sẽ thực hiện các init?(json: JSON) Initializer như định nghĩa trong giao thức.

TopApps

Các TopApps mô hình đại diện cho các đối tượng cấp cao nhất, và nó có chứa một cặp khóa-giá trị duy nhất:

{
  "feed": {
    ...
  }
}

Tạo một tập tin Swift mới gọi là TopApps.swift trong thư mục Sources của sân chơi của bạn và thêm đoạn mã sau:

public struct TopApps: Decodable {

  // 1
  public let feed: Feed?

  // 2
  public init?(json: JSON) {
    feed = "feed" <~~ json
  }

}
  1. định nghĩa 1 properties, ở lựa chọn này ban chỉ cần 1 mà thôi. Bạn sẽ định nghĩa 1 model feed sau

  2. Hãy chắc chắn rằng TopApps phù hợp với các Decodable giao thức bằng cách thực hiện các khởi tạo tùy chỉnh.

Bạn có thể tự hỏi những gì <~~ là! Nó được gọi là Encode Operator và nó được định nghĩa trong tập tin Operators.swift. Về cơ bản nó báo cho Gloss để lấy các giá trị mà thuộc về chính feed và encode nó. Feed là một Decodable đối tượng tốt.

Feed

Feed object là đối tượng cao nhất trong resource. feed có 2 căpk key và value. nhưng bạn chỉ qtam đến 25 đối tượng đầu tiên thôi, bạn ko cần phải truy cập vào đối tượng author.

{
  "author": {
    ...
  },
  "entry": [
    ...
  ]
}

Tạo một tập tin Swift mới gọi là Feed.swift trong thư mục Sources của sân chơi của bạn và xác định Feed như sau:

public struct Feed: Decodable {

  public let entries: [App]?

  public init?(json: JSON) {
    entries = "entry" <~~ json
  }

}

App

App là đối tượng mô hình cuối cùng bạn sẽ định nghĩa. Nó đại diện cho một ứng dụng trong biểu đồ:

{
  "im:name": {
    "label": "Game of War - Fire Age"
  },
  "id": {
    "label": "https://itunes.apple.com/us/app/game-of-war-fire-age/id667728512?mt=8&uo=2",
    ...
  },
  ...
}

Tạo một tập tin Swift mới gọi là App.swift trong thư mục Sources của sân chơi của bạn và thêm đoạn mã sau:

public struct App: Decodable {

  // 1
  public let name: String
  public let link: String

  public init?(json: JSON) {
    // 2
    guard let container: JSON = "im:name" <~~ json,
      let id: JSON = "id" <~~ json
      else { return nil }

    guard let name: String = "label" <~~ container,
      link: String = "label" <~~ id
      else { return nil }

    self.name = name
    self.link = link
  }

}
  1. Feed và TopApp cả hai sử dụng thuộc tính tùy chọn. Nó có thể xác định một properties là không bắt buộc nếu bạn chắc chắn rằng các JSON được sử dụng sẽ luôn luôn có chứa các giá trị để định vị chúng.
  2. Bạn không nhất thiết phải tạo các đối tượng mô hình cho mỗi thành viên trong JSON. Ví dụ, trong trường hợp này nó làm cho không có ý nghĩa để tạo ra một mô hình cho in: tên và id. Khi làm việc với các đối tượng không bắt buộc và lồng nhau, luôn luôn đảm bảo để kiểm tra nil .

Bây giờ các lớp mô hình của bạn đã sẵn sàng điều duy nhất còn lại là để cho Gloss làm công việc của nó!

Mở tập tin sân chơi và thêm đoạn mã sau:

import UIKit
import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

DataManager.getTopAppsDataFromFileWithSuccess { (data) -> Void in
  var json: [String: AnyObject]!

  // 1
  do {
    json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as? [String: AnyObject]
  } catch {
    print(error)
    XCPlaygroundPage.currentPage.finishExecution()
  }

  // 2
  guard let topApps = TopApps(json: json) else {
    print("Error initializing object")
    XCPlaygroundPage.currentPage.finishExecution()
  }

  // 3
  guard let firstItem = topApps.feed?.entries?.first else {
    print("No such item")
    XCPlaygroundPage.currentPage.finishExecution()
  }

  // 4
  print(firstItem)

  XCPlaygroundPage.currentPage.finishExecution()

}
  1. Trước tiên, bạn deserialize dữ liệu sử dụng NSJSONSerialization . Cùng một cách mà bạn đã làm trước đó.
  2. Khởi tạo một thể hiện của TopApps bởi ăn các dữ liệu JSON vào constructor của nó.
  3. Nhận ứng dụng # 1 bằng cách đơn giản nhận được các mục nhập đầu tiên của thức ăn.
  4. In các ứng dụng giao diện điều khiển.

Nghiêm túc - đó là tất cả các mã mà bạn cần.

Lưu và chạy kịch bản của bạn; bạn sẽ thấy rằng bạn có được thành công tên ứng dụng một lần nữa, nhưng lần này trong một cách thanh lịch hơn:

App(name: "Game of War - Fire Age", link: "https://itunes.apple.com/us/app/game-of-war-fire-age/id667728512?mt=8&uo=2")

Đó là bạn lấy dữ liệu ở local. Nhưng làm thế nào bạn sẽ phân tích dữ liệu từ một nguồn ở xa?

Retrieving Remote JSON

Đó là thời gian để thực hiện dự án này một chút thực tế hơn. Thông thường, bạn sẽ được lấy dữ liệu từ xa, không phải dữ liệu từ một tập tin địa phương. Bạn có thể dễ dàng lấy các xếp hạng App Store sử dụng một yêu cầu trực tuyến.

Mở DataManager.swift và xác định các URL trên Apps:

let TopAppURL = "https://itunes.apple.com/us/rss/topgrossingipadapplications/limit=25/json"

sau đó thêm các method sau đây để DataManager thực hiện:

public class func getTopAppsDataFromItunesWithSuccess(success: ((iTunesData: NSData!) -> Void)) {
  //1
  loadDataFromURL(NSURL(string: TopAppURL)!, completion:{(data, error) -> Void in
      //2
      if let data = data {
        //3
        success(iTunesData: data)
      }
  })
}

Đoạn mã trên có vẻ khá quen thuộc; nhưng thay vì lấy một tập tin địa phương mà bạn đang sử dụng NSURLSession để kéo dữ liệu từ iTunes. Dưới đây là những gì đang xảy ra một cách chi tiết:

  1. Đầu tiên bạn gọi loadDataFromURL ; này có URL và đóng cửa hoàn thành mà đi trong một NSData đối tượng.
  2. Tiếp theo, bạn hãy chắc chắn rằng dữ liệu tồn tại sử dụng tùy chọn ràng buộc.
  3. Cuối cùng, bạn chuyển dữ liệu đến việc đóng cửa thành công như bạn đã làm trước đó.

Mở kịch bản của bạn và thay thế dòng

DataManager.getTopAppsDataFromFileWithSuccess { (data) -> Void in

bằng dòng

DataManager.getTopAppsDataFromItunesWithSuccess { (data) -> Void in

Bây giờ bạn đang lấy dữ liệu thực tế từ iTunes.

Lưu và chạy kịch bản của bạn; bạn sẽ thấy rằng các phân tích dữ liệu vẫn đến cùng một kết luận của các ứng dụng # 1:

App(name: "Game of War - Fire Age", link: "https://itunes.apple.com/us/app/game-of-war-fire-age/id667728512?mt=8&uo=2")

Các giá trị trên có thể khác nhau cho bạn, như các ứng dụng hàng đầu trong App Store thay đổi liên tục.

Thông thường mọi người không chỉ quan tâm đến các ứng dụng hàng đầu trên App Store - họ muốn nhìn thấy một danh sách tất cả các ứng dụng hàng đầu. Bạn không cần phải viết mã bất cứ điều gì để truy cập chúng - bạn có thể dễ dàng truy cập chúng với đoạn mã sau:

topApps.feed?.entries

Peeking Under the Hood at Gloss

Bạn chắc chắn có thể thấy Gloss hoạt động thực sự tốt trong quá trình phân tích cú pháp - nhưng làm sao nó làm việc dưới hood. Là một nhà điều hành tùy chỉnh cho một tập hợp các chức năng Decoder.decode. Gloss đã được xây dựng trong hỗ trợ cho giải mã rất nhiều loại:

- Loại đơn giản ( Decoder.decode )
- Mô hình giải mã ( Decoder.decodeDecodable )
- Mảng đơn giản ( Decoder.decode )
- Mảng của các mô hình giải mã ( Decoder.decodeDecodableArray )
- Loại Enum ( Decoder.decodeEnum )
- Mảng Enum ( Decoder.decodeEnumArray )
- Loại NSURL ( Decoder.decodeURL )
- Mảng NSURL ( Decode.decodeURLArray )

Đây là bài viết nguồn:

https://www.raywenderlich.com/120442/swift-json-tutorial

tiếng anh còn hạn chế lên dịch có phần nào sai sót mong các bạn feedback lại cho mình nhé


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.