解析复杂的JSON,其中数据和“列标题”位于单独的数组中

我有以下从API获得的JSON数据:

{"datatable": 
  {"data" : [
    ["John", "Doe", "1990-01-01", "Chicago"], 
    ["Jane", "Doe", "2000-01-01", "San Diego"]
  ], 
  "columns": [
    { "name": "First", "type": "String" }, 
    { "name": "Last", "type": "String" },
    { "name": "Birthday", "type": "Date" }, 
    { "name": "City", "type": "String" }
  ]}
}

稍后的查询可能会导致以下结果:

{"datatable": 
  {"data" : [
    ["Chicago", "Doe", "John", "1990-01-01"], 
    ["San Diego", "Doe", "Jane", "2000-01-01"]
  ], 
  "columns": [
    { "name": "City", "type": "String" },
    { "name": "Last", "type": "String" },
    { "name": "First", "type": "String" }, 
    { "name": "Birthday", "type": "Date" }
  ]
  }
}

列的顺序似乎很不稳定。

I initially wanted to decode the JSON with JSONDecoder, but for that I need the data array to be a dictionary and not an array. The only other method I could think of was to convert the result to a dictionary with something like:

extension String {
    func convertToDictionary() -> [String: Any]? {
        if let data = data(using: .utf8) {
            return try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
        }
        return nil
    }
}

This will cause me however to have a lot of nested if let statements like if let x = dictOfStr["datatable"] as? [String: Any] { ... }. Not to mention the subsequent looping through the columns array to organize the data.

有更好的解决方案吗? 谢谢

评论
  • bquia
    bquia 回复

    The way I would do it is to create two model objects and have them both conform to the Codable protocol like so:

    struct Datatable: Codable {
        let data: [[String]]
        let columns: [[String: String]]
    }
    
    struct JSONResponseType: Codable {
        let datatable: Datatable
    }
    

    Then in your network call I'd decode the json response using JSONDecoder():

    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    guard let decodedData = try? decoder.decode(JSONResponseType.self, from: data) else {
        // handle decoding failure
        return
    }
    
    // do stuff with decodedData ex:
    
    let datatable = decodedData.datatable
    ...
    

    data in this case is the result from the URLSessionTask.

    让我知道这个是否奏效。

  • cnon
    cnon 回复

    也许尝试将给定的输入保存在用户对象列表中?但是,通过这种方式可以构建JSON的结构,您可以将它们添加到列表中,并在需要后再进行处理。名称后的初始字母顺序可能也有助于用户的显示顺序。

    这是我写的一个示例,您可以在列表中添加一个带有当前打印信息的新UserObject,而不是记录信息。

    let databaseData =  table["datatable"]["data"];
    let databaseColumns = table["datatable"]["columns"];
    
    for (let key in databaseData) { 
        console.log(databaseColumns[0]["name"] + " = " + databaseData[key][0]);
        console.log(databaseColumns[1]["name"] + " = " + databaseData[key][1]);
        console.log(databaseColumns[2]["name"] + " = " + databaseData[key][2]);    
        console.log(databaseColumns[3]["name"] + " = " + databaseData[key][3]);
    }