【问题标题】:Extract every value from a complex JSON into an array in Swift将复杂 JSON 中的每个值提取到 Swift 中的数组中
【发布时间】:2017-04-30 04:34:14
【问题描述】:

当后端发现错误时,我的客户端返回了一个疯狂的 JSON。它包括许多“错误”键以及不确定数量的数组和 JSON 的嵌套部分。这是一个例子:

{
 "errors": {
    "children": {
      "name": {
        "errors": [
          "Error name"
        ]
      },
      "lastName": {
        "errors": [
          "Error lastName"
        ]
      },
      "email": {
        "errors": [
          "Error mail"
        ]
      },
      "gender": {},
      "birthday": {
        "children": {
          "year": {},
          "month": {
            "errors": [
              "Error month"
             ]
          },
          "day": {}
        }
      }
    }
  }
}

由于他声称无法重写响应以返回更常规的错误处理,我想遍历 JSON 并获取包含每个错误字符串的数组。鉴于除了错误没有其他价值,我认为获得所有价值也可以解决问题。 JSON结构可能会根据错误位置和服务而改变。

我目前正在使用 SwiftyJSON 来解析响应(以防它可能有任何帮助)

【问题讨论】:

  • 在不知道键值的情况下如何查找数据,json根值可能是字典或数组,可以用这个来检查
  • 是的 Shobhakar,我知道这一点,我做了一个递归函数,遍历了 json 树,但我没有找到返回最终结果的方法。 iWheelBuy 和 Benzi 发布了解决该问题的两种有效方法。

标签: json swift nsdictionary flatten


【解决方案1】:

假设你有一些数据

let data: Data = /* the way you get your JSON data */

您希望有一些函数可以从 JSON 中提取所有错误消息并将它们放入数组 [String],不是吗?我在您的 JSON 中看到 4 个错误,所以让我们执行查找所有 4 条消息的函数:

func getErrors(_ dictionary: [String:JSON]?) -> [String] {
    guard let dictionary = dictionary else { return [] }
    let errorsNormal = dictionary
        .map({ (object) -> [String] in
            guard object.key == "errors" else { return [] }
            guard let array = object.value.array else { return [] }
            return  array.flatMap({ $0.string })
        })
        .flatMap({ $0 })
    let errorsRecursive = dictionary
        .flatMap({ (object) -> [String:JSON]? in
            return object.value.dictionary
        })
        .flatMap({ $0 })
        .map({ getErrors($0) })
        .flatMap({ $0 })
    return errorsNormal + errorsRecursive
}

如何使用?

let errors = getErrors(json["errors"].dictionary)
debugPrint(errors)

日志将是

["Error lastName", "Error name", "Error mail", "Error month"]

附:如果这不是您想要的 - 请告诉我,我会删除答案

【讨论】:

  • 事实上。这正是我想要的。谢谢。
【解决方案2】:

您可以编写一个递归函数,尝试从其输入中过滤掉错误消息。

由于您的 JSON 结构没有明确定义,您需要有条件地键入并检查您的根/当前对象作为数组或字典。

以下是可以在 XCode Playground 中运行的示例代码。

func extractErrors(jsonObject: Any, errors: inout [String]) {
    if let array = jsonObject as? [Any] {
        array.forEach {
            extractErrors(jsonObject: $0, errors: &errors)
        }
    }
    else if let dict = jsonObject as? [String : Any] {
        for key in dict.keys {
            if key == "errors", let errorList = dict[key] as? [String] {
                errorList.forEach { errors.append($0) }
            }
            else {
                extractErrors(jsonObject: dict[key]!, errors: &errors)
            }
        }
    }
}

let json = "{\"errors\":{\"children\":{\"name\":{\"errors\":[\"Error name\"]},\"lastName\":{\"errors\":[\"Error lastName\"]},\"email\":{\"errors\":[\"Error mail\"]},\"gender\":{},\"birthday\":{\"children\":{\"year\":{},\"month\":{\"errors\":[\"Error month\"]},\"day\":{}}}}}}"

if let data = json.data(using: .utf8),
    let jsonObject = try? JSONSerialization.jsonObject(with: data, options: [.mutableContainers])
{
    var errors = [String]()
    extractErrors(jsonObject: jsonObject, errors: &errors)
    print(errors) // ["Error lastName", "Error name", "Error mail", "Error month"]

}

【讨论】:

  • 谢谢。先前的答案是正确的,但我认为这是解决此问题的一种更优雅的方法,因此我会为以后的访问者检查它。我已经做了一些非常相似的事情,但我试图错误地使用 inout var。在您的帮助下,我也学到了这一点。
猜你喜欢
  • 1970-01-01
  • 2020-04-18
  • 1970-01-01
  • 1970-01-01
  • 2019-03-04
  • 1970-01-01
  • 2016-02-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多