简体   繁体   English

如何从复杂的 json 创建简单的可编码结构

[英]How to create simple codable struct from complex json

I receive a complex json response from the API something similar to this.我从 API 收到了一个类似于此的复杂 json 响应。

{
    "result": "success",
    "count": 100,
    "details": [{
            "unnecessaryDetails": "something",
            "area": {
                "name": "Test1"
            }
        },
        {
            "unnecessaryDetails": "something",
            "area": {
                "name": "Test2"
            }
        },
        {
            "unnecessaryDetails": "something",
            "area": {
                "name": "Test3"
            }
        }
    ]
}

My struct is我的结构是

struct Person {
    var name: String
}

struct Response {
    var result: String
    var count: Int
    var details: [Person]
}

I don't want to create properties for everything I receive from the response.我不想为我从响应中收到的所有内容创建属性。 I can't ask the backend developer to give the necessary details only.我不能要求后端开发人员只提供必要的详细信息。 How to avoid unnecessary details and create struct with require details only?如何避免不必要的细节并仅使用需要的细节创建结构?

You can skip intermediate arrays and dictionaries with nested containers.您可以跳过带有嵌套容器的中间数组和字典。

struct Person : Decodable {
    let name: String
}

struct Response : Decodable {
    let result: String
    let count: Int
    let details: [Person]

    enum CodingKeys: String, CodingKey { case result, count, details }
    enum DetailCodingKeys: String, CodingKey { case area }

    init(from decoder : Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        result = try container.decode(String.self, forKey: .result)
        count = try container.decode(Int.self, forKey: .count)
        var detailContainer = try container.nestedUnkeyedContainer(forKey: .details)
        var people = [Person]()
        while !detailContainer.isAtEnd {
            let areaContainer = try detailContainer.nestedContainer(keyedBy: DetailCodingKeys.self)
            let person = try areaContainer.decode(Person.self, forKey: .area)
            people.append(person)
        }
        details = people
    }
}

However the effort is much bigger than adding the extra struct然而,工作量比添加额外的结构要大得多

struct Response : Decodable {
    let result: String
    let count: Int
    let details: [Detail]
}

struct Detail : Decodable {
    let area : Person
}

struct Person : Decodable {
    let name: String
}

You can use Codable to parse only property you want to parse and rest of all will be ignored, If you want to parse json in 2 separate models, you can follow this question's answer.您可以使用Codable仅解析您要解析的属性,其余的将被忽略,如果您想在 2 个单独的模型中解析 json,则可以按照此问题的答案进行操作。

Is it possible to decode single level JSON into 2 separate models? 是否可以将单级 JSON 解码为 2 个单独的模型?

Use Codable (remove the fields however you want): 使用Codable (根据需要删除字段):

import Foundation

struct Response : Codable {
    let result : String?
    let count : Int?
    let details : [Details]?

    enum CodingKeys: String, CodingKey {

        case result = "result"
        case count = "count"
        case details = "details"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        result = try values.decodeIfPresent(String.self, forKey: .result)
        count = try values.decodeIfPresent(Int.self, forKey: .count)
        details = try values.decodeIfPresent([Details].self, forKey: .details)
    }

}

Details struct : 详细struct

struct Details : Codable {
    let unnecessaryDetails : String?
    let area : Area?

    enum CodingKeys: String, CodingKey {

        case unnecessaryDetails = "unnecessaryDetails"
        case area = "area"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        unnecessaryDetails = try values.decodeIfPresent(String.self, forKey: .unnecessaryDetails)
        area = try values.decodeIfPresent(Area.self, forKey: .area)
    }

}

Area struct : 区域struct

struct Area : Codable {
    let name : String?

    enum CodingKeys: String, CodingKey {

        case name = "name"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decodeIfPresent(String.self, forKey: .name)
    }

}

You can even use SwiftyJSON to parse only the fields you want. 您甚至可以使用SwiftyJSON仅解析所需的字段。

You can use Codable for this situation. 您可以在这种情况下使用Codable It will automatically ignore the properties you did not define in your struct. 它将自动忽略您未在结构中定义的属性。 Your structs can be like this: 您的结构可以像这样:

struct Response: Codable {
    let result: String
    let count: Int
    let details: [Detail]
}

struct Detail: Codable {
    let area: Area
}

struct Area: Codable {
    let name: String
}

And to parse: 并解析:

let response = try? JSONDecoder().decode(Response.self, from: jsonData)

After that you can reach out to any properties you defined in your struct. 之后,您可以访问在结构中定义的任何属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM