简体   繁体   English

在Swift中解码嵌套JSON对象的便捷方法?

[英]Convenient way to decode nested JSON object in Swift?

Let's say you have some JSON: 假设您有一些JSON:

{
    "status": "error",
    "data": {
        "errormessage": "Could not get user with ID: -1.",
        "errorcode": 14
    }
}

For a given Error struct: 对于给定的错误结构:

struct APIError: Decodable {
    let code: Int?
    let message: String?

    enum CodingKeys: String, CodingKey {
        case code = "errorcode"
        case message = "errormessage"
    }
}

Hit the web service, get the JSON, and initialize the struct: 点击Web服务,获取JSON,并初始化结构:

let urlRequest = URLRequest(url: url)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest)
{ (data, response, error) in
    // Doesn't work because the portion of the JSON we want is in the "data" key
    let e = try? JSONDecoder().decode(APIError.self, from: data)
}
task.resume()

Is there some easy way to do something like data["data"] ? 有一些简单的方法可以做类似data["data"]事情吗? What's the correct model to follow? 遵循的正确模型是什么?

Solution A - Convert the data to a JSON object, get the object we want, then convert it to a Data object and decode. 解决方案A-将数据转换为JSON对象,获取所需的对象,然后将其转换为Data对象并进行解码。

let jsonFull = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any]
let json = jsonFull["data"]
let data_error = try? JSONSerialization.data(withJSONObject: json, options: [])
let e = try? JSONDecoder().decode(APIError.self, from: data_error)

Solution B - Wrap the target item in another struct 解决方案B-将目标项目包装在另一个结构中

struct temp : Decodable {
    let status: String?
    let data: APIError?
}

let e = try? JSONDecoder().decode(temp.self, from: data).data

Solution C - Set the nested structure in decode (what if it is several objects deep?) 解决方案C-在解码中设置嵌套结构(如果深度为几个对象该怎么办?)

let e = try? JSONDecoder().decode([Any, APIError.self], from: data)

What patterns am I missing? 我缺少什么模式? What's the most elegant way to do this? 最优雅的方法是什么?

You can use the following approach: 您可以使用以下方法:

struct APIError: Decodable {
    let code: Int
    let message: String

    enum CodingKeys: String, CodingKey {
        case data
    }

    enum ErrorCodingKeys: String, CodingKey {
        case code = "errorcode"
        case message = "errormessage"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let nestedContainer = try container.nestedContainer(keyedBy: ErrorCodingKeys.self, forKey: .data)

        code = try nestedContainer.decode(Int.self, forKey: .code)
        message = try nestedContainer.decode(String.self, forKey: .message)
    }
}

let data = try! JSONSerialization.data(withJSONObject: ["status": "error", "data": ["errorcode": 14, "errormessage": "Could not get user with ID: -1."]], options: [])
let error = try! JSONDecoder().decode(APIError.self, from: data)

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

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