swift - 基于嵌套类型属性反序列化 JSON 数组

标签 swift codable

考虑这个示例 JSON:

{
    "sections": [{
        "title": "Sign up",
        "rows": [
            {
                "type": "image",
                "imageURL": "https://example.com/image.jpg"
            },
            {
                "type": "textField",
                "value": "",
                "placeholder": "Username"
            },
            {
                "type": "textField",
                "placeholder": "password"
            },
            {
                "type": "textField",
                "placeholder": "confirmPassword"
            },
            {
                "type": "button",
                "placeholder": "Register!"
            }
        ]
    }]
}

假设我想将上面的 JSON 解析为以下模型(我知道它不会编译,因为 Row 协议(protocol)与 Decodable 不对应):

enum RowType: String, Codable {
    case textField
    case image
    case button
}

protocol Row: Codable {
    var type: RowType { get }
}

struct TextFieldRow: Row {
    let type: RowType
    let placeholder: String
    let value: String

    enum CodingKey: String {
        case type
        case placeholder
        case value
    }
}

struct ImageRow: Row {
    let type: RowType
    let imageURL: URL

    enum CodingKey: String {
        case type
        case imageURL
    }
}

struct ButtonRow: Row {
    let type: RowType
    let title: String

    enum CodingKey: String {
        case type
        case title
    }
}

struct Section: Codable {
    let rows: [Row]
    let title: String

    enum CodingKey: String {
        case rows
        case title
    }
}

struct Response: Codable {
    let sections: [Section]

    enum CodingKey: String {
        case sections
    }
}


// Parsing the response using the Foundation JSONDecoder
let data: Data // From network
let decoder = JSONDecoder()
do {
    let response = try decoder.decode(Response.self, from: data)
} catch {
    print("error: \(error)")
}

有没有办法让 Codable 上面的 Swift 代码兼容? 我知道您可以手动解决这个问题,首先获取每个 Rowtype 字符串,然后创建正确类型的 Row 模型并更改它们从结构到类,并让 Row 协议(protocol)成为一个父类(super class)。但有没有一种方法可以减少体力劳动?

最佳答案

使用具有关联值的枚举是最佳选择:

考虑这个枚举:

enum Row: Decodable {
    case textField(TextFieldRow)
    case image(ImageRow)
    // and other cases

    case unknown

    enum CodingKeys: String, CodingKey {
        case type
    }

    public init(from decoder: Decoder) throws {
        do {
            let selfContainer = try decoder.singleValueContainer()
            let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
            let type = try typeContainer.decode(String.self, forKey: .type)

            switch type {
            case "textField": self = .textField( try selfContainer.decode(TextFieldRow.self) )
            case "Image": self = .image( try selfContainer.decode(ImageRow.self) )
            // and other cases
            default: self = .unknown
            }
        }
    }
}

有了这些变化:

struct TextFieldRow: Decodable {
    let placeholder: String?
    let value: String?
}

struct ImageRow: Decodable {
    let imageURL: URL
}

// and so on

现在这将像魅力一样解码:

// Minmal testing JSON
let json = """
[
            {
                "type": "image",
                "imageURL": "https://example.com/image.jpg"
            },
            {
                "type": "textField",
                "value": "",
                "placeholder": "Username"
            },
            {
                "type": "textField",
                "placeholder": "password"
            }
]
""".data(using: .utf8)!
let decoder = JSONDecoder()
print( try! decoder.decode([Row].self, from: json) )

您现在可以将您需要的任何其他案例添加到解码器以构建您的应用程序构建器应用程序。

关于swift - 基于嵌套类型属性反序列化 JSON 数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58256964/

相关文章:

swift - 如何实现上下文可控的 Swift Codable Encode 函数?

Swift 4 解码简单根级别 json 值

json - 使用 JSONDecoder 和可编码协议(protocol)进行联网

Swift4:新的 Codable 协议(protocol)问题

swift - 重音字母和表情符号

ios - 单个 JSON 返回的多个可编码对象类型

ios - 尼尔· swift 词典

ios - 无法将类型 'TestApplication.ViewControllerResultat' 的值转换为 'TestApplication.ViewControllerForkertKorrektSide'

iOS Today 扩展崩溃

swift - 当包装值设置为 nil 时,弱包装类是否会抛出基于 Hashable 的集合?