详解 Codable 的用法

作者: IAM121 | 来源:发表于2023-03-16 10:22 被阅读0次
    1.Codable 是什么
    typealias Codable = Decodable & Encodable
    

    Codable 也可以代表苹果为 Swift 开发的一套编解码系统。

    2. JSON 和 模型的相互转换

    Swift 是一门静态语言,本身是没有像 Objective-C 那样的动态 Runtime 的。苹果提供了 JSONEncoder 和 JSONDecoder 这两个结构体来方便得在 JSON 数据和自定义模型之间互相转换。
    只要让自己的数据类型符合 Codable 协议,就可以用系统提供的编解码器进行编解码。

    struct User: Codable {
        var name: String
        var age: Int
    }
    
    2.1 解码(JSON Data -> Model):
    let json = """
        {
            "name": "zhangsan",
            "age": 25
        }
        """.data(using: .utf8)!
    let user = JSONDecoder().decode(User.self, from: json)
    
    2.2 编码(Model -> JSON Data):
    let data = JSONEncoder().encode(user)
    
    3. Codable 支持的数据类型
    3.1 基础数据类型

    在 Swift 标准库的声明文件中可以看到,基础类型都通过 extension 实现了 Codable 协议。

    对于基础类型的属性,JSONEncoder 和 JSONDecoder 都可以正确的处理。

    3.2 Date

    JSONEncoder 提供了 dateEncodingStrategy 属性来指定日期编码策略。
    同样 JSONDecoder 提供了 dateDecodingStrategy 属性。
    就拿 dateDecodingStrategy 为例,它是一个枚举类型。

    struct User: Codable {
        var name: String
        var age: Int
        var birthday: Date
    }
    
    let json = """
        {
            "name": "zhangsan",
            "age": 18,
            "birthday": "2022-09-12T10:25:41+00:00"
        }
        """.data(using: .utf8)!
    
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .iso8601
    let user = decoder.decode(User.self, from: json)
    // user.birthday 正确解码为 Date 类型
    
    3.3 嵌套对象

    在自定义模型中嵌套对象的时候,只要这个嵌套对象也符合 Codable 协议,那整个对象就可以正常使用 JSONEncoder 和 JSONDecoder 编解码

    struct UserInfo: Codable {
        var name: String
        var age: Int
    }
    
    struct User: Codable {
        var info: UserInfo
    }
    
    3.4 枚举
    4.自定义 CodingKeys

    自定义 CodingKeys 主要是两个目的

    1. 当数据类型属性名和 JSON 中字段名不同时,做 key 的映射。
    2. 通过在不添加某些字段的 case,来跳过某些字段的编解码过程。
    struct User: Codable {
        var name: String
        var age: Int
        var birthday: Date?
    
        enum CodingKeys: String, CodingKey {
            case name = "userName"
            case age = "userAge"
        }
    }
    

    CodingKeys 必须是一个 RawValue 为 String 类型的枚举,并符合 CodingKey 协议。以上代码实现的效果为,为 name 和 age 字段做了 key 映射,让编解码过程中不包含 birthday 字段。

    5.多嵌套模型例子
    5.1模拟请求数据列表
    {
        "code": 10000,
        "msg": "请求成功",
        "datas" : [
            {
                "id": 1001,
                "name": "Durian",
                "points": 600,
                "parameters": {
                    "size": "80*80",
                    "area": "Thailand",
                    "quality": "bad"
                },
                "description": "A fruit with a distinctive scent."
            },
            {
                "id": 1002,
                "name": "Apple",
                "points": 600,
                "parameters": {
                    "size": "80*80",
                    "area": "China",
                    "quality": "good"
                },
                "description": "A round fruit with shiny red or green skin and firm white flesh."
            },
            {
                "id": 1003,
                "name": "Arange",
                "points": 600,
                "parameters": {
                    "size": "80*80",
                    "area": "China",
                    "quality": "caporal"
                },
                "description": "A round citrus fruit with thick reddish-yellow skin and a lot of sweet juice."
            },
            {
                "id": 1004,
                "name": "Banana",
                "points": 600,
                "parameters": {
                    "size": "80*80",
                    "area": "Malaysia",
                    "quality": "good"
                },
                "description": "A long curved fruit with a thick yellow skin and soft flesh, that grows on trees in hot countries."
            }
       ]
    }
    
    
    5.2 创建模型
    enum Quality: String, Codable { //枚举
        case good
        case caporal
        case bad
    }
    
    struct Parameters: Codable {
        var size: String?
        var area: String?
        var quality: Quality?
    }
    
    
    struct GroceryProduct: Codable {
        var ID: Int
        var name: String
        var points: Int?
        var description: String?
        var parameters: Parameters?
        
        enum CodingKeys: String, CodingKey {
            case ID = "id"
            case name
            case points
            case description
            case parameters
        }
    }
    
    
    struct ProductListModel: Codable {
        var code: Int
        var msg: String
        var datas: [GroceryProduct]
    }
    
    5.3 请求数据源并解析
    guard let urlPath = Bundle.main.path(forResource: "ProductList", ofType: "json") else { return }
            do {
                let decoder = JSONDecoder()
                // 数据转json数据
                let data = try Data(contentsOf: URL(fileURLWithPath: urlPath))
                let jsonData: Any = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers)
                print("present = ", jsonData)
                
                // 数据转模型
                let product3 = try decoder.decode(ProductListModel.self, from: data)
                print(product3.datas[2].ID)
    
            } catch let error {
                print(error)
            }
    
    6.模型转换成JSONString例子
    6.1 给Encodable扩展一个toJSONString()方法
    public extension Encodable {
        
        func toJSONString() -> String {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            guard let data = try? encoder.encode(self) else{ return "" }
            guard let jsonStr = String(data: data, encoding: .utf8) else{ return "" }
            return jsonStr
        }
    }
    
    6.2 建立一个模型,遵守Codable(Decodable & Encodable)协议
    struct TestModel: Codable {
        var name: String = ""
        var age: Int = 0
    }
    
    6.3 使用
    let testModel = TestModel(name: "name1", age: 10)
    debugPrint(testModel.toJSONString())
    

    输出结果

    "{\"name\":\"name1\",\"age\":10}"
    
    7. 参考资料

    详解 Codable 的用法和原理

    相关文章

      网友评论

        本文标题:详解 Codable 的用法

        本文链接:https://www.haomeiwen.com/subject/dvwlrdtx.html