我正在尝试解析如下所示的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张地图中?有没有更好的数据结构可用于此?
答案 0 :(得分:1)
(这是部分答案,我怀疑您会立即有更多问题,但是在我不知道您将如何使用此数据结构之前,我不想编写您需要的帮助程序。)< / p>
正如您所说,此操作的每个阶段都是布尔值,或者是将字符串映射到更多阶段的另一层。这么说吧。当您使用或一词描述事物时,通常会告诉您这是一个枚举。
// 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)
}
您不知道键,因此您需要“任何键”,这可能应该放在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)
(如何方便地访问它取决于您希望该表视图的外观;每行中的内容,UITableViewDataSource的外观如何?如果您在问题中进行解释,我很乐意为您提供帮助您想如何使用这些数据。)
下面的代码可能方式太复杂,以至于您无法真正使用,但是我想探究您要寻找的接口类型。这个数据结构非常复杂,我仍然不清楚如何使用它。可以帮助您编写一些使用此结果的代码,然后我可以帮助编写与该调用代码匹配的代码。
但是您可以考虑这种数据结构的一种方式是,它是一个“字典”,可以通过“ {” {1}}这条“路径”来索引。因此,一条路径是[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()]
}
}
}
类似地,您将获得所有路径。
result[["pro3", "pro4"]] // true
我知道这是一个过于复杂的答案,但是它可能会开始让您以正确的方式思考问题以及您想要从此数据结构中获取的信息。弄清楚您的用例,然后剩下的就可以了。
答案 1 :(得分:-2)
我认为最好的方法是使用AlamoFire,PromiseKit和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
或空字段。