如何使枚举可根据其大小写名称而非原始值进行解码



如果我有这样的枚举:

enum SomeEnum: String {
case case1 = "raw value 1"
case case2 = "raw value 2"
}

如何通过使用事例名称(case1case2(而不是原始值使其符合Decodable?例如,我可以这样使用它:

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

我试着像@Lutz所说的那样在CodingKeys中显式定义原始值,但我得到了同样的错误。为了防止JSONDecoder不允许碎片化的JSON,我尝试使用SomeEnums的数组(#"["case1", "case2"]"#,它也不起作用。

我研究过它,这里的问题是您在JSON结果中看到的是一个编码的,而不是。因此,添加CodingKeys没有帮助。

一个稍微复杂的解决方案使用自定义协议和相应的扩展来实现目标。

有了这个,你可以声明:

enum Test: String, CaseNameCodable {
case one = "Number One"
case two = "Number Two"
}

它会满足你的需要。

下面是一个完整的工作示例(适用于Xcode 11.2中的游乐场(:

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

最新更新