enum PostType: Decodable {

    init(from decoder: Decoder) throws {

        // What do i put here?
    }

    case Image
    enum CodingKeys: String, CodingKey {
        case image
    }
}

我要怎么做才能完成这个? 此外,让我们说我把情况改为这样:

case image(value: Int)

我如何使这符合解码?

这是我的完整代码(不工作)

let jsonData = """
{
    "count": 4
}
""".data(using: .utf8)!
        
        do {
            let decoder = JSONDecoder()
            let response = try decoder.decode(PostType.self, from: jsonData)
            
            print(response)
        } catch {
            print(error)
        }
    }
}

enum PostType: Int, Codable {
    case count = 4
}

此外,它将如何处理这样的枚举?

enum PostType: Decodable {
    case count(number: Int)
}

当前回答

实际上,上面的答案真的很好,但它们遗漏了许多人在持续开发客户端/服务器项目中需要的一些细节。我们开发一个应用程序,而我们的后端随着时间不断发展,这意味着一些枚举情况将改变这种发展。因此,我们需要一个枚举解码策略,能够解码包含未知情况的枚举数组。否则,解码包含数组的对象就会失败。

我所做的很简单:

enum Direction: String, Decodable {
    case north, south, east, west
}

struct DirectionList {
   let directions: [Direction]
}

extension DirectionList: Decodable {

    public init(from decoder: Decoder) throws {

        var container = try decoder.unkeyedContainer()

        var directions: [Direction] = []

        while !container.isAtEnd {

            // Here we just decode the string from the JSON which always works as long as the array element is a string
            let rawValue = try container.decode(String.self)

            guard let direction = Direction(rawValue: rawValue) else {
                // Unknown enum value found - ignore, print error to console or log error to analytics service so you'll always know that there are apps out which cannot decode enum cases!
                continue
            }
            // Add all known enum cases to the list of directions
            directions.append(direction)
        }
        self.directions = directions
    }
}

奖励:隐藏实现>使其成为一个集合

隐藏实现细节总是一个好主意。为此,您只需要再编写一点代码。诀窍是使DirectionsList符合Collection,并使你的内部列表数组私有:

struct DirectionList {

    typealias ArrayType = [Direction]

    private let directions: ArrayType
}

extension DirectionList: Collection {

    typealias Index = ArrayType.Index
    typealias Element = ArrayType.Element

    // The upper and lower bounds of the collection, used in iterations
    var startIndex: Index { return directions.startIndex }
    var endIndex: Index { return directions.endIndex }

    // Required subscript, based on a dictionary index
    subscript(index: Index) -> Element {
        get { return directions[index] }
    }

    // Method that returns the next index when iterating
    func index(after i: Index) -> Index {
        return directions.index(after: i)
    }
}

您可以在John Sundell的这篇博客文章中阅读更多关于遵循自定义集合的内容:https://medium.com/@johnsundell/ creating-customization - colls-in swift-a344e25d0bb0

其他回答

这很简单,只需使用String或Int原始值,这些值是隐式分配的。

enum PostType: Int, Codable {
    case image, blob
}

图像编码为0,blob编码为1

Or

enum PostType: String, Codable {
    case image, blob
}

图像被编码为Image而blob被编码为blob


这是一个简单的例子如何使用它:

enum PostType : Int, Codable {
    case count = 4
}

struct Post : Codable {
    var type : PostType
}

let jsonString = "{\"type\": 4}"

let jsonData = Data(jsonString.utf8)

do {
    let decoded = try JSONDecoder().decode(Post.self, from: jsonData)
    print("decoded:", decoded.type)
} catch {
    print(error)
}

更新

在iOS 13.3+和macOS 15.1+中,允许对片段(未包装在集合类型中的单个JSON值)进行解码

let jsonString = "4"

let jsonData = Data(jsonString.utf8)
do {
    let decoded = try JSONDecoder().decode(PostType.self, from: jsonData)
    print("decoded:", decoded) // -> decoded: count
} catch {
    print(error)
}

在Swift 5.5+中,甚至可以在不需要任何额外代码的情况下对枚举进行关联值的en /decode。这些值被映射到一个字典,并且必须为每个关联值指定一个参数标签

enum Rotation: Codable {
    case zAxis(angle: Double, speed: Int)
}

let jsonString = #"{"zAxis":{"angle":90,"speed":5}}"#

let jsonData = Data(jsonString.utf8)
do {
    let decoded = try JSONDecoder().decode(Rotation.self, from: jsonData)
    print("decoded:", decoded)
} catch {
    print(error)
}

@proxpero响应的一个更简洁的变体是将解码器表述为:

public init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    guard let key = values.allKeys.first else { throw err("No valid keys in: \(values)") }
    func dec<T: Decodable>() throws -> T { return try values.decode(T.self, forKey: key) }

    switch key {
    case .count: self = try .count(dec())
    case .title: self = try .title(dec())
    }
}

func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    switch self {
    case .count(let x): try container.encode(x, forKey: .count)
    case .title(let x): try container.encode(x, forKey: .title)
    }
}

这允许编译器详尽地验证这些情况,并且在编码值与键的期望值不匹配的情况下也不会抑制错误消息。

这里有很多很好的方法,但我还没有看到一个讨论枚举有一个以上的值,尽管它可以从例子中推导出来——也许有人可以找到这个方法的用途:

import Foundation

enum Tup {
  case frist(String, next: Int)
  case second(Int, former: String)
  
  enum TupType: String, Codable {
    case first
    case second
  }
  enum CodingKeys: String, CodingKey {
    case type
    
    case first
    case firstNext
    
    case second
    case secondFormer
  }
  
}

extension Tup: Codable {
  init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    let type = try values.decode(TupType.self, forKey: .type)
    switch type {
    case .first:
      let str = try values.decode(String.self, forKey: .first)
      let next = try values.decode(Int.self, forKey: .firstNext)
      self = .frist(str, next: next)
    case .second:
      let int = try values.decode(Int.self, forKey: .second)
      let former = try values.decode(String.self, forKey: .secondFormer)
      self = .second(int, former: former)
    }
  }
  
  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    
    switch self {
    case .frist(let str, next: let next):
      try container.encode(TupType.first, forKey: .type)
      try container.encode(str, forKey: .first)
      try container.encode(next, forKey: .firstNext)
    case .second(let int, former: let former):
      try container.encode(TupType.second, forKey: .type)
      try container.encode(int, forKey: .second)
      try container.encode(former, forKey: .secondFormer)
    }
    
  }
}

let example1 = Tup.frist("123", next: 90)
do {
  let encoded = try JSONEncoder().encode(example1)
  print(encoded)
  
  let decoded = try JSONDecoder().decode(Tup.self, from: encoded)
  print("decoded 1 = \(decoded)")
}
catch {
  print("errpr = \(error.localizedDescription)")
}


let example2 = Tup.second(10, former: "dantheman")

do {
  let encoded = try JSONEncoder().encode(example2)
  print(encoded)
  
  let decoded = try JSONDecoder().decode(Tup.self, from: encoded)
  print("decoded 2 = \(decoded)")
}
catch {
  print("errpr = \(error.localizedDescription)")
}

为了扩展@Toka的回答,你也可以在enum中添加一个原始的可表示值,并使用默认的可选构造函数来构建没有开关的enum:

enum MediaType: String, Decodable {
  case audio = "AUDIO"
  case multipleChoice = "MULTIPLE_CHOICES"
  case other

  init(from decoder: Decoder) throws {
    let label = try decoder.singleValueContainer().decode(String.self)
    self = MediaType(rawValue: label) ?? .other
  }
}

它可以使用一个允许重构构造函数的自定义协议进行扩展:

protocol EnumDecodable: RawRepresentable, Decodable {
  static var defaultDecoderValue: Self { get }
}

extension EnumDecodable where RawValue: Decodable {
  init(from decoder: Decoder) throws {
    let value = try decoder.singleValueContainer().decode(RawValue.self)
    self = Self(rawValue: value) ?? Self.defaultDecoderValue
  }
}

enum MediaType: String, EnumDecodable {
  static let defaultDecoderValue: MediaType = .other

  case audio = "AUDIO"
  case multipleChoices = "MULTIPLE_CHOICES"
  case other
}

如果指定了无效的enum值,也可以轻松地扩展它以抛出错误,而不是默认值。这一变化的要点可以在这里找到:https://gist.github.com/stephanecopin/4283175fabf6f0cdaf87fef2a00c8128。 使用Swift 4.1/Xcode 9.3编译和测试代码。

实际上,上面的答案真的很好,但它们遗漏了许多人在持续开发客户端/服务器项目中需要的一些细节。我们开发一个应用程序,而我们的后端随着时间不断发展,这意味着一些枚举情况将改变这种发展。因此,我们需要一个枚举解码策略,能够解码包含未知情况的枚举数组。否则,解码包含数组的对象就会失败。

我所做的很简单:

enum Direction: String, Decodable {
    case north, south, east, west
}

struct DirectionList {
   let directions: [Direction]
}

extension DirectionList: Decodable {

    public init(from decoder: Decoder) throws {

        var container = try decoder.unkeyedContainer()

        var directions: [Direction] = []

        while !container.isAtEnd {

            // Here we just decode the string from the JSON which always works as long as the array element is a string
            let rawValue = try container.decode(String.self)

            guard let direction = Direction(rawValue: rawValue) else {
                // Unknown enum value found - ignore, print error to console or log error to analytics service so you'll always know that there are apps out which cannot decode enum cases!
                continue
            }
            // Add all known enum cases to the list of directions
            directions.append(direction)
        }
        self.directions = directions
    }
}

奖励:隐藏实现>使其成为一个集合

隐藏实现细节总是一个好主意。为此,您只需要再编写一点代码。诀窍是使DirectionsList符合Collection,并使你的内部列表数组私有:

struct DirectionList {

    typealias ArrayType = [Direction]

    private let directions: ArrayType
}

extension DirectionList: Collection {

    typealias Index = ArrayType.Index
    typealias Element = ArrayType.Element

    // The upper and lower bounds of the collection, used in iterations
    var startIndex: Index { return directions.startIndex }
    var endIndex: Index { return directions.endIndex }

    // Required subscript, based on a dictionary index
    subscript(index: Index) -> Element {
        get { return directions[index] }
    }

    // Method that returns the next index when iterating
    func index(after i: Index) -> Index {
        return directions.index(after: i)
    }
}

您可以在John Sundell的这篇博客文章中阅读更多关于遵循自定义集合的内容:https://medium.com/@johnsundell/ creating-customization - colls-in swift-a344e25d0bb0