我正在解析一个设计不良的JSON结构,在该结构中,我可以期望找到被重用作为指向进一步数据的键的值。像这样
{"modificationDate" : "..."
"type" : "...",
"version" : 2,
"manufacturer": "<WILDCARD-ID>"
"<WILDCARD-ID>": { /* known structure */ } }
WILDCARD-ID
在运行时几乎可以是任何东西,因此在编译时无法将其映射到结构中的某个字段。但是,一旦我取消引用该字段,它的值就具有已知的结构,这时我可以按照将JSON映射到struct
的常规过程进行操作。
我发现自己走这条路
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any]
let manDict = json["manufacturer"]
let data = NSKeyedArchiver.archivedData(withRootObject: manDict)
// now you have data!
但是这似乎非常circuit回,这使我认为也许有一种更干净的方法可以实现这一目标?
答案 0 :(得分:2)
您可以通过Decodable
使用自定义键,如下所示:
let json = """
{
"modificationDate" : "...",
"type" : "...",
"version" : 2,
"manufacturer": "<WILDCARD-ID>",
"<WILDCARD-ID>": {
"foo": 1
}
}
""".data(using: .utf8)!
struct InnerStruct: Decodable { // just as an example
let foo: Int
}
struct Example: Decodable {
let modificationDate: String
let type: String
let version: Int
let manufacturer: String
let innerData: [String: InnerStruct]
enum CodingKeys: String, CodingKey {
case modificationDate, type, version, manufacturer
}
struct CustomKey: CodingKey {
var stringValue: String
var intValue: Int?
init?(stringValue: String) {
self.stringValue = stringValue
}
init?(intValue: Int) {
self.stringValue = "\(intValue)";
self.intValue = intValue
}
}
init(from decoder: Decoder) throws {
// extract all known properties
let container = try decoder.container(keyedBy: CodingKeys.self)
self.modificationDate = try container.decode(String.self, forKey: .modificationDate)
self.type = try container.decode(String.self, forKey: .type)
self.version = try container.decode(Int.self, forKey: .version)
self.manufacturer = try container.decode(String.self, forKey: .manufacturer)
// get the inner structs with the unknown key(s)
var inner = [String: InnerStruct]()
let customContainer = try decoder.container(keyedBy: CustomKey.self)
for key in customContainer.allKeys {
if let innerValue = try? customContainer.decode(InnerStruct.self, forKey: key) {
inner[key.stringValue] = innerValue
}
}
self.innerData = inner
}
}
do {
let example = try JSONDecoder().decode(Example.self, from: json)
print(example)
}
答案 1 :(得分:2)
您可以在结构中捕获“特定但当前未知的密钥”的概念:
struct StringKey: CodingKey {
static let modificationDate = StringKey("modificationDate")
static let type = StringKey("type")
static let version = StringKey("version")
static let manufacturer = StringKey("manufacturer")
var stringValue: String
var intValue: Int?
init?(stringValue: String) { self.init(stringValue) }
init?(intValue: Int) { return nil }
init(_ stringValue: String) { self.stringValue = stringValue }
}
这样,解码就很简单,并且只对与键匹配的结构进行解码:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringKey.self)
modificationDate = try container.decode(String.self, forKey: .modificationDate)
type = try container.decode(String.self, forKey: .type)
version = try container.decode(Int.self, forKey: .version)
manufacturer = try container.decode(String.self, forKey: .manufacturer)
// Decode the specific key that was identified by `manufacturer`,
// and fail if it's missing
manufacturerData = try container.decode(ManufacturerData.self,
forKey: StringKey(manufacturer))
}