首页 > 解决方案 > Swift Codable - 如何编码和解码字符串化的 JSON 值?

问题描述

我正在与之交谈的服务器需要以下格式的消息:

{
  "command": "subscribe",
  "identifier": "{\"channel\": \"UserChannel\"}",
  "data": "{\"key\": \"value\"}"
}

其中identifierdata值是转义的 json 字符串。

到目前为止我有这个:

struct ActionCableMessage<Message: Encodable>: Encodable {
    let command: Command
    let identifier: CableChannel
    let data: Message?

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(command, forKey: .command)

        try container.encode(identifier, forKey: .identifier) // ????
    }

    private enum CodingKeys: String, CodingKey {
        case command, identifier, data
    }
}

但我不知道从这里做什么。我认为我需要一个protocolthatCableChannel并且Message可以符合提供extension的实现 func的函数encode (to encoder: Encoder),它确保Encoder必须是 a JSONEncoder,如果是这样,则使用它将它自己的值重写为转义的 json 字符串。

我还需要将其解码回ActionCableMessage结构,但我还没有走那么远。

标签: jsonswiftcodable

解决方案


我想我需要一个 CableChannel 和 Message 可以遵守的协议

好吧,该协议是Encodable(或者Codable如果您愿意)。

// just implement these as you normally would
extension CableChannel : Encodable { ... }
extension Message : Encodable { ... }

然后在 中ActionCableMessage,您使用另一个编码器将内部对象编码为 JSON 数据,然后将其转换为字符串,然后对该字符串进行编码:

func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(command, forKey: .command)

    let subencoder = JSONEncoder()
    let identifierString = try String(data: subencoder.encode(identifier), encoding: .utf8)
    
    try container.encode(identifierString, forKey: .identifier)

    // do the same for "data"
}

类似的解码:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    command = try container.decode(String.self, forKey: .command)
    let identifierJSONString = try container.decode(String.self, forKey: .identifier)
    // do the same for "data"
    
    let subdecoder = JSONDecoder()
    identifier = try subdecoder.decode(CableChannel.self, from: identifierJSONString.data(using: .utf8)!)
    // do the same for "data"
}

推荐阅读