Reputation: 79
I am trying to parse a nested JSON string. Below are the JSON string, the structure I've created, and the JSON decoding function I am using. I am able to access the name member easily, but am having trouble with the other items. For instance, in my employeeData object, how can I access Sam's hours from the 3/31/2018 record?
Thank you in advance.
JSON String
[
{
"name": "John",
"records": [
{
"reportDate": "2018-06-30",
"hours": 204,
"billable": 32844
},
{
"reportDate": "2018-03-31",
"hours": 234,
"billable": 37715
}
]
},
{
"name": "Sam",
"records": [
{
"reportDate": "2018-06-30",
"hours": 187,
"billable": 13883
},
{
"reportDate": "2018-03-31",
"hours": 176,
"billable": 13467
}
]
}
]
Struct
struct Employee : Decodable {
let name : String?
let records : [Record]
private enum CodingKeys: String, CodingKey {
case name = "name"
case records
}
struct Record : Decodable {
let reportDate : String?
let hours : Int?
let billable : Int?
private enum CodingKeys: String, CodingKey {
case reportDate = "reportDate"
case hours = "hours"
case billable = "billable"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
reportDate = try values.decodeIfPresent(String.self, forKey: .reportDate)
hours = try values.decodeIfPresent(Int.self, forKey: .hours)
billable = try values.decodeIfPresent(Int.self, forKey: .billable)
}
}
}
JSON Decoding fiction
func downloadJSON( completed:@escaping ()->()){
guard let qurl = URL("https://website.com") else { return }
URLSession.shared.dataTask(with: qurl) { (data, response, error) in
if error == nil {
do{
self.employeeData = try JSONDecoder().decode([Employee].self, from: data!)
DispatchQueue.main.async{ completed() }
} catch { print("JSON Error") }
}
}.resume()
}
Upvotes: 1
Views: 592
Reputation: 285069
First of all you can reduce your structs to
struct Employee : Decodable {
let name : String
let records : [Record]
struct Record : Decodable {
let reportDate : String
let hours : Int
let billable : Int
}
}
The CodingKeys and the initializer are created by the protocol extension.
To get the inner data you need two loops
for employee in self.employeeData {
print(employee.name)
for record in employee.records {
print(record.reportDate)
print(record.hours)
}
}
To get Sam's hours at 2018-03-31 you could filter the data with
if let sam = self.employeeData.first(where: {$0.name == "Sam"}),
let date = sam.records.first(where: {$0.reportDate == "2018-03-31"}) {
print(date.hours)
}
Upvotes: 1