swift - 你如何让一个枚举可以通过它的案例名称而不是它的原始值来解码?
问题描述
如果我有这样的枚举:
enum SomeEnum: String {
case case1 = "raw value 1"
case case2 = "raw value 2"
}
如何Decodable
通过使用案例名称(case1
和case2
)而不是原始值来使其符合?例如,我可以像这样使用它:
let data = Data("\"case1\"".utf8)
let decodedEnum = try! JSONDecoder().decode(SomeEnum.self, from: data) // SomeEnum.case1
编辑
我添加了这个来SomeEnum
喜欢@Alexander 所说的:
enum CodingKeys: String, CodingKey {
case case1, case2
}
但我仍然收到错误
无法读取数据,因为它的格式不正确。
编辑 2
CodingKeys
我尝试像@Lutz 所说的那样明确定义原始值,但我得到了同样的错误。以防万一JSONDecoder
不允许分段 JSON,我尝试使用SomeEnum
s (数组#"["case1", "case2"]"#
,这也不起作用。
解决方案
我调查了一下,这里的问题是您在 JSON 结果中看到的是一个编码值,而不是一个键。因此,添加CodingKeys
将无济于事。
一个稍微复杂的解决方案使用自定义协议和相应的扩展来实现目标。
有了它,您可以声明:
enum Test: String, CaseNameCodable {
case one = "Number One"
case two = "Number Two"
}
它会做你需要的。
下面概述了一个完整的工作示例(在 Xcode 11.2 的 Playground 中为我工作):
import Foundation
// A custom error type for decoding...
struct CaseNameCodableError: Error {
private let caseName: String
init(_ value: String) {
caseName = value
}
var localizedDescription: String {
#"Unable to create an enum case named "\#(caseName)""#
}
}
//
// This is the interesting part:
//
protocol CaseNameCodable: Codable, RawRepresentable , CaseIterable {}
extension CaseNameCodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let value = try container.decode(String.self)
guard let raw = Self.allCases.first(where: { $0.caseName == value })?.rawValue else { throw CaseNameCodableError(value) }
self.init(rawValue: raw)!
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(caseName)
}
private var caseName: String {
return "\(self)"
}
}
//
// Now you can use the protocol CaseNameCodable just like you
// would use Codable (on RawRepresentable enums only)
//
enum Test: String, CaseNameCodable {
case one = "Number One"
case two = "Number Two"
}
// EXAMPLE:
// Create a test value
let testValue = Test.one
// encode it and convert it to a String
let jsonData = try! JSONEncoder().encode(testValue)
let jsonString = String(data: jsonData, encoding: .utf8)!
print (jsonString) // prints: "one"
// decode the same data to produce a decoded enum instance
let decodedTestValue = try JSONDecoder().decode(Test.self, from: jsonData)
print(decodedTestValue.rawValue) // prints: Number One
推荐阅读
- azure - 如何使用 msal4j 使用令牌进行身份验证?
- java - 如何在 glassfish / payara REST 服务器中注册 jackson-datatype-joda
- c# - 如何在 Application Insights 中为 Web 作业设置警报并在 Web 作业失败时获取通知
- javascript - 在 Angular-JS 中,如何根据所选行中包含的数据显示按钮?
- jira - jira 宏以分层(史诗、故事、任务、子任务)方式表示 jira 报告
- windows - 批处理文件运行时在 Windows 7 上随机更改时间
- google-analytics - Pinterest 显示转换标签有效并显示添加到购物车、结帐的事件计数,但它没有反映在 Google 分析推荐报告中
- python - 使用python将Julian日期转换为正常日期和时间
- python - 我的问题在我的索引页面中消失了(Django polls project)
- jquery - 如何在ckeditor中使用jQuery通过按钮单击粘贴后更改整个文本的格式