JSON 在 swift 中解析。解析 json 文件的最佳结构是什么
JSON parsing in swift. What's the best structure parse a json file
我正在尝试解析如下所示的 json 文件:
{
"MyApp": {
"pro1" : {
"enable": true
},
"pro2" : {
"enable": true
}
},
"pro3" : {
"pro4" : true,
"pro5" : true,
"pro6" : true,
"pro7" : true,
"pro8" : true,
"pro10" : true
},
"pro11": {
"pro12": false,
"pro13": false,
"pro14": false
},
"pro15": {
"prob16": true
},
"prob16": {
"prob17": {
"prob18": true,
}
},
"prob19": {
"prob20": {
"prob21": {
"prob22": false
}
}
},
"prob23": true,
"prob24": true
}
我正在尝试以一种易于访问的方式对其进行解析。我首先将 json 文件解析为类型为 [String:Any]
的 json 对象,然后我尝试将这些对放入 [String:[String:[String:Bool]]]
但后来我意识到一个问题是我没有不知道可能有多少层。也许会有一对中的一对..
但是如果知道层数,比如说最大层数是4,我还把它当成地图吗?在其他 3 张地图中映射?有没有更好的数据结构可以把它放进去?
在我看来,最好的方法是使用 AlamoFire, PromiseKit and SwiftyJSON 这 3 个 Swift 库将使解析这个复杂的 JSON 更容易,行数更少,并且肯定会为您节省很多当然,它们也有大量记录。
有关如何使用此库解析 MyApp
JSON 字段的一些示例代码:
func DownloadDataAndParseJSON() {
let headers = ["headerParameter": someParameter]
Alamofire.request(someUrl, method: .get, headers: headers).responseJSON { response in
let json = JSON(response.result.value as Any)
if let allJsonFields = json.array {
for item in allJsonFields{
let myApp = item["MyApp"].array ?? []
for item in myApp{
let pro1 = item["pro1"].array ?? []
let pro2 = item["pro2"].array ?? []
for item in pro1{
let enable = item["enable"].bool ?? false}
for item in pro2{
let enable = item["enable"].bool ?? false}
}
//Here you can append data to dictionary, object or struct.
}
}
}
}
了解如何使用 .bool
将解析后的值转换为布尔值,还可以使用 ??
参数添加可选的默认值,以防 JSON 抛出nil
或空字段。
(这是部分答案,我怀疑您会立即有更多问题,但在我知道您将如何使用此数据结构之前,我不想编写您需要的帮助程序。 )
正如你所说,这个的每个阶段要么是一个布尔值,要么是另一个层将字符串映射到更多阶段。所以在类型中这样说。当您使用 或 一词描述某物时,通常会告诉您它是一个枚举。
// Each level of Settings is either a value (bool) or more settings.
enum Settings {
// Note that this is not order-preserving; it's possible to fix that if needed
indirect case settings([String: Settings])
case value(Bool)
}
你不知道密钥,所以你需要 "any key," 这可能应该在 stdlib 中,但它很容易编写。
// A CodingKey that handle any string
struct AnyStringKey: CodingKey {
var stringValue: String
init?(stringValue: String) { self.stringValue = stringValue }
var intValue: Int?
init?(intValue: Int) { return nil }
}
有了这些,解码只是递归地遍历树,并解码一个级别或一个值。
extension Settings: Decodable {
init(from decoder: Decoder) throws {
// Try to treat this as a settings dictionary
if let container = try? decoder.container(keyedBy: AnyStringKey.self) {
// Turn all the keys in to key/settings pairs (recursively).
let keyValues = try container.allKeys.map { key in
(key.stringValue, try container.decode(Settings.self, forKey: key))
}
// Turn those into a dictionary (if dupes, keep the first)
let level = Dictionary(keyValues, uniquingKeysWith: { first, _ in first })
self = .settings(level)
} else {
// Otherwise, it had better be a boolen
self = .value(try decoder.singleValueContainer().decode(Bool.self))
}
}
}
let result = try JSONDecoder().decode(Settings.self, from: json)
(您如何方便地访问它有点取决于您希望 table 视图看起来像什么;每行中有什么,您的 UITableViewDataSource 是什么样的?如果您愿意,我很乐意帮助您解决这个问题我会在问题中解释你想如何使用这些数据。)
下面的代码可能方式过于复杂,您无法真正使用,但我想探索一下您正在寻找什么样的界面。这个数据结构相当复杂,我还不清楚你想如何使用它。这将有助于您编写一些使用此结果的代码,然后我可以帮助编写与该调用代码匹配的代码。
但是您可以考虑此数据结构的一种方式是,它是一个 "dictionary",可以由 "path" 索引,后者是 [String]
。所以一条路径是 ["prob23"]
一条路径是 ["prob19", "prob20", "prob21", "prob22"]
.
所以要下标,我们可以这样做:
extension Settings {
// This is generic so it can handle both [String] and Slice<[String]>
// Some of this could be simplified by making a SettingsPath type.
subscript<Path>(path: Path) -> Bool?
where Path: Collection, Path.Element == String {
switch self {
case .value(let value):
// If this is a value, and there's no more path, return the value
return path.isEmpty ? value : nil
case .settings(let settings):
// If this is another layer of settings, recurse down one layer
guard let key = path.first else { return nil }
return settings[key]?[path.dropFirst()]
}
}
}
这不是一本真正的词典。它甚至不是真正的 Collection。它只是一个带有下标语法的数据结构。但是有了这个,你可以说:
result[["pro3", "pro4"]] // true
并且,同样地,您获得了所有路径。
extension Settings {
var paths: [[String]] {
switch self {
case .settings(let settings):
// For each key, prepend it to all its children's keys until you get to a value
let result: [[[String]]] = settings.map { kv in
let key = kv.key
let value = kv.value
switch value {
case .value:
return [[key]] // Base case
case .settings:
return value.paths.map { [key] + [=15=] } // Recurse and add our key
}
}
// The result of that is [[[String]]] because we looped twice over something
// that was already an array. We want to flatten it back down one layer to [[String]]
return Array(result.joined())
case .value:
return [] // This isn't the base case; this is just in case you call .paths on a value.
}
}
}
for path in result.paths {
print("\(path): \(result[path]!)")
}
==>
["pro15", "prob16"]: true
["pro3", "pro4"]: true
["pro3", "pro10"]: true
["pro3", "pro7"]: true
["pro3", "pro8"]: true
["pro3", "pro5"]: true
["pro3", "pro6"]: true
["prob19", "prob20", "prob21", "prob22"]: false
["prob23"]: true
["prob24"]: true
["MyApp", "pro1", "enable"]: true
["MyApp", "pro2", "enable"]: true
["prob16", "prob17", "prob18"]: true
["pro11", "pro13"]: false
["pro11", "pro14"]: false
["pro11", "pro12"]: false
我知道这个答案太复杂了,但它可能会让您开始以正确的方式思考问题以及您希望从此数据结构中得到什么。找出您的用例,其余的将由此产生。
我正在尝试解析如下所示的 json 文件:
{
"MyApp": {
"pro1" : {
"enable": true
},
"pro2" : {
"enable": true
}
},
"pro3" : {
"pro4" : true,
"pro5" : true,
"pro6" : true,
"pro7" : true,
"pro8" : true,
"pro10" : true
},
"pro11": {
"pro12": false,
"pro13": false,
"pro14": false
},
"pro15": {
"prob16": true
},
"prob16": {
"prob17": {
"prob18": true,
}
},
"prob19": {
"prob20": {
"prob21": {
"prob22": false
}
}
},
"prob23": true,
"prob24": true
}
我正在尝试以一种易于访问的方式对其进行解析。我首先将 json 文件解析为类型为 [String:Any]
的 json 对象,然后我尝试将这些对放入 [String:[String:[String:Bool]]]
但后来我意识到一个问题是我没有不知道可能有多少层。也许会有一对中的一对..
但是如果知道层数,比如说最大层数是4,我还把它当成地图吗?在其他 3 张地图中映射?有没有更好的数据结构可以把它放进去?
在我看来,最好的方法是使用 AlamoFire, PromiseKit and SwiftyJSON 这 3 个 Swift 库将使解析这个复杂的 JSON 更容易,行数更少,并且肯定会为您节省很多当然,它们也有大量记录。
有关如何使用此库解析 MyApp
JSON 字段的一些示例代码:
func DownloadDataAndParseJSON() {
let headers = ["headerParameter": someParameter]
Alamofire.request(someUrl, method: .get, headers: headers).responseJSON { response in
let json = JSON(response.result.value as Any)
if let allJsonFields = json.array {
for item in allJsonFields{
let myApp = item["MyApp"].array ?? []
for item in myApp{
let pro1 = item["pro1"].array ?? []
let pro2 = item["pro2"].array ?? []
for item in pro1{
let enable = item["enable"].bool ?? false}
for item in pro2{
let enable = item["enable"].bool ?? false}
}
//Here you can append data to dictionary, object or struct.
}
}
}
}
了解如何使用 .bool
将解析后的值转换为布尔值,还可以使用 ??
参数添加可选的默认值,以防 JSON 抛出nil
或空字段。
(这是部分答案,我怀疑您会立即有更多问题,但在我知道您将如何使用此数据结构之前,我不想编写您需要的帮助程序。 )
正如你所说,这个的每个阶段要么是一个布尔值,要么是另一个层将字符串映射到更多阶段。所以在类型中这样说。当您使用 或 一词描述某物时,通常会告诉您它是一个枚举。
// Each level of Settings is either a value (bool) or more settings.
enum Settings {
// Note that this is not order-preserving; it's possible to fix that if needed
indirect case settings([String: Settings])
case value(Bool)
}
你不知道密钥,所以你需要 "any key," 这可能应该在 stdlib 中,但它很容易编写。
// A CodingKey that handle any string
struct AnyStringKey: CodingKey {
var stringValue: String
init?(stringValue: String) { self.stringValue = stringValue }
var intValue: Int?
init?(intValue: Int) { return nil }
}
有了这些,解码只是递归地遍历树,并解码一个级别或一个值。
extension Settings: Decodable {
init(from decoder: Decoder) throws {
// Try to treat this as a settings dictionary
if let container = try? decoder.container(keyedBy: AnyStringKey.self) {
// Turn all the keys in to key/settings pairs (recursively).
let keyValues = try container.allKeys.map { key in
(key.stringValue, try container.decode(Settings.self, forKey: key))
}
// Turn those into a dictionary (if dupes, keep the first)
let level = Dictionary(keyValues, uniquingKeysWith: { first, _ in first })
self = .settings(level)
} else {
// Otherwise, it had better be a boolen
self = .value(try decoder.singleValueContainer().decode(Bool.self))
}
}
}
let result = try JSONDecoder().decode(Settings.self, from: json)
(您如何方便地访问它有点取决于您希望 table 视图看起来像什么;每行中有什么,您的 UITableViewDataSource 是什么样的?如果您愿意,我很乐意帮助您解决这个问题我会在问题中解释你想如何使用这些数据。)
下面的代码可能方式过于复杂,您无法真正使用,但我想探索一下您正在寻找什么样的界面。这个数据结构相当复杂,我还不清楚你想如何使用它。这将有助于您编写一些使用此结果的代码,然后我可以帮助编写与该调用代码匹配的代码。
但是您可以考虑此数据结构的一种方式是,它是一个 "dictionary",可以由 "path" 索引,后者是 [String]
。所以一条路径是 ["prob23"]
一条路径是 ["prob19", "prob20", "prob21", "prob22"]
.
所以要下标,我们可以这样做:
extension Settings {
// This is generic so it can handle both [String] and Slice<[String]>
// Some of this could be simplified by making a SettingsPath type.
subscript<Path>(path: Path) -> Bool?
where Path: Collection, Path.Element == String {
switch self {
case .value(let value):
// If this is a value, and there's no more path, return the value
return path.isEmpty ? value : nil
case .settings(let settings):
// If this is another layer of settings, recurse down one layer
guard let key = path.first else { return nil }
return settings[key]?[path.dropFirst()]
}
}
}
这不是一本真正的词典。它甚至不是真正的 Collection。它只是一个带有下标语法的数据结构。但是有了这个,你可以说:
result[["pro3", "pro4"]] // true
并且,同样地,您获得了所有路径。
extension Settings {
var paths: [[String]] {
switch self {
case .settings(let settings):
// For each key, prepend it to all its children's keys until you get to a value
let result: [[[String]]] = settings.map { kv in
let key = kv.key
let value = kv.value
switch value {
case .value:
return [[key]] // Base case
case .settings:
return value.paths.map { [key] + [=15=] } // Recurse and add our key
}
}
// The result of that is [[[String]]] because we looped twice over something
// that was already an array. We want to flatten it back down one layer to [[String]]
return Array(result.joined())
case .value:
return [] // This isn't the base case; this is just in case you call .paths on a value.
}
}
}
for path in result.paths {
print("\(path): \(result[path]!)")
}
==>
["pro15", "prob16"]: true
["pro3", "pro4"]: true
["pro3", "pro10"]: true
["pro3", "pro7"]: true
["pro3", "pro8"]: true
["pro3", "pro5"]: true
["pro3", "pro6"]: true
["prob19", "prob20", "prob21", "prob22"]: false
["prob23"]: true
["prob24"]: true
["MyApp", "pro1", "enable"]: true
["MyApp", "pro2", "enable"]: true
["prob16", "prob17", "prob18"]: true
["pro11", "pro13"]: false
["pro11", "pro14"]: false
["pro11", "pro12"]: false
我知道这个答案太复杂了,但它可能会让您开始以正确的方式思考问题以及您希望从此数据结构中得到什么。找出您的用例,其余的将由此产生。