(Это частичный ответ, я подозреваю, что у вас сразу же возникнут дополнительные вопросы, но пока я не знаю, как вы собираетесь использовать эту структуру данных, я не хотел писать помощников, которые вам понадобятся.)
Как вы говорите, каждый этап этого является либо логическим значением, либо другим слоем, отображающим строки на несколько этапов.Так и говорите, что в виду.Когда вы описываете что-то, используя слово или , это обычно говорит вам, что это перечисление.
// 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? Я с радостью помогу вам в этом, если вы объясните в вопросе, каквы хотите использовать эти данные.)
Swift Runner
Следующий код, вероятно, way слишком сложен для того, чтобы вы действительно могли его использовать, но я хочу выяснить, какой интерфейс вы ищете.Эта структура данных довольно сложна, и мне все еще неясно, как вы хотите ее использовать.Для вас было бы полезно написать некоторый код, который использует этот результат, а затем я могу помочь написать код, соответствующий этому вызывающему коду.
Но вы можете думать об этой структуре данных как о «словаре».это может быть проиндексировано "путем", который является [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
И, аналогично, вы получите все пути.
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] + $0 } // 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
Я знаю, что это слишком сложный ответ, но он может начать получатьвы в правильном мышлении о проблеме и что вы хотите от этой структуры данных.Выясните свой вариант использования, и все остальное вытекает из этого.