如何从字典中制作 Decodable 对象?

Aar*_*her 3 decoding swift

我想要一个可以通过普通 Codable 协议或字典实例化的结构(现有代码需要字典实例化)。

我在操场上有这个代码,但我不确定在我的第二个需要字典的初始化中做什么。如何从字典中制作解码器对象?

import Foundation

public protocol Parsable: Decodable {
    init(dict: [String: Any]) throws
}

struct LinkModel: Parsable {
    var href: String
    var text: String

    init(dict: [String: Any]) throws {
        href = "/store/options.aspx"
        text = "Buy"
    }
}

struct ResponseModel: Parsable {
    var link: LinkModel?
    let showCell: Bool

    enum CodingKeys : String, CodingKey {
        case link
        case showCell = "show"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        let linkResponses = try container.decode([LinkModel].self, forKey: .link)
        link = linkResponses.first

        showCell = try container.decode(Bool.self, forKey: .showCell)
    }

    init(dict: [String: Any]) throws {
        let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
        let decoder = ??? // what do I do here?
        self.init(from: decoder)
    }
}


let jsonText = """
{
    "show": true,
    "link": [
        {
        "text": "Buy",
        "href": "/store/options.aspx"
        }
    ]
}
"""

// test standard Decodable instantiation
let jsonData = jsonText.data(using: .utf8)!
let model = try! JSONDecoder().decode(ResponseModel.self, from: jsonData)

print(model.link?.href)

// test dictionary instantiation
...
Run Code Online (Sandbox Code Playgroud)

wad*_*dda 9

扩展您的Parsable协议以自动生成您正在寻找的初始化程序。

extension Parsable {
    init(dict: [String: Any]) throws {
        let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
        let decoder = JSONDecoder()
        self = try decoder.decode(Self.self, from: jsonData)
    }
}
Run Code Online (Sandbox Code Playgroud)


Hon*_*ney 5

你走在正确的道路上。

import Foundation

public protocol Parsable: Decodable {
    init(dict: [String: Any]) throws
}

struct LinkModel: Parsable {
    var href: String
    var text: String

    init(dict: [String: Any]) throws {
        href = "/store/options.aspx"
        text = "Buy"
    }
}

struct ResponseModel: Parsable {
    var link: LinkModel?
    let showCell: Bool

    enum CodingKeys : String, CodingKey {
        case link
        case showCell = "show"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        let linkResponses = try container.decode([LinkModel].self, forKey: .link)
        link = linkResponses.first

        showCell = try container.decode(Bool.self, forKey: .showCell)
    }

    init(dict: [String: Any]) throws {
        let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
        // 1.
        let decoder = JSONDecoder()
        // 2. 
        let result = try decoder.decode(ResponseModel.self, from: jsonData)
        // 3. 
        self = result


    }
}


let jsonText = """
{
    "show": true,
    "link": [
        {
        "text": "Buy",
        "href": "/store/options.aspx"
        }
    ]
}
"""

// test standard Decodable instantiation
let jsonData = jsonText.data(using: .utf8)!
let model = try! JSONDecoder().decode(ResponseModel.self, from: jsonData)

print(model.link?.href)
Run Code Online (Sandbox Code Playgroud)

我所做的只是:

  1. 创建一个 JSONdecoder 对象。
  2. 使用 JSONdecoder 来解码 type 的对象ResponseModel
  3. 将解码结果赋给self。这样 的所有属性都self被分配了。

  • @AaronBratcher FWIW如果采用“Decodable”的对象是类类型,那么您将不被允许在“init”方法中重新分配“self”。如果您尝试,您会收到错误:**无法分配给值:'self'是不可变的**。这仅适用于结构,即值类型。 (2认同)