【问题标题】:Swift Codable: Use a parent's key as as valueSwift Codable:使用父键作为值
【发布时间】:2020-02-04 13:35:33
【问题描述】:

我有一个在根级别有 ID 的 JSON:

{
    "12345": {
        "name": "Pim"
    },
    "54321": {
        "name": "Dorien"
    }
}

我的目标是使用 Codable 创建一个包含名称和 ID 属性的用户对象数组。

struct User: Codable {
    let id: String
    let name: String
}

我知道如何使用 Codable with a single root level key 并且我知道如何工作 with unknown keys。但我在这里尝试做的是两者的结合,我不知道下一步该做什么。

这是我目前得到的:(您可以将其粘贴到 Playground 中)

import UIKit

var json = """
{
    "12345": {
        "name": "Pim"
    },
    "54321": {
        "name": "Dorien"
    }
}
"""

let data = Data(json.utf8)

struct User: Codable {
    let name: String
}

let decoder = JSONDecoder()
do {
    let decoded = try decoder.decode([String: User].self, from: data)
    decoded.forEach { print($0.key, $0.value) }
    // 54321 User(name: "Dorien")
    // 12345 User(name: "Pim")
} catch {
    print("Failed to decode JSON")
}

这就是我想做的:

let decoder = JSONDecoder()
do {
    let decoded = try decoder.decode([User].self, from: data)
    decoded.forEach { print($0) }
    // User(id: "54321", name: "Dorien")
    // User(id: "12345", name: "Pim")
} catch {
    print("Failed to decode JSON")
}

非常感谢任何帮助。

【问题讨论】:

    标签: swift codable decodable jsondecoder


    【解决方案1】:

    您可以使用自定义编码键并设置用户如下来解析未知键,

    struct CustomCodingKey: CodingKey {
    
        let intValue: Int?
        let stringValue: String
    
        init?(stringValue: String) {
            self.intValue = Int(stringValue)
            self.stringValue = stringValue
        }
    
        init?(intValue: Int) {
            self.intValue = intValue
            self.stringValue = "\(intValue)"
        }
    }
    
    struct UserInfo: Codable {
        let name: String
    }
    
    struct User: Codable {
        var id: String = ""
        var info: UserInfo?
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CustomCodingKey.self)
            if let key = container.allKeys.first {
                self.id = key.stringValue
                self.info = try container.decode(UserInfo.self, forKey: key)
            }
        }
    
        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CustomCodingKey.self)
            if let key = CustomCodingKey(stringValue: self.id) {
                try container.encode(self.info, forKey: key)
            }
        }
    }
    
    let decoder = JSONDecoder()
    do {
        let decoded = try decoder.decode(User.self, from: data)
        print(decoded.id) // 12345
        print(decoded.info!.name) // Pim
    } catch {
        print("Failed to decode JSON")
    }
    

    【讨论】:

    • 别忘了设置自定义编码器。或者使用 Decodable 而不是 Codable。
    • 这很棒。唯一要做的就是在同一个对象中获取所有属性,如下所示:User { let id: String, let name: String }。我相信我会解决这个问题的。
    • 我刚刚意识到这样我得到了一个用户对象,而我期待一组用户。我会更新我的问题以使这一点更清楚。
    • 您的响应结构越来越差。 Id、name 或其他 user 相关属性应该位于作为数组返回的同一对象中。如果这不可能,则当前结构应作为数组发送,否则没有选项将其解码为Dictionary(即[String: UserInfo].self),然后将map每个字典解码为User。
    • 我完全同意你的看法。不幸的是,这是我必须处理的情况......感谢您抽出宝贵时间提出您的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-28
    • 2016-01-09
    • 2019-05-12
    • 2021-05-24
    • 1970-01-01
    相关资源
    最近更新 更多