swift - 在 Swift 中编码字典而不添加编码键枚举

标签 swift encoding codable encodable

我想编码一个 JSON,可以是

{"hw1":{"get_trouble":true},"seq":2,"session_id":1}


{"hw2":{"get_trouble":true},"seq":3,"session_id":2}

编码类如下所示

class Request: Codable {
    let sessionId, seq:Int
    let content:[String:Content]
    
    enum CodingKeys:String, CodingKey{
        case sessionId = "session_id"
        case seq
        case content
    }
    
    init(sessionId:Int, seq:Int, content:[String:Content]) {
        self.sessionId = sessionId
        self.seq = seq
        self.content = content
    }
}

class Content:Codable{
    let getTrouble = true
    
    enum CodingKeys:String, CodingKey {
        case getTrouble = "get_trouble"
    }
}

如何对请求进行编码才能获得所需的结果?目前,如果我这样做

let request = Request(sessionId: session, seq: seq, content: [type:content])
let jsonData = try! encoder.encode(request)

我明白了

{"content":{"hw1":{"get_trouble":true}},"seq":2,"session_id":1}

并且我不想在 JSON 中包含“内容”。已经研究过
Swift Codable: encode structure with dynamic keys 并且不知道如何应用于我的用例

最佳答案

与几乎所有自定义编码问题一样,您需要的工具是 AnyStringKey(令我沮丧的是,它不在 stdlib 中):

struct AnyStringKey: CodingKey, Hashable, ExpressibleByStringLiteral {
    var stringValue: String
    init(stringValue: String) { self.stringValue = stringValue }
    init(_ stringValue: String) { self.init(stringValue: stringValue) }
    var intValue: Int?
    init?(intValue: Int) { return nil }
    init(stringLiteral value: String) { self.init(value) }
}

这只是让你可以对任意键进行编码和编码。这样,编码器就很简单了:

func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: AnyStringKey.self)
    for (key, value) in content {
        try container.encode(value, forKey: AnyStringKey(key))
    }
    try container.encode(sessionId, forKey: AnyStringKey("session_id"))
    try container.encode(seq, forKey: AnyStringKey("seq"))
}

这假设您的意思是在内容中允许多个键/值对。我希望你不会;您只是使用字典,因为您想要一种更好的编码方式。如果 Content 有一个键,那么您可以这样更自然地重写它:

// Content only encodes getTrouble; it doesn't encode key
struct Content:Codable{
    let key: String
    let getTrouble: Bool

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(["get_trouble": getTrouble])
    }
}

struct Request: Codable {
    // ...

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: AnyStringKey.self)
        try container.encode(content, forKey: AnyStringKey(content.key))
        try container.encode(sessionId, forKey: AnyStringKey("session_id"))
        try container.encode(seq, forKey: AnyStringKey("seq"))
    }
}

现在这可能仍然困扰您,因为它将部分内容编码逻辑推送到请求中。 (好吧,也许这只是困扰我。)如果你暂时搁置 Codable 一下,你也可以解决这个问题。

// Encode Content directly into container
extension KeyedEncodingContainer where K == AnyStringKey {
    mutating func encode(_ value: Content) throws {
        try encode(["get_trouble": value.getTrouble], forKey: AnyStringKey(value.key))
    }
}


struct Request: Codable {
    // ...

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: AnyStringKey.self)

        // And encode into the container (note no "forKey")
        try container.encode(content)

        try container.encode(sessionId, forKey: AnyStringKey("session_id"))
        try container.encode(seq, forKey: AnyStringKey("seq"))
    }
}

关于swift - 在 Swift 中编码字典而不添加编码键枚举,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70644235/

相关文章:

vba - VBA IDE 中长字符串的格式

encoding - 在 Hive 中使用冰岛 Thorn 字符作为分隔符

swift - Generic Decodable 停止使用 swift 4.1

ios - 来自另一个目标的 Swift 伞头

ios - 使用 Alamofire 上传图片不成功

swift - 如何快速检查两个实例是否为同一类/类型

Python:字节到带有重音字符的字符串

json - 使用可编码解析 JSON 并忽略 JSON 的第一层

arrays - 如何从 Decodable (Swift) 中的数组访问容器

swift - 使用灵活变量在函数中访问字典(JSON 格式)