如何在Swift中解析具有与类型相关的子对象的JSON对象? [英] How do I parse a JSON object that has type-dependent sub-objects, in Swift?

查看:83
本文介绍了如何在Swift中解析具有与类型相关的子对象的JSON对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下JSON对象:

I have the following JSON object:

[{
    "type": "foo",
    "props": {
        "word": "hello"
    }
}, {
    "type": "bar",
    "props": {
        "number": 42
    }
}]

根据type中存储的类型,props中的对象具有不同的键.所以,我尝试了一些逻辑

Depending on the type stored in type, the object in props has different keys. So, I tried with some logic

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)
                                      ^~~~~~~~

然后,我认为某些类魔法可能会做到

Then, I thought some class magic would probably do

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解析结果时,我只会得到默认值

but when I do dump the result of the parsing, I only get the default values

▿ 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

我的问题是:我想念什么?可以做到吗?

My question is: what am I missing? Can this be done at all?

编辑:按照凯文·巴拉德(Kevin Ballard)的建议,出现以下错误:

EDIT Following Kevin Ballard's suggestion, I get the following errors:

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))
                                    ^

推荐答案

看看您原来列出的错误,有两个不同的问题.

Looking at your original listed errors, there are two distinct issues.

  1. 您声明符合Codable,但是错误告诉您它不能自动合成Encodable.您的问题不是关于编码,而是关于解码,因此,我想说的是,遵循Decodable而不是Codable(或自己实现编码).
  2. props的类型为PropTypes?,其中PropTypes是枚举.您正在解码FooPropsBarProps,然后将结果填充到props中.您需要将结果包装在枚举中.同样,您的枚举定义为错误,您有名为FooPropsBarProps的个案,它们不带有值.应该像{ case foo(FooProps), bar(BarPros) }这样重新定义它.
  1. You declared conformance to Codable, but the errors are telling you it cannot automatically synthesize Encodable. Your question isn't about encoding, but decoding, so for this I'd say just conform to Decodable instead of Codable (or implement encoding yourself).
  2. props is of type PropTypes?, where PropTypes is an enum. You're decoding either FooProps or BarProps, and stuffing the result into props. You need to wrap the result in the enum instead. Also your enum is defined wrong, you have cases named FooProps and BarProps, which don't carry values. It should be redefined like { case foo(FooProps), bar(BarPros) } instead.

总的来说,这看起来像

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
        }
    }
}

这篇关于如何在Swift中解析具有与类型相关的子对象的JSON对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆