I have this json
{
"status": [
{
"state": "checked",
"errorCode": "123",
"userId": "123456"
}
]
}
this is an array of statuses but is implemented badly because can be just one so I would like to decode just the status object
struct StatusResponse: Codable {
let state: String
let error: String
let id: String
enum CodingKeys: String, CodingKey {
case state = "state"
case error = "errorCode"
case id = "userId"
}
}
I try to custom decode it
let container = try decoder.container(keyedBy: ContainerKeys.self)
var statuses = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .status)
but as expected I get "Expected to decode Dictionary<String, Any> but found an array instead."
how I can have access to the first object from statuses variable and decode it into StatusResponse? or some other idea on how to procede?
CodePudding user response:
I would make a struct with field status
to represent the top level object. That field is an array of StatusResponse
:
struct TopLevelResponse: Codable {
var status: [StatusResponse]
}
when decoding the json:
let decoded = JSONDecoder().decode(TopLevelResponse.self, from: data)
let first = decoded.status.first! // Already decoded!
Unless it's guaranteed that there will be at least one item in the array then you should handle a nil case.
CodePudding user response:
I will go with this solution inspired by this answer:
fileprivate struct RawStatusResponse: Decodable {
let status: [RawStatus]
struct RawStatus: Decodable {
let state: String
let errorCode: String
let userId: String
}
}
struct StatusResponse: Codable {
let state: String
let error: String
let id: String
enum CodingKeys: String, CodingKey {
case state = "state"
case error = "errorCode"
case id = "userId"
}
public init(from decoder: Decoder) throws {
let raw = try RawStatusResponse(from: decoder)
state = raw.status.first!.state
error = raw.status.first!.errorCode
id = raw.status.first!.userId
}
}
then when decode it just decode the actual object:
let state = try JSONDecoder().decode(StatusResponse, from: json)
CodePudding user response:
You could decode it as a dictionary and use flatMap
to get the array
let status = try JSONDecoder().decode([String: [StatusResponse]].self, from: data).flatMap(\.value)