Ultimate Guide to JSON Parsing With Swift 4 (Phần 2)
Bài đăng này đã không được cập nhật trong 7 năm
Mở đầu
Trong bài viết trước, tôi đã giới thiệu cơ bản về cách Parsing Data JSON trên swift 4, phương pháp xử lý một số kiểu dữ liệu thông dụng sử dụng công cụ có sẵn mà Apple cung cấp. Trong phần này tôi sẽ tiếp tục giới thiệu tới các bạn các thuộc tính nâng cao của Codable để Decode và encode JSON format.
Customizing Key Names
Thông thường trường hợp sử dụng của API cho việc đặt tên các khóa và phong cách này không khớp với các nguyên tắc đặt tên cho thuộc tính Swift.
Để tùy chỉnh điều này, chúng ta cần phải implement Codable default.
Các key được handled tự động bởi "CodingKeys" enumeration do trình biên dịch tạo ra. Enum này phù hợp với CodingKey, định nghĩa cách chúng ta có thể kết nối một thuộc tính với một giá trị kiểu encoded fomat.
Để tùy chỉnh các key, chúng ta sẽ phải viết việc thực hiện điều này
struct Beer : Codable {
// ...
enum CodingKeys : String, CodingKey {
case name
case abv = "alcohol_by_volume"
case brewery = "brewery_name"
case style
}
}
Ví dụ về một instanse Beer và endcode nó dưới dạng JSON:
let encoder = JSONEncoder()
let data = try! encoder.encode(beer)
print(String(data: data, encoding: .utf8)!)
Output:
{"style":"ipa","name":"Endeavor","alcohol_by_volume":8.8999996185302734,"brewery_name":"Saint Arnold"}
Chúng ta có thể tùy chỉnh định dạng ouput của JSONEncoder để làm cho nó trở nên đẹp hơn với thuộc tính outputFormatting.
encoder.outputFormatting = .prettyPrinted
{
"style" : "ipa",
"name" : "Endeavor",
"alcohol_by_volume" : 8.8999996185302734,
"brewery_name" : "Saint Arnold"
}
Wrapper Keys
Thông thường API sẽ bao gồm các tên khóa wrapper để đối tượng JSON root luôn là một đối tượng.
Ví dụ như sau:
{
"beers": [ {...} ]
}
Để thể hiện điều này trong Swift, chúng ta có thể tạo một kiểu mới cho response:
struct BeerList : Codable {
let beers: [Beer]
}
Lưu ý rằng chúng ta đang sử dụng kiểu Array ở đây.
Dealing with Object Wrapping Keys
Một ví dụ khác, response array mà mỗi đối tượng trong mảng được wrapped bởi 1 key:
[
{
"beer" : {
"id": "uuid12459078214",
"name": "Endeavor",
"abv": 8.9,
"brewery": "Saint Arnold",
"style": "ipa"
}
}
]
Chúng ta có thể thực hiện như sau:
[[String:Beer]]
hoặc
Array<Dictionary<String, Beer>>
Decoded:
let decoder = JSONDecoder()
let beers = try decoder.decode([[String:Beer]].self, from: data)
dump(beers)
▿ 1 element
▿ 1 key/value pair
▿ (2 elements)
- key: "beer"
▿ value: __lldb_expr_37.Beer
- name: "Endeavor"
- brewery: "Saint Arnold"
- abv: 8.89999962
- style: __lldb_expr_37.BeerStyle.ipa
More Complex Nested Response
Đôi khi dữ liệu respone từ API trả về rất phức tạp, ví dụ như sau:
{
"meta": {
"page": 1,
"total_pages": 4,
"per_page": 10,
"total_records": 38
},
"breweries": [
{
"id": 1234,
"name": "Saint Arnold"
},
{
"id": 52892,
"name": "Buffalo Bayou"
}
]
}
Chúng ta có thể thực hiện việc decode/encode JSON trong swift như sau:
struct PagedBreweries : Codable {
struct Meta : Codable {
let page: Int
let totalPages: Int
let perPage: Int
let totalRecords: Int
enum CodingKeys : String, CodingKey {
case page
case totalPages = "total_pages"
case perPage = "per_page"
case totalRecords = "total_records"
}
}
struct Brewery : Codable {
let id: Int
let name: String
}
let meta: Meta
let breweries: [Brewery]
}
Custom Encoding
Để bắt đầu, chúng ta bắt đầu với việc encoding:
extension Beer {
func encode(to encoder: Encoder) throws {
}
}
Thêm một vài thuộc tính của ** Beer**:
struct Beer : Coding {
// ...
let createdAt: Date
let bottleSizes: [Float]
let comments: String?
enum CodingKeys: String, CodingKey {
// ...
case createdAt = "created_at",
case bottleSizes = "bottle_sizes"
case comments
}
}
Trong method này chúng ta cần lấy bộ encoder, lấy một "container" và encode các giá trị vào nó.
What is a container?
Các kiểu container: Keyed Container – cung cấp các giá trị bởi các key. đây có nghĩa là một dictionary. Unkeyed Container – cung cấp các giá trị không bởi các key. Trong JSONEncoder, đây có nghĩa là một array. Single Value Container – Output là một kiểu giá trị nguyên và không bởi bao hàm phần tử nào. Để bắt đầu encode bắt đầu chúng ta phải khai báo một container:
var container = encoder.container(keyedBy: CodingKeys.self)
Có 2 điều cần chú ý
- Container phải là một thuộc tính có thể thay đổi, vì chúng ta sẽ ghi vào nó, do đó, biến phải được khai báo với var
- Chúng ta phải xác định các key để nó biết chúng ta có thể endcode những gì chúng ta vào trong container này Tiếp theo chúng ta cần phải encode các giá trị vào container. Bất kỳ cuộc gọi nào trong số này có thể gây ra lỗi, vì vậy chúng tôi sẽ bắt đầu bằng try :
try container.encode(name, forKey: .name)
try container.encode(abv, forKey: .abv)
try container.encode(brewery, forKey: .brewery)
try container.encode(style, forKey: .style)
try container.encode(createdAt, forKey: .createdAt)
try container.encode(comments, forKey: .comments)
try container.encode(bottleSizes, forKey: .bottleSizes)
Đối với trường comments, mặc Encodable sử dụng encodeIfPresent trên optional values. Có nghĩa là các key sẽ bị miss nếu chúng nil. Điều này thực sự không phải là tốt cho APIs, vậy tốt nhất là bao gồm các key mặc dù chúng có giá trị là nil. Ở đây, chúng ta buộc output các key này bằng cách sử dụng encode (: forKey thay vì encodeIfPresent (: forKey .
Và encoded JSON sẽ như sau:
{
"comments" : null,
"style" : "ipa",
"brewery_name" : "Saint Arnold",
"created_at" : "2016-05-01T12:00:00Z",
"alcohol_by_volume" : 8.8999996185302734,
"bottle_sizes" : [
12,
16
],
"name" : "Endeavor"
}
Tổng kết
Ở trên, tôi đã giới thiệu tới các bạn các thuộc tính nâng cao của Codable để Decode và encode JSON format. Cám ơn các bạn đã đọc bài, trong bài tiếp theo tôi tiếp tục giới thiệu về các thuộc tính nâng cao của Codable.
Nguồn:
https://developer.apple.com/documentation/swift/codable http://benscheirman.com/2017/06/ultimate-guide-to-json-parsing-with-swift-4/
All rights reserved