首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Swift 4中的JSONEncoder将[String: Encodable]字典编码为JSON

使用Swift 4中的JSONEncoder将[String: Encodable]字典编码为JSON
EN

Stack Overflow用户
提问于 2018-07-21 00:58:15
回答 2查看 3.5K关注 0票数 7

我只是好奇如何将带有JSON键和JSON值的字典编码成String

例如:

代码语言:javascript
复制
let dict: [String: Encodable] = [
    "Int": 1,
    "Double": 3.14,
    "Bool": false,
    "String": "test"
]

dict中的键都是String类型,但值的类型有所不同。

但是,在JSON中允许所有这些类型。

我想知道是否有一种方法可以在Swift 4中使用JSONEncoder将这个dict编码成JSON Data

我确实理解在不使用JSONEncoder的情况下还有其他方法来实现这一点,但我只是想知道JSONEncoder是否有能力做到这一点。

Dictionary在扩展中确实有一个func encode(to encoder: Encoder) throws,但这只适用于constraint Key: Encodable, Key: Hashable, Value: Encodable,而对于我们的dict,它需要constraint Key: Encodable, Key: Hashable, Value == Encodable

拥有用于此的struct将足以使用JSONEncoder

代码语言:javascript
复制
struct Test: Encodable {
    let int = 1
    let double = 3.14
    let bool = false
    let string = "test"
}

但是,我很想知道是否可以不指定具体的类型而只指定Encodable协议。

EN

回答 2

Stack Overflow用户

发布于 2018-07-21 01:44:17

我刚刚想出了一种用包装器来实现这一点的方法:

代码语言:javascript
复制
struct EncodableWrapper: Encodable {
    let wrapped: Encodable

    func encode(to encoder: Encoder) throws {
        try self.wrapped.encode(to: encoder)
    }
}

let dict: [String: Encodable] = [
    "Int": 1,
    "Double": 3.14,
    "Bool": false,
    "String": "test"
]
let wrappedDict = dict.mapValues(EncodableWrapper.init(wrapped:))
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
let jsonData = try! jsonEncoder.encode(wrappedDict)
let json = String(decoding: jsonData, as: UTF8.self)
print(json)

结果如下:

{ "Double“:3.1400000000000001,"String”:“测试”,"Bool“:false,"Int”:1}

我对此仍然不满意。如果还有其他方法,我非常乐意看到。

谢谢!

编辑1将包装器移动到JSONEncoder的扩展中

代码语言:javascript
复制
extension JSONEncoder {
    private struct EncodableWrapper: Encodable {
        let wrapped: Encodable

        func encode(to encoder: Encoder) throws {
            try self.wrapped.encode(to: encoder)
        }
    }
    func encode<Key: Encodable>(_ dictionary: [Key: Encodable]) throws -> Data {
        let wrappedDict = dictionary.mapValues(EncodableWrapper.init(wrapped:))
        return try self.encode(wrappedDict)
    }
}

let dict: [String: Encodable] = [
    "Int": 1,
    "Double": 3.14,
    "Bool": false,
    "String": "test"
]

let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
let jsonData = try! jsonEncoder.encode(dict)
let json = String(decoding: jsonData, as: UTF8.self)
print(json)

结果:

{ "Int“:1,"Double”:3.1400000000000001,"Bool“:false,"String”:“测试”}

编辑2:根据@Hamish的评论,考虑定制的策略

代码语言:javascript
复制
private extension Encodable {
    func encode(to container: inout SingleValueEncodingContainer) throws {
        try container.encode(self)
    }
}

extension JSONEncoder {
    private struct EncodableWrapper: Encodable {
        let wrapped: Encodable

        func encode(to encoder: Encoder) throws {
            var container = encoder.singleValueContainer()
            try self.wrapped.encode(to: &container)
        }
    }

    func encode<Key: Encodable>(_ dictionary: [Key: Encodable]) throws -> Data {
        let wrappedDict = dictionary.mapValues(EncodableWrapper.init(wrapped:))
        return try self.encode(wrappedDict)
    }
}
票数 14
EN

Stack Overflow用户

发布于 2018-07-21 03:08:12

您将需要一个包装器,因为使用Encodable协议才能知道哪一项更容易编码。

我建议使用一个名为JSONValue的枚举,它对所有IntStringDoubleArrayDictionary都有5到6个案例。然后,您可以以一种类型安全的方式编写JSON。

这个link也会有帮助。

我是这样使用它的:

代码语言:javascript
复制
indirect enum JSONValue {
    case string(String)
    case int(Int)
    case double(Double)
    case bool(Bool)
    case object([String: JSONValue])
    case array([JSONValue])
    case encoded(Encodable)
}

然后制作JSONValue: Encodable并为每种情况编写编码代码。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51447254

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档