Reputation: 107
Let's say I have the following JSON which I would like to decode to this a particular structure. How do I do this
JSON:
{
"fullName": "Federico Zanetello",
"id": 123456,
"twitter": "http://twitter.com/zntfdr",
"results": ["mate","bate"]
}
Decoded Struct
struct Response {
let data: UserData,
let results: [String]
}
The UserData
struct
Struct UserData {
let fullName: String,
let id: Int,
let twitter: String
}
I did my research and couldn't find a valid solution. Here is the code I wrote so far
struct UserData: Decodable {
let fullName: String
let id: Int
let twitter: String
enum CodingKeys: String, CodingKey {
case fullName
case id
case twitter
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.fullName = try container.decode(String.self, forKey: .fullName)
self.id = try container.decode(Int.self, forKey: .id)
self.twitter = try container.decode(String.self, forKey: .twitter)
}
}
struct Respone: Decodable{
let data: UserData
let results: [String]
}
let json = """
{
"fullName": "Federico Zanetello",
"id": 123456,
"twitter": "http://twitter.com/zntfdr",
"results": ["mate","bate"]
}
""".data(using: .utf8)! // our data in native (JSON) format
let myStruct = try JSONDecoder().decode(MetaData.self, from: json) // Decoding our data
print(myStruct)
I'm getting KeynotFound error because data
key is not present in the JSON. How do I solve this?
Upvotes: 1
Views: 831
Reputation: 270850
You can do something like this:
struct UserData: Decodable {
let fullName: String
let id: Int
let twitter: String
}
struct Response: Decodable{
let data: UserData
let results: [String]
enum CodingKeys : CodingKey {
case results
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
results = try container.decode([String].self, forKey: .results)
data = try decoder.singleValueContainer().decode(UserData.self)
}
}
The trick is to use custom decoding in Response
, and after decoding the results
like you do normally, get a singleValueContainer
. This container will contain all the keys in your JSON, so you can use this container to decode the UserData
.
Upvotes: 3
Reputation: 14397
You just need this struct.. Data is not present as a key in your response
// MARK: - User
struct User: Codable {
let fullName: String
let id: Int
let twitter: String
let results: [String]
}
Ad decode it like this
let myStruct = try JSONDecoder().decode(User.self, from: json)
Note: you dont need init()
function for codable if you are not performing anything extra.. And dont need CodingKeys
if names of your struct elements are same
Here is how you can use UserData
as separate object with interface segregation
struct Response: UserData, Codable {
let fullName: String
let id: Int
let twitter: String
let results: [String]
}
protocol UserData {
var fullName: String {get}
var id: Int {get}
var twitter: String{get}
}
let myStruct = try JSONDecoder().decode(Response.self, from: json)
let userData : UserData = myStruct
print(userData.fullName)
Upvotes: 1