基于嵌套的type属性反序列化JSON数组

时间:2019-10-06 11:52:32

标签: 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协议与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代码兼容? 我知道您可以通过以下方法手动解决此问题:首先获取每个Row的{​​{1}}字符串,然后创建正确类型的type模型,并将它们从结构更改为类,然后让{ {1}}协议应改为超类。但是有没有一种方法可以减少手工劳动呢?

1 个答案:

答案 0 :(得分:1)

使用具有关联值的private void connectDb() { SqlConnection conn = new SqlConnection("YOUR DATABASE LOCATION/ DATABASE PATH"); conn.Open(); // initalize your database connection } private void searchForStudentName() { string name;// = your textbox that holds value of students name; this.connectDb(); //your connection into database SqlCommand cmd = new SqlCommand("Select * from ['YOUR TABLE NAME'] where Name = @Name",conn); cmd.parameters.AddWithValue("@Name", name); dr= cmd.ExcecuteReader(); if(dr.Read())//meaning, the system found the value of the input data/ name { ListViewItem lvi = new ListViewItem(dr["NAME"].ToString);// name will be displayed in listview control listView1.items.add(lvi); }else { //there is no data found } } 是最佳选择:

考虑这个枚举:

enum

进行以下更改:

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)!

您现在可以将所需的其他任何情况添加到解码器中,以构建您的 application builder 应用。