Swift Codable

Cách đây rất lâu, chúng ta đã từng sử dụng class JsonSerialization để chuyển đổi sang và từ các đối tượng tùy chỉnh dữ liệu JSON trong iOS. Trong khi truy xuất dữ liệu từ sever web (API), trước tiên, chúng tôi chuyển đổi dữ liệu thành Đối tượng JSON và sau đó nhập truyền các thuộc tính từ Đối tượng JSON đó sang các loại phù hợp.

Swift 4 đã thay đổi hoàn toàn điều này với việc giới thiệu codable Protocols. Codable Protocol là thành phần của hai giao thức được gọi là 'Encodable' và 'Decodable'.

JSON :

Trong máy tính, JavaScript Object Notation hay JSON là một định dạng tệp tiêu chuẩn mở sử dụng văn bản có thể đọc được của con người để truyền các đối tượng dữ liệu bao gồm các cặp giá trị thuộc tính và kiểu dữ liệu mảng (hoặc bất kỳ giá trị có thể tuần tự hóa nào khác). Đây là một định dạng dữ liệu rất phổ biến được sử dụng cho giao tiếp browser–server không đồng bộ, bao gồm cả việc thay thế cho XML trong một số hệ thống kiểu AJAX (Wiki).

JSON là một định dạng dữ liệu văn bản và độc lập với ngôn ngữ mà nhiều dịch vụ web sử dụng.

JSON sử dụng và chấp nhận các kiểu dữ liệu sau:

String, Int, Double, URL, Date, Data, Array và Dictionary

Ưu điểm:

  • Tất cả các ngôn ngữ lập trình đều hỗ trợ tạo, đọc và giải mã các Đối tượng JSON.

  • Có thể mã hóa bất kỳ định dạng dữ liệu nào thành json và giải mã json sang bất kỳ định dạng dữ liệu nào. Điều này làm cho JSON trở nên mạnh mẽ như vậy.


{
  "id": 1,
  "name": "chair",
  "price": 350,
  "tags": [
    "recliner",
    "foldable"
  ],
  "stock": {
    "warehouse": 300,
    "retail": 20
  }
}

Codable Protocols :

  • Codable protocol chứa hai phương pháp: phương thức đầu tiên là func encode(to encoder: Encoder) là từ Encodable

  • “Func init (from decoder: Decoder)” là từ giao thức Decodable.

Hai lớp trong Foundation Framework của swift, được gọi là:

JSONEncoder & JSONDecoder sử dụng hai phương pháp trên để thực hiện các hoạt động mã hóa và giải mã trên các loại tùy chỉnh được xác nhận với các giao thức được đề cập ở trên.

Thực hiện encoding and decoding

Đối với điều này, tốt hơn là sử dụng các tên thuộc tính tương tự như với các tên thuộc tính có trong cấu trúc JSON cho tất cả các loại tùy chỉnh được xác nhận với các giao thức ở trên.

Nếu bạn muốn sử dụng các tên khác cho các thuộc tính với cấu trúc JSON, bạn cần viết thêm một đoạn code và xác định một enum có tên là CodingKeys, thuộc loại String và được xác nhận với CodingKey Type.

Trên thực tế, các giao thức codable sử dụng enum này trong nội bộ ngay cả khi bạn không xác định trong loại tùy chỉnh của mình. Khi bạn sử dụng các tên thuộc tính khác nhau, bạn phải xác định enum này trong các loại tùy chỉnh của mình để làm cho các biến khác nhau đó dễ hiểu đối với các giao thức. Chúng ta sẽ xem các ví dụ sau.

struct Stock: Codable {
    var warehouse: Int
    var retail: Int
}

struct Product: Codable {
    
    var id: Int
    var name: String
    var price: Int
    var tags: [String]
    var stock: Stock
    
}

func fetchDataFromRemote() {
    //Taking Json as a String type for example purpose
//Normally we will get this from web service calls
    let jsonString =
        """
        {
            "id": 1,
            "name": "chair",
            "price": 350,
            "tags": [
            "recliner",
            "foldable"
            ],
            "stock": {
                "warehouse": 300,
                "retail": 20
            }
        }
        """
        
        if let jsonData = jsonString.data(using: String.Encoding.utf8) as? Data {
            
            let decoder = JSONDecoder()
            
            let product = try! decoder.decode(Product.self, from: jsonData)
            
            print(product)
            
        }
}

OutPut :
Product(id: 1, name: "chair", price: 350, tags: ["recliner", "foldable"], stock: GoalPost.Stock(warehouse: 300, retail: 20))

struct Stock: Codable {
    var warehouse: Int
    var retail: Int
}

struct Product: Codable {
    
    var id: Int
    var name: String
    var price: Int
    var types: [String]
    var stockInfo: Stock
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case price
        case types = "tags"
        case stockInfo = "stock"
    }
    
}

func fetchDataFromRemote() {
        let jsonString =
        """
        {
            "id": 1,
            "name": "chair",
            "price": 350,
            "tags": [
            "recliner",
            "foldable"
            ],
            "stock": {
                "warehouse": 300,
                "retail": 20
            }
        }
        """
        
        if let jsonData = jsonString.data(using: String.Encoding.utf8) as? Data {
            
            let decoder = JSONDecoder()
            
            let product = try! decoder.decode(Product.self, from: jsonData)
            
            print(product)
            print("************************")
            print(product.stockInfo)
            print(product.types)
            
        } 
        
    }

Output :-
Product(id: 1, name: "chair", price: 350, types: ["recliner", "foldable"], stockInfo: GoalPost.Stock(warehouse: 300, retail: 20))
************************
Stock(warehouse: 300, retail: 20)
["recliner", "foldable"]


trong ví dụ trên, chúng ta có type "Product" được xác nhận với Codable’ protocol. trong ‘product’, có một biến ‘Stock’, đây là một loại ‘Stock’ cũng được xác nhận là Codable. Tất cả các tên biến trong các loại tùy chỉnh đó được đặt tên chính xác như cấu trúc json. để decoding, trước tiên chúng ta cần có dữ liệu.

trong ví dụ, chúng tôi đã khởi tạo một biến "decoder" với lớp JSONDecoder. Sau đó, chúng tôi gọi phương thức có tên là ‘func decode (Codable.Type, from:)’ chấp nhận 2 tham số, tham số đầu tiên là kiểu được xác nhận với Codable và tham số thứ hai là kiểu đối tượng của Dữ liệu. Hàm này có thể throwable, vì vậy chúng ta cần xử lý các trường hợp lỗi.

Điều gì sẽ xảy ra nếu thuộc tính JSON khác với thuộc tính Product type ?


struct Stock: Codable {
    var warehouse: Int
    var retail: Int
}

struct Product: Codable {
    
    var id: Int
    var name: String
    var price: Int
    var types: [String]
    var stockInfo: Stock
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case price
        case types = "tags"
        case stockInfo = "stock"
    }
    
}

func fetchDataFromRemote() {
        let jsonString =
        """
        {
            "id": 1,
            "name": "chair",
            "price": 350,
            "tags": [
            "recliner",
            "foldable"
            ],
            "stock": {
                "warehouse": 300,
                "retail": 20
            }
        }
        """
        
        if let jsonData = jsonString.data(using: String.Encoding.utf8) as? Data {
            
            let decoder = JSONDecoder()
            
            let product = try! decoder.decode(Product.self, from: jsonData)
            
            print(product)
            print("************************")
            print(product.stockInfo)
            print(product.types)
            
        } 
        
    }

Output :-
Product(id: 1, name: "chair", price: 350, types: ["recliner", "foldable"], stockInfo: GoalPost.Stock(warehouse: 300, retail: 20))
************************
Stock(warehouse: 300, retail: 20)
["recliner", "foldable"]

Trong ví dụ trên, hãy quan sát các thuộc tính Product type, hai thuộc tính cuối cùng khác nhau thì ở cấu trúc json.

Vì vậy, để làm cho chúng dễ hiểu với protocols, để encoding và decoding, chúng ta phải sử dụng các CodingKeys liệt kê, trong loại đó. Khi chúng ta khai báo enum, chúng ta cần define điều đó với tất cả các thuộc tính của custom type, bao gồm các raw values giống như trong json cho những thuộc tính khác với trong json. Vì vậy, sau đó Codables biết cách map chúng với các biến thích hợp assign và get values trong encoding và decoding.

struct Stock: Codable {
    var warehouse: Int
    var retail: Int
}

struct Product: Codable {
    
    var id: Int
    var name: String
    var price: Int
    var types: [String]
    var stockInfo: Stock
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case price
        case types = "tags"
        case stockInfo = "stock"
    }
    
}

func exampleEncodeFunction() {
        
        let tableStock = Stock.init(warehouse: 114, retail: 16)
        let table = Product.init(id: 112, name: "Long Table", price: 450, types: ["Long", "Study", "Short"], stockInfo: tableStock)
        
        let encoder = JSONEncoder()
        
        let productData = try! encoder.encode(table.self)
        
        let productStr = String.init(data: productData, encoding: .utf8)
        print("String from Product data is \(productStr ?? "No data")")
        
    }

Output :-
String from Product data is {"id":112,"tags":["Long","Study","Short"],"name":"Long Table","price":450,"stock":{"retail":16,"warehouse":114}}

Bạn define Stock và Product types,Sau đó với sự trợ giúp của object JSONEncoder, bằng cách gọi hàm func encode (_ to: Encodable), Chúng ta đã encoded product type thành dữ liệu json. Ví dụ, mục đích để hiển thị dữ liệu được encoded data đó, chúng ta đã chuyển đổi object to String và print out.


All Rights Reserved