如何在Swift中解析具有类型相关子对象的JSON对象?
我有以下JSON对象:如何在Swift中解析具有类型相关子对象的JSON对象?,json,swift,swift4,Json,Swift,Swift4,我有以下JSON对象: [{ "type": "foo", "props": { "word": "hello" } }, { "type": "bar", "props": { "number": 42 } }] 根据type中存储的类型,props中的对象具有不同的键。所以,我尝试了一些逻辑 struct MyObject : Codable { struct FooProps { let word:
[{
"type": "foo",
"props": {
"word": "hello"
}
}, {
"type": "bar",
"props": {
"number": 42
}
}]
根据type
中存储的类型,props
中的对象具有不同的键。所以,我尝试了一些逻辑
struct MyObject : Codable {
struct FooProps { let word: String }
struct BarProps { var number: Int }
enum PropTypes { case FooProps, BarProps }
let type: String
let props: PropTypes?
enum CodingKeys : CodingKey {
case type, props
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
type = try values.decode(String.self, forKey: .type)
switch type {
case "foo":
props = try values.decode(FooProps.self, forKey: .props)
case "bar":
props = try values.decode(BarProps.self, forKey: .props)
default:
props = nil
}
}
}
但是没有运气
error: jsontest.playground:10:8: error: type 'MyObject' does not conform to protocol 'Encodable'
struct MyObject : Codable {
^
jsontest.playground:16:9: note: cannot automatically synthesize 'Encodable' because 'MyObject.PropTypes?' does not conform to 'Encodable'
let props: PropTypes?
^
error: jsontest.playground:27:39: error: cannot convert value of type 'MyObject.FooProps.Type' to expected argument type 'MyObject.PropTypes?.Type'
props = try values.decode(FooProps.self, forKey: .props)
^~~~~~~~
error: jsontest.playground:29:39: error: cannot convert value of type 'MyObject.BarProps.Type' to expected argument type 'MyObject.PropTypes?.Type'
props = try values.decode(BarProps.self, forKey: .props)
^~~~~~~~
然后,我想一些课堂魔术也许可以
class PropTypes : Codable { }
class FooProps : PropTypes { var word: String = "Default String" }
class BarProps : PropTypes { var number: Int = -1 }
class MyObject : Codable {
let type: String
var props: PropTypes?
enum CodingKeys : CodingKey {
case type, props
}
...
但是当我对解析结果进行dump
时,我只得到默认值
▿ 2 elements
▿ __lldb_expr_32.MyObject #0
- type: "foo"
▿ props: Optional(__lldb_expr_32.FooProps)
▿ some: __lldb_expr_32.FooProps #1
- super: __lldb_expr_32.PropTypes
- word: "Default String"
▿ __lldb_expr_32.MyObject #2
- type: "bar"
▿ props: Optional(__lldb_expr_32.BarProps)
▿ some: __lldb_expr_32.BarProps #3
- super: __lldb_expr_32.PropTypes
- number: -1
我的问题是:我错过了什么?这能做到吗
编辑根据凯文·巴拉德的建议,我发现以下错误:
error: jsontest.playground:15:37: error: no 'decode' candidates produce the expected contextual result type 'MyObject.FooProps'
props = try .foo(values.decode(FooProps.self, forKey: .props))
^
jsontest.playground:15:37: note: overloads for 'decode' exist with these result types: Bool, Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float, Double, String, T
props = try .foo(values.decode(FooProps.self, forKey: .props))
^
error: jsontest.playground:17:37: error: no 'decode' candidates produce the expected contextual result type 'MyObject.BarProps'
props = try .bar(values.decode(BarProps.self, forKey: .props))
^
jsontest.playground:17:37: note: overloads for 'decode' exist with these result types: Bool, Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float, Double, String, T
props = try .bar(values.decode(BarProps.self, forKey: .props))
^
查看您最初列出的错误,有两个不同的问题
Codable
,但错误告诉您它无法自动合成Encodable
。你的问题不是关于编码,而是关于解码,所以对于这一点,我要说的是,只需遵循Decodable
,而不是Codable
(或者自己实现编码)props
属于类型PropTypes?
,其中PropTypes
是一个枚举。您正在解码FooProps
或BarProps
,并将结果填充到props
中。您需要将结果包装到枚举中。此外,您的枚举定义错误,您有名为FooProps
和BarProps
的案例,它们不带值。它应该重新定义为{case foo(FooProps),bar(BarPros)}
struct MyObject : Decodable {
struct FooProps : Decodable { let word: String }
struct BarProps : Decodable { var number: Int }
enum PropTypes { case foo(FooProps), bar(BarProps) }
let type: String
let props: PropTypes?
enum CodingKeys : CodingKey {
case type, props
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
type = try values.decode(String.self, forKey: .type)
switch type {
case "foo":
props = try .foo(values.decode(FooProps.self, forKey: .props))
case "bar":
props = try .bar(values.decode(BarProps.self, forKey: .props))
default:
props = nil
}
}
}
另外,您的
type
属性现在是多余的,因为您可以使用props
属性来确定类型。1。谢谢,我对问题进行了编辑,以包含我收到的与您建议的更改有关的错误消息。2.是的,但我还是明白了,所以还是用它吧:)@Morpheu5啊,对,FooProps
和BarProps
不符合Decodable
。你需要更新这些。您可能只需要声明一致性并让它自动合成。我将更新我的代码片段来实现这一点。关于type
,保留它的问题是,如果type
和props
键对对象的实际类型不一致,则可以构造值,例如MyObject(type:“error”,props:.foo(FooProps(word:“hello”))
。事实上,但我所处理的实际数据来自一个自动生成的日志表,几乎从不更改——当它更改时,它会进行版本控制。