我有以下JSON
{"DynamicKey":6410,"Meta":{"name":"","page":""}}
DynamicKey在编译时是未知的。我正在尝试查找如何 使用可解码的方法解析此结构。
public struct MyStruct: Decodable {
public let unknown: Double
public let meta: [String: String]
private enum CodingKeys: String, CodingKey {
case meta = "Meta"
}
}
有什么想法吗?
答案 0 :(得分:5)
要解码任意字符串,您需要这样的密钥:
// Arbitrary key
private struct Key: CodingKey, Hashable, CustomStringConvertible {
static let meta = Key(stringValue: "Meta")!
var description: String {
return stringValue
}
var hashValue: Int { return stringValue.hash }
static func ==(lhs: Key, rhs: Key) -> Bool {
return lhs.stringValue == rhs.stringValue
}
let stringValue: String
init(_ string: String) { self.stringValue = string }
init?(stringValue: String) { self.init(stringValue) }
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
}
这是一个非常通用的工具(期望static let meta
),可用于各种通用密钥问题。
这样,您可以找到不是.meta
的第一个键,并将其用作动态键。
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
meta = try container.decode([String: String].self, forKey: .meta)
guard let dynamicKey = container.allKeys.first(where: { $0 != .meta }) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [],
debugDescription: "Could not find dynamic key"))
}
unknown = try container.decode(Double.self, forKey: dynamicKey)
}
一起作为游乐场:
import Foundation
let json = Data("""
{"DynamicKey":6410,"Meta":{"name":"","page":""}}
""".utf8)
public struct MyStruct: Decodable {
public let unknown: Double
public let meta: [String: String]
// Arbitrary key
private struct Key: CodingKey, Hashable, CustomStringConvertible {
static let meta = Key(stringValue: "Meta")!
var description: String {
return stringValue
}
var hashValue: Int { return stringValue.hash }
static func ==(lhs: Key, rhs: Key) -> Bool {
return lhs.stringValue == rhs.stringValue
}
let stringValue: String
init(_ string: String) { self.stringValue = string }
init?(stringValue: String) { self.init(stringValue) }
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
meta = try container.decode([String: String].self, forKey: .meta)
guard let dynamicKey = container.allKeys.first(where: { $0 != .meta }) else {
throw DecodingError.dataCorrupted(.init(codingPath: [],
debugDescription: "Could not find dynamic key"))
}
unknown = try container.decode(Double.self, forKey: dynamicKey)
}
}
let myStruct = try! JSONDecoder().decode(MyStruct.self, from: json)
myStruct.unknown
myStruct.meta
此技术可以扩展为decode arbitrary JSON。有时这样做比较容易,然后取出所需的片段,然后对每个片段进行解码。例如,使用上述JSON要点,您可以通过以下方式实现MyStruct
:
public struct MyStruct: Decodable {
public let unknown: Double
public let meta: [String: String]
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let json = try container.decode(JSON.self)
guard let meta = json["Meta"]?.dictionaryValue as? [String: String] else {
throw DecodingError.dataCorrupted(.init(codingPath: [],
debugDescription: "Could not find meta key"))
}
self.meta = meta
guard let (_, unknownJSON) = json.objectValue?.first(where: { (key, _) in key != "Meta" }),
let unknown = unknownJSON.doubleValue
else {
throw DecodingError.dataCorrupted(.init(codingPath: [],
debugDescription: "Could not find dynamic key"))
}
self.unknown = unknown
}
}
答案 1 :(得分:1)
import UIKit
var str = """
{"DynamicKey":6410,"Meta":{"name":"","page":""}}
"""
public struct MyStruct: Decodable {
public var unknown: Double?
public var meta: [String: String]?
public init(from decoder: Decoder) {
guard let container = try? decoder.container(keyedBy: CodingKeys.self) else {
fatalError()
}
for key in container.allKeys {
unknown = try? container.decode(Double.self, forKey: key)//) ?? 0.0
if key.stringValue == "Meta" {
meta = try? container.decode([String: String].self, forKey: key)
}
}
print(container.allKeys)
}
struct CodingKeys: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init?(intValue: Int) {
return nil
}
}
}
let jsonData = str.data(using: .utf8)!
let jsonDecoder = JSONDecoder()
let myStruct = try! jsonDecoder.decode(MyStruct.self, from: jsonData)
print("Meta : \(myStruct.meta)")
print("Double : \(myStruct.unknown)")
我已经回答了类似的问题