动态解码 JSON 个单个对象或对象数组

Decode JSON single object or array of object dynamically

假设我有一个来自 GET 方法的 JSON 响应数组,例如:

[{
    "id":"1",
    "Name":"John Doe",
},{
    "id":"2",
    "Name":"Jane Doe",
}]

从使用 id 参数的 POST 方法我只有 1 个对象 JSON 响应:

{
    "id":"1",
    "Name":"John Doe",
}

如何编写一个方法来动态解码 JSON? 目前,这就是我正在使用的:

func convertJSON<T:Decodable>(result: Any?, model: T.Type) -> T? {
    if let res = result {
        do {
            let data = try JSONSerialization.data(withJSONObject: res, options: JSONSerialization.WritingOptions.prettyPrinted)                
            return try JSONDecoder().decode(model, from: data)
        } catch {
            print(error)
            return nil
        }
    } else {
        return nil
    }
}

该方法可用于使用动态模型对单个对象进行解码,但我只是想不出它来动态处理单个对象/对象数组。

The most I can get with is just using a duplicate of the method but replacing T with [T] in the method parameter and return type, if the response is an array.

我乐于接受任何建议,感谢您的帮助,在此先感谢您。

编辑:如果此问题与 this 重复,我不确定标记的答案如何成为解决方案。

一个解决方案可能是总是 return [Model]?.

在您的函数中,首先尝试解码为 Model,成功时 return 一个包含单个解码对象的数组。如果失败则尝试解码为 [Model],成功时 return 解码对象 else return nil.

使用您的示例 JSON,我创建了一个结构:

struct Person: Codable {
  let id, name: String

  enum CodingKeys: String, CodingKey {
    case id
    case name = "Name"
  }
}

然后我创建了一个结构,其中包含一些方法,可以从 String 或可选的 Data 解码。

struct Json2Type<T: Decodable> {
  // From data to type T
  static public func convertJson(_ data: Data?) -> [T]? {
    // Check data is not nil
    guard let data = data else { return nil }
    let decoder = JSONDecoder()
    // First try to decode as a single object
    if let singleObject = try? decoder.decode(T.self, from: data) {
      // On success return the single object inside an array
      return [singleObject]
    }
    // Try to decode as multiple objects
    guard let multipleObjects = try? decoder.decode([T].self, from: data) else { return nil }
    return multipleObjects
 }

 // Another function to decode from String
 static public func convertJson(_ string: String) -> [T]? {
   return convertJson(string.data(using: .utf8))
 }
}

最后调用你喜欢的方法:

Json2Type<Person>.convertJson(JsonAsDataOrString)

更新:@odin_123,一种将Model[Model]作为return值的方法可以是使用 enum 完成。我们甚至可以在那里添加错误条件以避免 returning 选项。让我们将枚举定义为:

enum SingleMulipleResult<T> {
  case single(T)
  case multiple([T])
  case error
}

然后结构变成这样:

struct Json2Type<T: Decodable> {
  static public func convertJson(_ data: Data?) -> SingleMulipleResult<T> {
     guard let data = data else { return .error }
    let decoder = JSONDecoder()
    if let singleObject = try? decoder.decode(T.self, from: data) {
     return .single(singleObject)
    }
     guard let multipleObjects = try? decoder.decode([T].self, from: data) else { return .error }
     return .multiple(multipleObjects)
 }

 static public func convertJson(_ string: String) -> SingleMulipleResult<T> {
   return convertJson(string.data(using: .utf8))
 }
}

你可以像我们之前一样调用它:

let response = Json2Type<Person>.convertJson(JsonAsDataOrString)

并使用开关检查每个可能的响应值:

switch(response) {
  case .single(let object):
    print("One value: \(object)")
  case .multiple(let objects):
    print("Multiple values: \(objects)")
  case .error:
    print("Error!!!!")
}