在iOS Swift中使用codable解析嵌套的json响应数据?

在iOS Swift中使用codable解析嵌套的json响应数据?,json,swift,xcode,nested,codable,Json,Swift,Xcode,Nested,Codable,我试图用swift 4中的可解码格式解析json数据。它打印零值。我找不到这是什么问题 以下是模型类: public struct TaskID: Decodable { let embedded: Embedded? let count: Int? enum CodingKeys: String, CodingKey { case count = "count" case embedded = "_embedded" } } public struct Embedd

我试图用swift 4中的可解码格式解析json数据。它打印零值。我找不到这是什么问题

以下是模型类:

public struct TaskID: Decodable {

let embedded: Embedded?
let count: Int?

enum CodingKeys: String, CodingKey {
    case count = "count"
    case embedded = "_embedded"
  }
}

 public struct Embedded: Decodable {


let task: [Task]?
enum CodingKeys: String, CodingKey {
       case task = "task"
   }

 }

public struct Task : Decodable {

let id : String?
let name: String?
let assignee: String?
let created: String?
let processDefinitionId: String?
enum CodingKeys: String, CodingKey {
    case id = "id"
    case name = "name"
    case assignee = "assignee"
    case created = "created"
    case processDefinitionId = "processDefinitionId"


 }

}
以下是json:

{
"_links": {
    "self": {
        "href": "/task"
    }
},
"_embedded": {

    "task": [
        {
            "_links": {
                "assignee": {
                    "href": "/user/demo"
                },
                "execution": {
                    "href": "/execution/1b64cf75-0616-11ea-8860-120ef5ab2c25"
                },
                "identityLink": {
                    "href": "/task/1b64f688-0616-11ea-8860-120ef5ab2c25/identity-links"
                },
                "processDefinition": {
                    "href": "/process-definition/quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25"
                },
                "processInstance": {
                    "href": "/process-instance/1b64cf75-0616-11ea-8860-120ef5ab2c25"
                },
                "self": {
                    "href": "/task/1b64f688-0616-11ea-8860-120ef5ab2c25"
                }
            },
            "_embedded": {
                "variable": []
            },
            "id": "1b64f688-0616-11ea-8860-120ef5ab2c25",
            "name": "Quick Evaluation",
            "assignee": "demo",
            "created": "2019-11-13T13:04:20.687+0000",
            "due": null,
            "followUp": null,
            "delegationState": null,
            "description": null,
            "executionId": "1b64cf75-0616-11ea-8860-120ef5ab2c25",
            "owner": null,
            "parentTaskId": null,
            "priority": 50,
            "processDefinitionId": "quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25",
            "processInstanceId": "1b64cf75-0616-11ea-8860-120ef5ab2c25",
            "taskDefinitionKey": "QuickEvaluation",
            "caseExecutionId": null,
            "caseInstanceId": null,
            "caseDefinitionId": null,
            "suspended": false,
            "formKey": "a8apps:suryoday:gng:v0.1.0:kycUpload",
            "tenantId": null
        },
        {
            "_links": {
                "assignee": {
                    "href": "/user/demo"
                },
                "execution": {
                    "href": "/execution/412a03b7-06ae-11ea-8860-120ef5ab2c25"
                },
                "identityLink": {
                    "href": "/task/412a2aca-06ae-11ea-8860-120ef5ab2c25/identity-links"
                },
                "processDefinition": {
                    "href": "/process-definition/quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25"
                },
                "processInstance": {
                    "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25"
                },
                "self": {
                    "href": "/task/412a2aca-06ae-11ea-8860-120ef5ab2c25"
                }
            },
            "_embedded": {
                "variable": [
                    {
                        "_links": {
                            "self": {
                                "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25/variables/loanAmount"
                            }
                        },
                        "_embedded": null,
                        "name": "loanAmount",
                        "value": "650000",
                        "type": "String",
                        "valueInfo": {}
                    },
                    {
                        "_links": {
                            "self": {
                                "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25/variables/firstName"
                            }
                        },
                        "_embedded": null,
                        "name": "firstName",
                        "value": "Kamesh",
                        "type": "String",
                        "valueInfo": {}
                    }
                ]
            },
            "id": "412a2aca-06ae-11ea-8860-120ef5ab2c25",
            "name": "Quick Evaluation",
            "assignee": "demo",
            "created": "2019-11-14T07:13:27.558+0000",
            "due": null,
            "followUp": null,
            "delegationState": null,
            "description": null,
            "executionId": "412a03b7-06ae-11ea-8860-120ef5ab2c25",
            "owner": null,
            "parentTaskId": null,
            "priority": 50,
            "processDefinitionId": "quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25",
            "processInstanceId": "412a03b7-06ae-11ea-8860-120ef5ab2c25",
            "taskDefinitionKey": "QuickEvaluation",
            "caseExecutionId": null,
            "caseInstanceId": null,
            "caseDefinitionId": null,
            "suspended": false,
            "formKey": "a8apps:suryoday:gng:v0.1.0:kycUpload",
            "tenantId": null
        }

    ]
},
"count": 13
}
这是您的请求:

 // MARK: - URLRequestConvertible
func asURLRequest() throws -> URLRequest {
    let url = try K.ProductionServer.baseURL.asURL()

    var urlRequest = URLRequest(url: url.appendingPathComponent(path))
    print(urlRequest)
    // HTTP Method
    urlRequest.httpMethod = method.rawValue

    let authToken = UserDefaults.standard.string(forKey: "authToken")
    let bearerToken: String = "Bearer " + (authToken ?? "")
    print("baearer token::\(bearerToken)")

    // Common Headers
    urlRequest.setValue(ContentType.json.rawValue, forHTTPHeaderField: HTTPHeaderField.acceptType.rawValue)
    urlRequest.setValue(ContentType.json.rawValue, forHTTPHeaderField: HTTPHeaderField.contentType.rawValue)
    urlRequest.setValue(bearerToken, forHTTPHeaderField: HTTPHeaderField.authentication.rawValue)

    // Parameters
    if let parameters = parameters {
        do {
            urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
        } catch {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }
    }

    return urlRequest
}
以下是alamofire请求:

 import Foundation
import Alamofire

public class APIClient {
@discardableResult
private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:@escaping (AFResult<T>)->Void) -> DataRequest {

    return AF.request(route)
                    .responseDecodable (decoder: decoder){ (response: AFDataResponse<T>) in
                        completion(response.result)
                        print("framework response::",response.result)
    }
}

public static func taskID(id: String, completion:@escaping (AFResult< [TaskID]>)->Void) {

    performRequest(route: APIRouter.TaskById(id: id), completion: completion)
}


}//APIClient
<代码>导入基础 进口阿拉莫菲尔 公共类客户端{ @可丢弃结果 私有静态func performRequest(路由:APIRouter,解码器:JSONDecoder=JSONDecoder(),完成:@escaping(AFResult)->Void)->DataRequest{ 返回AF.request(路由) .responseDecodable(解码器:解码器){(响应:AFDataResponse)在 完成(响应、结果) 打印(“框架响应::”,response.result) } } public static func taskID(id:String,completion:@escaping(AFResult<[taskID]>)->Void){ performRequest(路由:APIRouter.TaskById(id:id),完成:完成) } }//APIClient 最初它显示的是字典,Swift.DecodingError.Context(codingPath:[],debugDescription:“本应解码字典,但却找到了一个数组。但现在我在控制台日志中得到了nil。不知道为什么会得到nil值。根据我的json响应,struct是否正确?我正在努力获取嵌套数据


非常感谢您的任何帮助。

您的结构是正确的。请参阅下面的游乐场以获得证明;我没有得到错误。
nil
是非常常见的结果,当您没有完整的数据集,并且您假设响应中的某个可选字段是非可选的,因为您在示例数据中看到它(您的样品不一定具有代表性)。如果服务器没有实际的规范,您需要找出哪些字段无法解码,并且可能需要获取一组数据以确定哪些字段是真正可选的。您可以通过在上面的AF项目中输入错误处理代码或将响应粘贴到下面的“我的游乐场”中来完成此操作。无论哪种方式,解码ing错误应该告诉您哪个字段不存在

import PlaygroundSupport
import UIKit
import WebKit


public struct TaskID: Decodable {

let embedded: Embedded?
let count: Int?

enum CodingKeys: String, CodingKey {
    case count = "count"
    case embedded = "_embedded"
  }
}

 public struct Embedded: Decodable {


let task: [Task]?
enum CodingKeys: String, CodingKey {
       case task = "task"
   }

 }

public struct Task : Decodable {

let id : String?
let name: String?
let assignee: String?
let created: String?
let processDefinitionId: String?
enum CodingKeys: String, CodingKey {
    case id = "id"
    case name = "name"
    case assignee = "assignee"
    case created = "created"
    case processDefinitionId = "processDefinitionId"


 }

}


let data = """
{
"_links": {
    "self": {
        "href": "/task"
    }
},
"_embedded": {

    "task": [
        {
            "_links": {
                "assignee": {
                    "href": "/user/demo"
                },
                "execution": {
                    "href": "/execution/1b64cf75-0616-11ea-8860-120ef5ab2c25"
                },
                "identityLink": {
                    "href": "/task/1b64f688-0616-11ea-8860-120ef5ab2c25/identity-links"
                },
                "processDefinition": {
                    "href": "/process-definition/quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25"
                },
                "processInstance": {
                    "href": "/process-instance/1b64cf75-0616-11ea-8860-120ef5ab2c25"
                },
                "self": {
                    "href": "/task/1b64f688-0616-11ea-8860-120ef5ab2c25"
                }
            },
            "_embedded": {
                "variable": []
            },
            "id": "1b64f688-0616-11ea-8860-120ef5ab2c25",
            "name": "Quick Evaluation",
            "assignee": "demo",
            "created": "2019-11-13T13:04:20.687+0000",
            "due": null,
            "followUp": null,
            "delegationState": null,
            "description": null,
            "executionId": "1b64cf75-0616-11ea-8860-120ef5ab2c25",
            "owner": null,
            "parentTaskId": null,
            "priority": 50,
            "processDefinitionId": "quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25",
            "processInstanceId": "1b64cf75-0616-11ea-8860-120ef5ab2c25",
            "taskDefinitionKey": "QuickEvaluation",
            "caseExecutionId": null,
            "caseInstanceId": null,
            "caseDefinitionId": null,
            "suspended": false,
            "formKey": "a8apps:suryoday:gng:v0.1.0:kycUpload",
            "tenantId": null
        },
        {
            "_links": {
                "assignee": {
                    "href": "/user/demo"
                },
                "execution": {
                    "href": "/execution/412a03b7-06ae-11ea-8860-120ef5ab2c25"
                },
                "identityLink": {
                    "href": "/task/412a2aca-06ae-11ea-8860-120ef5ab2c25/identity-links"
                },
                "processDefinition": {
                    "href": "/process-definition/quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25"
                },
                "processInstance": {
                    "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25"
                },
                "self": {
                    "href": "/task/412a2aca-06ae-11ea-8860-120ef5ab2c25"
                }
            },
            "_embedded": {
                "variable": [
                    {
                        "_links": {
                            "self": {
                                "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25/variables/loanAmount"
                            }
                        },
                        "_embedded": null,
                        "name": "loanAmount",
                        "value": "650000",
                        "type": "String",
                        "valueInfo": {}
                    },
                    {
                        "_links": {
                            "self": {
                                "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25/variables/firstName"
                            }
                        },
                        "_embedded": null,
                        "name": "firstName",
                        "value": "Kamesh",
                        "type": "String",
                        "valueInfo": {}
                    }
                ]
            },
            "id": "412a2aca-06ae-11ea-8860-120ef5ab2c25",
            "name": "Quick Evaluation",
            "assignee": "demo",
            "created": "2019-11-14T07:13:27.558+0000",
            "due": null,
            "followUp": null,
            "delegationState": null,
            "description": null,
            "executionId": "412a03b7-06ae-11ea-8860-120ef5ab2c25",
            "owner": null,
            "parentTaskId": null,
            "priority": 50,
            "processDefinitionId": "quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25",
            "processInstanceId": "412a03b7-06ae-11ea-8860-120ef5ab2c25",
            "taskDefinitionKey": "QuickEvaluation",
            "caseExecutionId": null,
            "caseInstanceId": null,
            "caseDefinitionId": null,
            "suspended": false,
            "formKey": "a8apps:suryoday:gng:v0.1.0:kycUpload",
            "tenantId": null
        }

    ]
},
"count": 13
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
do {
  let decoded = try decoder.decode(TaskID.self, from: data)
  print(decoded)
} catch ( let error ) {
  print(error.localizedDescription)
}

此外,如果您感到懒惰,您可以使用,因为
nil
是来自decodable的一个非常常见的响应,您可以使用DataRequest的扩展来解析请求并从请求中获取值,就像使用
responseDecodable
,这里您可以使用类似的方法

 extension DataRequest {
    fileprivate func decodableResponseSerializer<T: Decodable>() -> DataResponseSerializer<T> {
        return DataResponseSerializer { _, response, data, error in
            guard error == nil else { return .failure(error!) }

            guard let data = data else {
                return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
            }

            return Result { try newJSONDecoder().decode(T.self, from: data) }
        }
    }

    @discardableResult
    fileprivate func responseDecodable<T: Decodable>(queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<T>) -> Void) -> Self {
        return response(queue: queue, responseSerializer: decodableResponseSerializer(), completionHandler: completionHandler)
    }
}
扩展数据请求{
fileprivate func decodableResponseSerializer()->DataResponseSerializer{
返回DataResponseSerializer{},响应,数据,中的错误
guard error==nil else{return.failure(error!)}
guard let data=其他数据{
return.failure(AFError.responseSerialization失败(原因:.inputDataNil))
}
返回结果{try newJSONDecoder().decode(T.self,from:data)}
}
}
@可丢弃结果
fileprivate func responseDecodable(队列:DispatchQueue?=nil,completionHandler:@escaping(DataResponse)->Void)->Self{
返回响应(队列:队列,响应序列化程序:decodableResponseSerializer(),completionHandler:completionHandler)
}
}
然后你可以这样称呼它

Alamofire.request(url, method:.post, parameters: parameters, headers: headers)
        .responseDecodable { (response: DataResponse< Task >) in completion(response.result) }
Alamofire.request(url,方法:.post,参数:参数,标题:标题)
.responseDecodable{(响应:DataResponse)正在完成(response.result)}

当您得到类似的结果时,只有当您有不同类型的数据而不是您指定的数据时,才会出现此问题。

谢谢您的回答。.我怀疑我是否需要传入数组或类。func taskID(id:String,completion:@escaping(AFResult)->void仍然面临相同的问题我也有url请求代码。你能检查一下吗包装器是
TaskID
,就像我的例子一样。不用使用AF ResponsedCodeTable,只需要获取数据并自己解码,这样你就可以捕捉解码错误并找出哪个字段失败了。这和另外两个任务有什么区别你最近几天发布的离子?它们看起来非常相似。@JoakimDanielson最后两个问题我有编码键,希望解码字典,但找到了一个数组。但是现在我得到了零值。你能帮我解决这个问题吗this@JoakimDanielson现在我有了url请求代码,但我和其他人已经试过帮助你一次了对于你的json,你似乎仍然在为同样的事情而挣扎。我并不认为这有什么意义,因为没有什么真正的改变。你需要用你的代码来区分可能的问题,实际的json解码(这很好)以及您需要的AF和ApiClient类have@JoakimDanielson,好的,我有一个疑问,我是否需要在数组中传递结构名,还是只传递类名。公共静态func taskID(id:String,completion:@escaping(AFResult<[taskID]>)如果我传递taskID,它会显示错误,预期会解码字典,但找到数组。如果我改为数组[taskID]它打印零值。
Alamofire.request(url, method:.post, parameters: parameters, headers: headers)
        .responseDecodable { (response: DataResponse< Task >) in completion(response.result) }