如何解码类型取决于标签的值数组? [英] How to decode an array of values whose types depend on a tag?

查看:43
本文介绍了如何解码类型取决于标签的值数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有值数组的JSON:

I have a JSON with an array of values:

[
    { "tag": "Foo", … },
    { "tag": "Bar", … },
    { "tag": "Baz", … },
]

我想将此数组解码为 struct s的数组,具体类型取决于在标签上:

I want to decode this array into an array of structs where the particular type depends on the tag:

protocol SomeCommonType {}

struct Foo: Decodable, SomeCommonType { … }
struct Bar: Decodable, SomeCommonType { … }
struct Baz: Decodable, SomeCommonType { … }

let values = try JSONDecoder().decode([SomeCommonType].self, from: …)

我该怎么做?目前,我有一个稍微难看的包装器:

How do I do that? At the moment I have this slightly ugly wrapper:

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

然后:

let wrapped = try JSONDecoder().decode([DecodingWrapper].self, from: …)
let values = wrapped.map { $0.value }

有更好的方法吗?

推荐答案

您的数组包含异构对象数量有限的品种;听起来像是Swift枚举的完美用例。它不适合多态,因为从概念上讲,这些事物不一定是同一种。他们只是碰巧被标记了。

Your array contains heterogeneous objects of finite, enumerable varieties; sounds like a perfect use case for Swift enums. It's not suitable for polymorphism, because these "things" are not necessarily of the same kind, conceptually speaking. They just happen to be tagged.

这样看:您拥有一堆东西,都带有标签,有些是这种标签,有些是完全不同的标签,还有一些。 ..有时您甚至无法识别标签。 Swift枚举是捕捉此想法的理想工具。

Look at it this way: you have an array of things that all have tags, some are of this kind, others are of a completely different kind, and still others ... and sometimes you don't even recognize the tag. A Swift enum is the perfect vehicle to capture this idea.

因此,您有一堆共享标签属性但彼此完全不同的结构:

So you have a bunch of structs that shares a tag property but otherwise completely different from each other:

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
}

您的数组可以包含任何上述类型或未知类型的实例。因此,您有了枚举 TagggedThing (或更好的名称)。

And your array could contain any instance of the above types, or of an unknown type. So you have the enum TagggedThing (or a better name).

enum TagggedThing {
    case foo(Foo)
    case bar(Bar)
    case baz(Baz)
    case unknown
}

用Swift术语来说,您的数组的类型为 [TagggedThing] 。因此,您将 TagggedThing 类型与可解码的类型保持一致,像这样:

Your array, in Swift terms, is of type [TagggedThing]. So you conform the TagggedThing type to Decodable like this:

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

现在可以解码以下JSON:

Now you can decode the following JSON:

let json: Data! = """
[
    {"tag": "foo", "fooValue": 1},
    {"tag": "bar", "barValue": 2},
    {"tag": "baz", "bazValue": 3}
]
""".data(using: .utf8)

像这样:

let taggedThings = try? JSONDecoder().decode([TagggedThing].self, from: json)

这篇关于如何解码类型取决于标签的值数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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