您如何通过其案例名称而不是其原始值使枚举可解码? 编辑编辑2

如果我有这样的枚举:

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

如何使用案例名称(Decodablecase1)而不是原始值使其符合case2?例如,我将能够像这样使用它:

let data = Data("\"case1\"".utf8)
let decodedEnum = try! JSONDecoder().decode(SomeEnum.self,from: data) // SomeEnum.case1

编辑

我像@Alexander所说的那样将其添加到SomeEnum

enum CodingKeys: String,CodingKey {
  case case1,case2
}

但我仍然遇到错误

  

由于数据格式不正确,因此无法读取。


编辑2

我尝试像@Lutz所说的那样在CodingKeys中显式定义原始值,但是遇到了同样的错误。万一JSONDecoder不允许使用分段的JSON,我尝试使用SomeEnum s数组(#"["case1","case2"]"#,这也行不通。

lty83938477 回答:您如何通过其案例名称而不是其原始值使枚举可解码? 编辑编辑2

我调查了一下,这里的问题是,您在JSON结果中看到的是编码的 value ,而不是 key 。因此,添加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

本文链接:https://www.f2er.com/3162851.html

大家都在问