如何在Swift中表示通用的JSON结构? [英] How to represent a generic JSON structure in Swift?

查看:120
本文介绍了如何在Swift中表示通用的JSON结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Swift中表示一个通用的JSON对象:

I would like to represent a generic JSON object in Swift:

let foo: [String: Any] = [
    "foo": 1,
    "bar": "baz",
]

但是编译器建议的[String: Any]类型不能很好地工作.例如,我无法检查该类型的两个实例是否相等,而使用两个JSON树应该可以做到这一点.

But the [String: Any] type suggested by the compiler doesn’t really work well. I can’t check two instances of the type for equality, for example, while that should be possible with two JSON trees.

使用Codable机制将值编码为JSON字符串也无效:

What also doesn’t work is using the Codable machinery to encode that value into a JSON string:

let encoded = try JSONEncoder().encode(foo)

哪个会因错误而崩溃:

fatal error: Dictionary<String, Any> does not conform to Encodable because Any does not conform to Encodable.

我知道我可以介绍一个精确的类型,但是我追求通用的JSON结构.我什至尝试为通用JSON引入特定类型:

I know I can introduce a precise type, but I am after a generic JSON structure. I even tried to introduce a specific type for generic JSON:

enum JSON {
    case string(String)
    case number(Float)
    case object([String:JSON])
    case array([JSON])
    case bool(Bool)
    case null
}

但是在为此枚举实现Codable时,我不知道如何实现encode(to:),因为(用于编码对象的)键控容器需要特定的CodingKey参数,而且我不知道该如何获取该参数

But when implementing Codable for this enum I don’t know how to implement encode(to:), since a keyed container (for encoding objects) requires a particular CodingKey argument and I don’t know how to get that.

真的不可能创建Equatable通用JSON树并使用Codable对其进行编码吗?

Is it really not possible to create an Equatable generic JSON tree and encode it using Codable?

推荐答案

我们将使用通用字符串作为编码键:

We will use generic strings as coding keys:

extension String: CodingKey {
    public init?(stringValue: String) {
        self = stringValue
    }

    public var stringValue: String {
        return self
    }

    public init?(intValue: Int) {
        return nil
    }

    public var intValue: Int? {
        return nil
    }
}

剩下的实际上只是获取正确类型的容器并将其值写入其中的问题.

The rest really is just a matter of getting the correct type of container and writing your values to it.

extension JSON: Encodable {
    public func encode(to encoder: Encoder) throws {
        switch self {
        case .string(let string):
            var container = encoder.singleValueContainer()
            try container.encode(string)
        case .number(let number):
            var container = encoder.singleValueContainer()
            try container.encode(number)
        case .object(let object):
            var container = encoder.container(keyedBy: String.self)

            for (key, value) in object {
                try container.encode(value, forKey: key)
            }
        case .array(let array):
            var container = encoder.unkeyedContainer()

            for value in array {
                try container.encode(value)
            }
        case .bool(let bool):
            var container = encoder.singleValueContainer()
            try container.encode(bool)
        case .null:
            var container = encoder.singleValueContainer()
            try container.encodeNil()
        }
    }
}

鉴于此,我确定您可以自己实现DecodableEquatable.

Given this, I'm sure you can implement Decodable and Equatable yourself.

请注意,如果您尝试将数组或对象以外的任何内容编码为顶级元素,则会崩溃.

Note that this will crash if you try to encode anything other than an array or an object as a top-level element.

这篇关于如何在Swift中表示通用的JSON结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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