Разбор JSON по-быстрому.Какова лучшая структура разбора файла JSON - PullRequest
0 голосов
/ 13 марта 2019

Я пытаюсь проанализировать файл 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 в объект json с типом [String:Any], затем попытался поместить пары в [String:[String:[String:Bool]]], но потом понял, что проблема в том, что я не знаю, сколько слоев может быть там. Возможно, в парах внутри пар будут пары.

Но если вы знаете слои, скажем, максимальный слой равен 4, я все еще помещу это как карту? карта в 3 других картах? Есть ли лучшая структура данных, чтобы поместить это в?

Ответы [ 2 ]

1 голос
/ 14 марта 2019

(Это частичный ответ, я подозреваю, что у вас сразу же возникнут дополнительные вопросы, но пока я не знаю, как вы собираетесь использовать эту структуру данных, я не хотел писать помощников, которые вам понадобятся.)

Как вы говорите, каждый этап этого является либо логическим значением, либо другим слоем, отображающим строки на несколько этапов.Так и говорите, что в виду.Когда вы описываете что-то, используя слово или , это обычно говорит вам, что это перечисление.

// 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

Я знаю, что это слишком сложный ответ, но он может начать получатьвы в правильном мышлении о проблеме и что вы хотите от этой структуры данных.Выясните свой вариант использования, и все остальное вытекает из этого.

0 голосов
/ 14 марта 2019

На мой взгляд, лучший подход - использовать 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 или пустое поле.

...