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



我只是想知道如何将具有String键和Encodable值的字典编码为JSON。

例如:

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

dict中的键都属于String类型,但值的类型各不相同。

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

我想知道是否有一种方法可以在Swift 4中使用JSONEncoderdict编码为JSONData

我知道还有其他方法不使用JSONEncoder来实现这一点,但我只是想知道JSONEncoder是否能够管理这一点。

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

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

struct Test: Encodable {
let int = 1
let double = 3.14
let bool = false
let string = "test"
}

然而,我很想知道是否可以在不指定具体类型而只指定Encodable协议的情况下完成。

刚刚找到了一种用包装器实现这一点的方法:

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)

结果是:

{"双":3.1400000000000001,"字符串":"测试","Bool":错误,"Int":1}

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

谢谢!

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

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,"双":3.1400000000000001,"Bool":错误,"字符串":"测试"}

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

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

您需要一个包装器,因为使用Encodable协议可以知道哪一项是哪一项,从而更容易地对其进行编码。

我建议使用一个名为JSONValue的枚举,它对所有IntStringDoubleArrayDictionary的情况都有5到6个情况。那么就可以用类型安全的方式编写JSON。

这个链接也会有所帮助。

这就是我使用它的方式:

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,并为每种情况编写编码代码。

相关内容

  • 没有找到相关文章

最新更新