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

时间:2019-11-29 03:51:16

标签: json swift codable decodable

假设我有一个来自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
    }
}

该方法可用于使用动态模型对单个对象进行解码,但是我无法弄清楚它可以动态地处理单个对象/对象数组。

  

我能得到的最多就是使用方法的重复,但是用T代替T   如果响应为数组,则在方法参数和返回类型中使用[T]。

我愿意接受任何建议,感谢您的帮助。

编辑:如果该问题与this重复,则不确定标记的答案将如何解决。

1 个答案:

答案 0 :(得分:0)

一种解决方案是始终返回[Model]?

在您的函数内部,首先尝试将其解码为Model,如果成功,则返回一个数组,其中包含单个解码的对象。如果失败,则尝试解码为[Model],成功解码后返回解码的对象,否则返回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]enum作为返回值的方法。我们甚至可以在其中添加错误条件,以避免返回可选值。让我们将枚举定义为:

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!!!!")
}
相关问题