【问题标题】:Encode dictionary without adding the coding key enum in Swift编码字典而不在 Swift 中添加编码键枚举
【发布时间】:2022-01-09 18:23:43
【问题描述】:

我想编码一个可能是

的 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 并且无法弄清楚如何在我的用例中应用

【问题讨论】:

  • 你也需要解码吗?
  • 是的,类似的请求需要被解码。可能是 {"seq":1,"hw1":{"get_trouble":{"troubles":["alarm"],"error_code":0}}} {"seq":1,"hw2":{ "get_trouble":{"trouble":["bus"],"error_code":0}}}
  • 您评论中的 json 看起来甚至与您代码中的类型不一样。
  • 抱歉,我无法正确格式化。评论中的 json 是我稍后需要解码的响应。目前我正在尝试对需要发送到服务器的请求进行编码,并且我将采用相同的方法来解码响应

标签: swift encoding codable encodable


【解决方案1】:

与几乎所有自定义编码问题一样,您需要的工具是 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 有一个键,那么您可以更自然地重写它:

// 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"))
    }
}

【讨论】:

    猜你喜欢
    • 2019-01-06
    • 1970-01-01
    • 2015-02-24
    • 1970-01-01
    • 1970-01-01
    • 2013-08-24
    • 2014-12-13
    • 1970-01-01
    • 2023-03-26
    相关资源
    最近更新 更多