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)
}
这里有很多很好的方法,但我还没有看到一个讨论枚举有一个以上的值,尽管它可以从例子中推导出来——也许有人可以找到这个方法的用途:
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)")
}
这很简单,只需使用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)
}
}
这允许编译器详尽地验证这些情况,并且在编码值与键的期望值不匹配的情况下也不会抑制错误消息。
如果遇到未知的enum值,Swift会抛出一个. datacorurt错误。如果你的数据来自服务器,它可以在任何时候给你一个未知的枚举值(错误服务器端,在API版本中添加的新类型,你希望你的应用程序的前版本能优雅地处理这种情况,等等),你最好做好准备,并编码“防御性风格”来安全地解码你的枚举。
这里有一个关于如何使用或不使用相关值的示例
enum MediaType: Decodable {
case audio
case multipleChoice
case other
// case other(String) -> we could also parametrise the enum like that
init(from decoder: Decoder) throws {
let label = try decoder.singleValueContainer().decode(String.self)
switch label {
case "AUDIO": self = .audio
case "MULTIPLE_CHOICES": self = .multipleChoice
default: self = .other
// default: self = .other(label)
}
}
}
以及如何在封闭结构中使用它:
struct Question {
[...]
let type: MediaType
enum CodingKeys: String, CodingKey {
[...]
case type = "type"
}
extension Question: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
[...]
type = try container.decode(MediaType.self, forKey: .type)
}
}
为了扩展@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编译和测试代码。