【问题标题】:How to implement a context controllable Swift Codable Encode function?如何实现上下文可控的 Swift Codable Encode 功能?
【发布时间】:2019-02-24 16:17:04
【问题描述】:

我正在尝试为 Codable 结构实现编码器,我可以通过传递一个上下文来控制它,该上下文确定我将添加到输出 JSON 中的字段。但我想不出一个好的方法。

例如说我有以下内容:

import Foundation

struct MyStruct: Encodable {
    var a:Int = 0
    var b:Int = 0

    enum CodingKeys: String, CodingKey {
        case a
        case b
    }

    enum Context {
        case summary, full
    }

    // I can write something like this
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(a, forKey: .a)
        try container.encode(b, forKey: .b)
    }

    // ...but I want to write is something like this
    func encode(to encoder: Encoder, withContextcontext: Context ) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(a, forKey: .a)

        if context == .full
        {
            try container.encode(b, forKey: .b)
        }
    }

}

var myObject = MyStruct()
myObject.a = 10
myObject.b = 5

let jsonEncoder = JSONEncoder()
do {
    let jsonData = try jsonEncoder.encode(myObject)
    // let jsonData = try jsonEncode.encode(myObject, withContext:.full)
    let jsonString = String(data: jsonData, encoding: .utf8)!
    print(jsonString)
} catch {
    print(error)
}

有没有什么好的解决方案可以产生与我想象中的 encode(:WithContext) 功能相同的效果?

我有一个我不太喜欢的解决方案是将上下文变量添加到结构中并在调用 encode() 之前对其进行设置:

struct MyStruct: Encodable {
    var a:Int = 0
    var b:Int = 0
    var context = .full

    enum CodingKeys: String, CodingKey {
        case a
        case b
    }

    enum Context {
        case summary, full
    }



    // But I want to write is something like this
    func encode(to encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(a, forKey: .a)

        if context == .full
        {
            try container.encode(b, forKey: .b)
        }
    }

}

var myObject = MyStruct()
myObject.a = 10
myObject.b = 5
myObject.context = .summary

let jsonEncoder = JSONEncoder()
do {
    let jsonData = try jsonEncoder.encode(myObject)
    let jsonString = String(data: jsonData, encoding: .utf8)!
    print(jsonString)
} catch {
    print(error)
}

不确定我是否要将此 Context 变量添加到我拥有的每个结构中。还有更优雅的方式吗?

【问题讨论】:

  • 为什么你写的不是一个“好的解决方案”?换句话说:为什么你写的是“虚构的”?编写自己的encode(to) 的全部意义在于,您可以为所欲为。你有什么实际问题?
  • 因为 swift 期望 encode 只有一个参数(对编码器:编码器)?我不确定如何传入上下文变量。
  • 哦!我懂了。好吧,没有必要“传入”上下文;上下文只需要生活在足够高的范围内,encode(to:) 可以在调用它时看到它。它确实如此。所以我再次问,有什么问题?基本上你的(新)代码编译得很好。
  • 顺便说一句,使用类和继承处理类似问题也很常见(我并不是说这是最好的解决方案)。

标签: swift codable


【解决方案1】:

如果您需要将一些上下文传递给Encoder,您可以随时使用Encoder.userInfo

用户为编码设置的任何上下文信息。

例子:

struct MyStruct: Encodable {
    var a:Int = 0
    var b:Int = 0

    enum CodingKeys: String, CodingKey {
        case a
        case b
    }

    enum Context {
        case summary, full

        static let encodingKey = CodingUserInfoKey(rawValue: "my_struct_context")!
    }

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

        if (encoder.userInfo[Context.encodingKey] as? Context) == .full {
            try container.encode(b, forKey: .b)
        }
    }
}

var myObject = MyStruct()
myObject.a = 10
myObject.b = 5

let jsonEncoder = JSONEncoder()
jsonEncoder.userInfo = [
    MyStruct.Context.encodingKey: MyStruct.Context.summary
]

do {
    let jsonData = try jsonEncoder.encode(myObject)
    let jsonString = String(data: jsonData, encoding: .utf8)!
    print(jsonString)
} catch {
    print(error)
}

【讨论】:

  • 酷,我都忘记了! (如果我什至知道它!!!)不过,在这种特殊情况下,我不明白 OP 的难度应该是什么;上下文在范围内。
  • 寻找更好的解决方案并不困难——如果它们存在的话。我不想在我的所有课程中添加上下文变量,只是想问问社区是否有我不知道的更好的方法——比如建议的。
  • @PhilipPegden 酷,如果这是您正在寻找的内容,您应该接受这个答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-21
  • 2021-08-29
  • 1970-01-01
相关资源
最近更新 更多