Swift 5 + Alamofire 5.*: 解码顶部具有相同根对象的数据

Swift 5 + Alamofire 5.*: Decode data with same root object at the top

我的问题是我有 JSON 从服务器获取的对象,如下所示:

{
    "data": [
        {
            "id": 1,
            "name": "at",
            "amount": 446,
            "createdAt": "25/04/2020",
            "updatedAt": "25/04/2020"
        },
        {
            "id": 2,
            "name": "iste",
            "amount": 872,
            "createdAt": "25/04/2020",
            "updatedAt": "25/04/2020"
        }
    ]
}

而且我有解码这个对象的 Codable 结构:

struct Expense: Codable, Identifiable {
    var id: Int
    var name: String
    var amount: String
    var createdAt: String
    var updatedAt: String
}

另外,我 class 有一个静态方法来执行 AF 请求,我还使用 FuturePromise 库来处理请求的完成:

struct RequestAPI {
    @discardableResult
    static func callAndDecode<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder()) -> Future<T> {
      return Future(operation: { completion in
          AF.request(route).responseDecodable(decoder: decoder, completionHandler: { (response: DataResponse<T, AFError>) in
                switch response.result {
                    case .success(let value):
                      completion(.success(value))
                    case .failure(let error):
                      print(error.localizedDescription)
                      completion(.failure(error))
                }
          })
      })
    }
}

问题是我有一个根 "data" 参数有时存在有时不存在。

我知道有一个解决方案,我可以创建一个结果编码模型,该模型将成为费用模型的父级,但这并没有达到我想要的效果,因为如果我有 20 个不同的模型会发生什么我必须创建 20 个不同的根模型吗?

是的,我可以使用 CodingKeys 来完成,但是对于这个简单的任务来说这有点老套而且代码太多了。

所以最好的方法是添加如下内容:

struct ExpensesList: Codable {
    var data: [Expense]
}

但对我来说,问题是我将始终拥有 'data' root,因此对于任何模型,我都会有一些 'List' 模型。

是否有更好的方法而不是 hacky 或者这是唯一的方法。

也许可以将子模型发送到一个数据模型,但是如何在视图中识别它,...?

提前致谢。

如果我理解正确,你可以使你的 Root 结构通用,并且还应该使 data 属性 可选。

struct Foo: Decodable {
    let foo: Int
}

struct Bar: Decodable {
    let bar: String
}

struct Root<T: Decodable>: Decodable {
    let data: [T]?
}

typealias FooList = Root<Foo>
typealias BarList = Root<Bar>

let fooData = """
{ "data": [ { "foo": 42 } ] }
""".data(using: .utf8)!

let barData = """
{ "data": [ { "bar": "baz" } ] }
""".data(using: .utf8)!

do {
    let foos = try JSONDecoder().decode(FooList.self, from: fooData)
    let bars = try JSONDecoder().decode(BarList.self, from: barData)
} catch {
    print(error)
}