zou*_*oul 7 json swift codable
我有一个带有一组值的JSON:
[
{ "tag": "Foo", … },
{ "tag": "Bar", … },
{ "tag": "Baz", … },
]
Run Code Online (Sandbox Code Playgroud)
我想将这个数组解码为一个structs 数组,其中特定类型取决于标记:
protocol SomeCommonType {}
struct Foo: Decodable, SomeCommonType { … }
struct Bar: Decodable, SomeCommonType { … }
struct Baz: Decodable, SomeCommonType { … }
let values = try JSONDecoder().decode([SomeCommonType].self, from: …)
Run Code Online (Sandbox Code Playgroud)
我怎么做?目前我有这个有点丑陋的包装:
struct DecodingWrapper: Decodable {
let value: SomeCommonType
public init(from decoder: Decoder) throws {
let c = try decoder.singleValueContainer()
if let decoded = try? c.decode(Foo.self) {
value = decoded
} else if let decoded = try? c.decode(Bar.self) {
value = decoded
} else if let decoded = try? c.decode(Baz.self) {
value = decoded
} else {
throw …
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后:
let wrapped = try JSONDecoder().decode([DecodingWrapper].self, from: …)
let values = wrapped.map { $0.value }
Run Code Online (Sandbox Code Playgroud)
有没有更好的办法?
您的数组包含有限的、可枚举种类的异构对象;听起来像是 Swift 枚举的完美用例。它不适合多态,因为从概念上讲,这些“东西”不一定是同一类。他们只是碰巧被标记了。
这样看:你有很多东西都有标签,有些是这种类型,有些是完全不同的类型,还有一些......有时你甚至不认识标签。Swift 枚举是捕捉这个想法的完美工具。
所以你有一堆共享一个标签属性但彼此完全不同的结构:
struct Foo: Decodable {
let tag: String
let fooValue: Int
}
struct Bar: Decodable {
let tag: String
let barValue: Int
}
struct Baz: Decodable {
let tag: String
let bazValue: Int
}
Run Code Online (Sandbox Code Playgroud)
您的数组可以包含上述类型或未知类型的任何实例。所以你有枚举TagggedThing(或更好的名字)。
enum TagggedThing {
case foo(Foo)
case bar(Bar)
case baz(Baz)
case unknown
}
Run Code Online (Sandbox Code Playgroud)
用 Swift 术语来说,你的数组是 类型的[TagggedThing]。所以你符合这样的TagggedThing类型Decodable:
extension TagggedThing: Decodable {
private enum CodingKeys: String, CodingKey {
case tag
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let tag = try container.decode(String.self, forKey: .tag)
let singleValueContainer = try decoder.singleValueContainer()
switch tag {
case "foo":
// if it's not a Foo, throw and blame the server guy
self = .foo(try singleValueContainer.decode(Foo.self))
case "bar":
self = .bar(try singleValueContainer.decode(Bar.self))
case "baz":
self = .baz(try singleValueContainer.decode(Baz.self))
default:
// this tag is unknown, or known but we don't care
self = .unknown
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以解码以下 JSON:
let json: Data! = """
[
{"tag": "foo", "fooValue": 1},
{"tag": "bar", "barValue": 2},
{"tag": "baz", "bazValue": 3}
]
""".data(using: .utf8)
Run Code Online (Sandbox Code Playgroud)
像这样:
let taggedThings = try? JSONDecoder().decode([TagggedThing].self, from: json)
Run Code Online (Sandbox Code Playgroud)
可能 enum 可以让你的代码更干净一些。每种情况都对应于您的 json 的类型(标签)。根据情况,您将把 json 解析为适当的模型。无论如何,应该对选择哪种模型进行某种评估。所以我来到了这个
protocol SomeCommonType {}
protocol DecodableCustomType: Decodable, SomeCommonType {}
struct Foo: DecodableCustomType {}
struct Bar: DecodableCustomType {}
struct Baz: DecodableCustomType {}
enum ModelType: String {
case foo
case bar
case baz
var type: DecodableCustomType.Type {
switch self {
case .foo: return Foo.self
case .bar: return Bar.self
case .baz: return Baz.self
}
}
}
func decoder(json: JSON) {
let type = json["type"].stringValue
guard let modelType = ModelType(rawValue: type) else { return }
// here you can use modelType.type
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
734 次 |
| 最近记录: |