Swift 4 JSON 仅在运行时知道类型时解码
Swift 4 JSON decoding when type is only known at runtime
当要解码的类型是 only 时,Swift 4 中的 Decodable
协议是否可以解码 JSON 对象在运行时已知?
我有一个注册表,可以将 String
标识符映射到我们要解码的类型,如下所示:
import Foundation
struct Person: Decodable {
let forename: String
let surname: String
}
struct Company: Decodable {
let officeCount: Int
let people: [Person]
}
let registry: [String:Decodable.Type] = [
"Person": Person.self,
"Company": Company.self
]
let exampleJSON = """
{
"forename": "Bob",
"surname": "Jones"
}
""".data(using: .utf8)!
let t = registry["Person"]!
try! JSONDecoder().decode(t, from: exampleJSON) // doesn't work :-(
我的思路是否正确,或者有更好的方法吗?
您的设计确实独一无二,但不幸的是,我认为您遇到了 Swift 类型系统的边缘情况。基本上,协议不符合自身,因此,您的通用 Decodable.Type
在这里是不够的(即,您 really 需要一个具体类型来满足类型系统要求)。这可能解释了您遇到的错误:
Cannot invoke decode
with an argument list of type (Decodable.Type, from: Data)
. Expected an argument list of type (T.Type, from: Data)
.
但是,话虽如此,这确实有一个(肮脏的!)破解方法。首先,创建一个虚拟 DecodableWrapper
来保存你的运行时 Decodable
类型:
struct DecodableWrapper: Decodable {
static var baseType: Decodable.Type!
var base: Decodable
init(from decoder: Decoder) throws {
self.base = try DecodableWrapper.baseType.init(from: decoder)
}
}
然后像这样使用它:
DecodableWrapper.baseType = registry["Person"]!
let person = try! JSONDecoder().decode(DecodableWrapper.self, from: exampleJSON).base
print("person: \(person)")
打印预期结果:
person: Person(forename: "Bob", surname: "Jones")
Paulo 的 缺点是它不是线程安全的。这是一个更简单的解决方案示例,它允许您在没有可用具体类型的情况下解码值:
struct DecodingHelper: Decodable {
private let decoder: Decoder
init(from decoder: Decoder) throws {
self.decoder = decoder
}
func decode(to type: Decodable.Type) throws -> Decodable {
let decodable = try type.init(from: decoder)
return decodable
}
}
func decodeFrom(_ data: Data, to type: Decodable.Type) throws -> Decodable {
let decodingHelper = try JSONDecoder().decode(DecodingHelper.self, from: data)
let decodable = try decodingHelper.decode(to: type)
return decodable
}
当要解码的类型是 only 时,Swift 4 中的 Decodable
协议是否可以解码 JSON 对象在运行时已知?
我有一个注册表,可以将 String
标识符映射到我们要解码的类型,如下所示:
import Foundation
struct Person: Decodable {
let forename: String
let surname: String
}
struct Company: Decodable {
let officeCount: Int
let people: [Person]
}
let registry: [String:Decodable.Type] = [
"Person": Person.self,
"Company": Company.self
]
let exampleJSON = """
{
"forename": "Bob",
"surname": "Jones"
}
""".data(using: .utf8)!
let t = registry["Person"]!
try! JSONDecoder().decode(t, from: exampleJSON) // doesn't work :-(
我的思路是否正确,或者有更好的方法吗?
您的设计确实独一无二,但不幸的是,我认为您遇到了 Swift 类型系统的边缘情况。基本上,协议不符合自身,因此,您的通用 Decodable.Type
在这里是不够的(即,您 really 需要一个具体类型来满足类型系统要求)。这可能解释了您遇到的错误:
Cannot invoke
decode
with an argument list of type(Decodable.Type, from: Data)
. Expected an argument list of type(T.Type, from: Data)
.
但是,话虽如此,这确实有一个(肮脏的!)破解方法。首先,创建一个虚拟 DecodableWrapper
来保存你的运行时 Decodable
类型:
struct DecodableWrapper: Decodable {
static var baseType: Decodable.Type!
var base: Decodable
init(from decoder: Decoder) throws {
self.base = try DecodableWrapper.baseType.init(from: decoder)
}
}
然后像这样使用它:
DecodableWrapper.baseType = registry["Person"]!
let person = try! JSONDecoder().decode(DecodableWrapper.self, from: exampleJSON).base
print("person: \(person)")
打印预期结果:
person: Person(forename: "Bob", surname: "Jones")
Paulo 的
struct DecodingHelper: Decodable {
private let decoder: Decoder
init(from decoder: Decoder) throws {
self.decoder = decoder
}
func decode(to type: Decodable.Type) throws -> Decodable {
let decodable = try type.init(from: decoder)
return decodable
}
}
func decodeFrom(_ data: Data, to type: Decodable.Type) throws -> Decodable {
let decodingHelper = try JSONDecoder().decode(DecodingHelper.self, from: data)
let decodable = try decodingHelper.decode(to: type)
return decodable
}