我有以下JSON:
{
"header":{
"namespace":"Device",
"name":"Response",
"messageID":"60FA815A-DC432316",
"payloadVersion":"1"
},
"payload":{
"device":{
"firmware":"1.23W",
"name":"Device 1",
"uuid":"0ba64a0c-7a88b278-0001",
"security":{
"code":"aXdAPqd2OO9sZ6evLKjo2Q=="
}
},
"system":{
"uptime":5680126
}
}
}我使用quicktype.io创建了Swift结构:
// MARK: - Welcome
struct Welcome: Codable {
let header: Header
let payload: Payload
}
// MARK: - Header
struct Header: Codable {
let namespace, name, messageID, payloadVersion: String
}
// MARK: - Payload
struct Payload: Codable {
let device: Device
let system: System
}
// MARK: - Device
struct Device: Codable {
let firmware, name, uuid: String
let security: Security
}
// MARK: - Security
struct Security: Codable {
let code: String
}
// MARK: - System
struct System: Codable {
let uptime: Int
}但是,我已经有了一个Device类型,这是一个更小的类型:
struct Device: Identifiable {
let id: UUID
let ip: String
let name: String
let firmware: String
let uptime: Double
// ...
}如何才能很好地将原始JSON数据解码为Device结构?请注意,我的Device是平面的,并且具有嵌套在原始Device响应模型中更深的字段。我有一个自定义的Decodable实现吗?
发布于 2021-07-16 00:45:55
您可以创建中间CodingKeys,但这通常会变得相当乏味和不必要。相反,您可以创建一个通用的"string-key“,如下所示:
struct AnyStringKey: CodingKey, Hashable, ExpressibleByStringLiteral {
var stringValue: String
init(stringValue: String) { self.stringValue = stringValue }
init<S: StringProtocol>(_ stringValue: S) { self.init(stringValue: String(stringValue)) }
var intValue: Int?
init?(intValue: Int) { return nil }
init(stringLiteral value: String) { self.init(value) }
}这样,您就可以通过解码嵌套容器在单个解码器init中轻松导航您的结构:
extension Device: Decodable {
init(from decoder: Decoder) throws {
let root = try decoder.container(keyedBy: AnyStringKey.self)
let header = try root.nestedContainer(keyedBy: AnyStringKey.self, forKey: "header")
self.name = try header.decode(String.self, forKey: "name")
let payload = try root.nestedContainer(keyedBy: AnyStringKey.self, forKey: "payload")
let device = try payload.nestedContainer(keyedBy: AnyStringKey.self, forKey: "device")
self.id = try device.decode(UUID.self, forKey: "uuid")
self.firmware = try device.decode(String.self, forKey: "firmware")
let system = try payload.nestedContainer(keyedBy: AnyStringKey.self, forKey: "system")
self.uptime = try system.decode(Double.self, forKey: "uptime")
}
}(我跳过了ip,因为它不在您的数据中,并且我假设您的UUID只是一个拼写错误,因为它无效。)
有了这个,你应该能够解码你需要的任何部分。
这是非常简单和标准的,但如果你有很多东西要解码,它可能会变得有点乏味。在这种情况下,您可以使用helper函数对其进行改进。
extension KeyedDecodingContainer {
func decode<T>(_ type: T.Type, forPath path: String) throws -> T
where T : Decodable, Key == AnyStringKey {
let components = path.split(separator: ".")
guard !components.isEmpty else {
throw DecodingError.keyNotFound(AnyStringKey(path),
.init(codingPath: codingPath,
debugDescription: "Could not find path \(path)",
underlyingError: nil))
}
if components.count == 1 {
return try decode(type, forKey: AnyStringKey(components[0]))
} else {
let container = try nestedContainer(keyedBy: AnyStringKey.self, forKey: AnyStringKey(components[0]))
return try container.decode(type, forPath: components.dropFirst().joined(separator: "."))
}
}
}这样,您就可以通过点分路径语法访问值:
extension Device: Decodable {
init(from decoder: Decoder) throws {
let root = try decoder.container(keyedBy: AnyStringKey.self)
self.name = try root.decode(String.self, forPath: "header.name")
self.id = try root.decode(UUID.self, forPath: "payload.device.uuid")
self.firmware = try root.decode(String.self, forPath: "payload.device.firmware")
self.uptime = try root.decode(Double.self, forPath: "payload.system.uptime")
}
}发布于 2021-07-15 22:22:42
我看到了两种可能的快速解决方案:
解决方案1:
重命名可编码设备:
struct Device: Codable {
...
}转到
struct DeviceFromAPI: Codable {
...
}然后替换
struct Payload: Codable {
let device: Device
...
}转到
struct Payload: Codable {
let device: DeviceFromAPI
...
}Solution2:
使用嵌套结构。
将所有内容都放在Welcome中(顺便说一句,这是默认的QuickType.io名称,重命名它可能很有趣)。
struct Welcome: Codable {
let header: Header
let payload: Payload
// MARK: - Header
struct Header: Codable {
let namespace, name, messageID, payloadVersion: String
}
...
}即使需要将Device放在Payload中,也可以使用Go。
然后,当你想引用你的可编码设备时,你只需要使用Welcome.Payload.Device或Welcome.Device (取决于它的嵌套方式),当它是你自己的设备时,你只需要使用Device。
然后
然后,只需为Device创建一个自定义的init(),并将Codable Device作为参数。
extension Device {
init(withCodableDevice codableDevice: DeviceFromAPI) {
self.firmware = codableDevice.firmware
...
}
}或者使用解决方案2:
extension Device {
init(withCodableDevice codableDevice: Welcome.Payload.Device) {
self.firmware = codableDevice.firmware
...
}
}https://stackoverflow.com/questions/68395400
复制相似问题