【发布时间】:2017-11-08 08:19:36
【问题描述】:
我有一个使用上传时创建的唯一键的 JSON 数据结构。如果我逐行阅读每个字典项目,我可以阅读所有内容。但是,我正在尝试修改我的代码以使用 Swift 4 可编码属性。
不幸的是,阅读Ray Wenderlich tutorial 和阅读Ultimate Guide to JSON Parsing with Swift 并没有让我成为天才。
JSON 看起来像这个简单的示例: 请注意,“123”、“456”、“case1”、“case2”、“u1”、“u2”等键在运行时是未知的。
let json = """
{
"things" : {
"123" : {
"name" : "Party",
"owner" : "Bob",
"isActive" : true,
"cases" : {
"case1" : {
"no" : 1
},
"case2" : {
"no" : 2
}
}
},
"456" : {
"name" : "Bus",
"owner" : "Joe",
"isActive" : true
}
},
"users" : {
"u1" : {
"name" : "Summer"
},
"u2" : {
"name" : "Daffy"
}
}
}
"""
在this SO question on flattening JSON 之后,我能够为我的大部分数据创建解码器,但不能为嵌套字典创建解码器(在示例中,案例就像嵌套字典一样)。我确信我错过了一些简单的东西。
如果我尝试包含注释掉的部分,playground 将不会运行,不会给出错误。
struct Thing: Decodable {
let id: String
let isActive: Bool
let name: String
let owner: String
//var cases = [Case]()
init(id: String, isActive: Bool, name: String, owner: String){//}, cases: [Case]?) {
self.id = id
self.isActive = isActive
self.name = name
self.owner = owner
//self.cases = cases ?? [Case(id: "none", caseNumber: 0)]
}
}
struct User: Decodable {
let id: String
let name: String
}
struct Case: Decodable {
let id: String
let caseNumber: Int
}
struct ResponseData: Decodable {
var things = [Thing]()
var users = [User]()
enum CodingKeys: String, CodingKey {
case trips
case users
}
private struct PhantomKeys: CodingKey {
var intValue: Int?
var stringValue: String
init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
init?(stringValue: String) { self.stringValue = stringValue }
}
private enum ThingKeys: String, CodingKey {
case isActive, name, owner, cases
}
private enum UserKeys: String, CodingKey {
case name
}
private enum CaseKeys: String, CodingKey {
case id
case caseNumber = "no"
}
init(from decoder: Decoder) throws {
let outer = try decoder.container(keyedBy: CodingKeys.self)
let thingcontainer = try outer.nestedContainer(keyedBy: PhantomKeys.self, forKey: .things)
for key in thingcontainer.allKeys {
let aux = try thingcontainer.nestedContainer(keyedBy: ThingKeys.self, forKey: key)
let name = try aux.decode(String.self, forKey: .name)
let owner = try aux.decode(String.self, forKey: .owner)
let isActive = try aux.decode(Bool.self, forKey: .isActive)
// let c = try aux.nestedContainer(keyedBy: CaseKeys.self, forKey: .cases)
// var cases = [Case]()
// for ckey in c.allKeys {
// let caseNumber = try c.decode(Int.self, forKey: .caseNumber)
// let thiscase = Case(id: ckey.stringValue, caseNumber: caseNumber)
// cases.append(thiscase)
// }
let thing = Thing(id: key.stringValue, isActive: isActive, name: name, owner: owner)//, cases: cases)
things.append(thing)
}
let usercontainer = try outer.nestedContainer(keyedBy: PhantomKeys.self, forKey: .users)
for key in usercontainer.allKeys {
let aux = try usercontainer.nestedContainer(keyedBy: UserKeys.self, forKey: key)
let name = try aux.decode(String.self, forKey: .name)
let user = User(id: key.stringValue,name: name)
users.append(user)
}
}
}
它适用于事物和用户,但我不得不忽略这些案例。在 cmets// 中查看 print 的输出。
let data = json.data(using: .utf8)!
let things = try JSONDecoder().decode(ResponseData.self, from: data).things
print(things[0])
//Thing(id: "456", isActive: true, name: "Bus", owner: "Joe")
let users = try JSONDecoder().decode(ResponseData.self, from: data).users
print(users[0])
//User(id: "u1", name: "Summer")
我尝试使用this SO question on decoding 的指导,这对我来说似乎更清晰,但我没有成功实施。
这个代码是also a GIST
我的问题有两个:
- 如何在我的事物中以嵌套数组的形式获取案例数据?
- 你能 建议一种更简洁/更短的编码方式?感觉就像我在 重复的事情,但我在 JSON 编码/解码的几个示例。
【问题讨论】:
-
您目前无法通过
Codable协议在 Swift 中为 动态键 自动编码/解码值——这样的工作需要手动完成。 -
我可以使用动态键来做到这一点 - 见上文。 “123”和“456”是动态键,拿到数据的时候不知道。它通过“PhantomKeys”工作 - 我想我是手动做的。但是,我仍然无法理解嵌套的“案例”,我认为这不是不可能的。
-
在下面查看我的答案,我重用了您的“PhantomKeys”在您的“事物”中创建了一个可选的案例列表 - 我最初误解了您的问题,因此进行了编辑,我将原始回复留在那里只是为了参考