Parsing JSON with decodable issue
I'm trying to parse JSON schedule data of weekdays and every weekday has array of different events/schedules that repeat each weak. So i have a data array that has weekday objects from monday to sunday and weekday has array of events/schedules.
struct Scheduledata: Decodable {
let data: [WeekDay]
}
struct WeekDay: Decodable {
let monday, tuesday, wednesday, thursday, friday, saturday, sunday : [Schedule]?
}
struct Schedule: Decodable {
let id: Int?
let start: String?
let end: String?
let address: String?
let name: String?
let text: String?
let imageURL: String?
let location: CLLocationCoordinate2D?
enum CodingKeys: String, CodingKey {
case text, location, start, end, name, address, id
case imageURL = "image_url"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(Int.self, forKey: .id)
text = try container.decodeIfPresent(String.self, forKey: .text)
imageURL = try container.decodeIfPresent(String.self, forKey: .imageURL)
location = try container.decodeIfPresent(CLLocationCoordinate2D.self, forKey: .location)
start = try container.decodeIfPresent(String.self, forKey: .start)
end = try container.decodeIfPresent(String.self, forKey: .end)
address = try container.decodeIfPresent(String.self, forKey: .address)
name = try container.decodeIfPresent(String.self, forKey: .name)
}
}
extension CLLocationCoordinate2D: Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.init()
longitude = try container.decode(Double.self)
latitude = try container.decode(Double.self)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(longitude)
try container.encode(latitude)
}
}
This is the json object I'm trying to parse
{
"data": [
{
"monday":
},
{
"tuesday": [
{
"id": 1,
"day_id": 2,
"start": "16:30",
"end": "21:00",
"name": "Test Event",
"address": "6 mohamed galal street cairo, heliopolis",
"lat": "30.0866280",
"long": "31.3236130",
"image": "http://80.211.174.200/img/event/1542547661.jpeg",
"title": "Test_Event",
"description": "This is just a test event to test the testable testi test test testit test............................. yes this is a test indeed",
"created_at": "2018-11-18 15:27:41",
"updated_at": "2018-11-18 15:27:41"
}
]
},
{
"wednesday":
},
{
"thursday":
},
{
"friday":
},
{
"saturday":
},
{
"sunday":
}
]
}
What I'm expecting is a dictionary:
var schedule = ["monday":[schedule], "tuesday":[schedule], ...]
What I'm getting seems like an array of dictionaries. I have only one day in each weekday object not all days of the week.
var schedule = [["monday":[schedule], "tuesday":[schedule], ...],["monday":[schedule], "tuesday":[schedule], ...]]
So how can i do it? I create a different struct for each day instead of the weekday struct? Doesn't seem logic. Something is not just right. I'm sure there is a smarter solution for parsing this.
json swift codable decodable
add a comment |
I'm trying to parse JSON schedule data of weekdays and every weekday has array of different events/schedules that repeat each weak. So i have a data array that has weekday objects from monday to sunday and weekday has array of events/schedules.
struct Scheduledata: Decodable {
let data: [WeekDay]
}
struct WeekDay: Decodable {
let monday, tuesday, wednesday, thursday, friday, saturday, sunday : [Schedule]?
}
struct Schedule: Decodable {
let id: Int?
let start: String?
let end: String?
let address: String?
let name: String?
let text: String?
let imageURL: String?
let location: CLLocationCoordinate2D?
enum CodingKeys: String, CodingKey {
case text, location, start, end, name, address, id
case imageURL = "image_url"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(Int.self, forKey: .id)
text = try container.decodeIfPresent(String.self, forKey: .text)
imageURL = try container.decodeIfPresent(String.self, forKey: .imageURL)
location = try container.decodeIfPresent(CLLocationCoordinate2D.self, forKey: .location)
start = try container.decodeIfPresent(String.self, forKey: .start)
end = try container.decodeIfPresent(String.self, forKey: .end)
address = try container.decodeIfPresent(String.self, forKey: .address)
name = try container.decodeIfPresent(String.self, forKey: .name)
}
}
extension CLLocationCoordinate2D: Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.init()
longitude = try container.decode(Double.self)
latitude = try container.decode(Double.self)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(longitude)
try container.encode(latitude)
}
}
This is the json object I'm trying to parse
{
"data": [
{
"monday":
},
{
"tuesday": [
{
"id": 1,
"day_id": 2,
"start": "16:30",
"end": "21:00",
"name": "Test Event",
"address": "6 mohamed galal street cairo, heliopolis",
"lat": "30.0866280",
"long": "31.3236130",
"image": "http://80.211.174.200/img/event/1542547661.jpeg",
"title": "Test_Event",
"description": "This is just a test event to test the testable testi test test testit test............................. yes this is a test indeed",
"created_at": "2018-11-18 15:27:41",
"updated_at": "2018-11-18 15:27:41"
}
]
},
{
"wednesday":
},
{
"thursday":
},
{
"friday":
},
{
"saturday":
},
{
"sunday":
}
]
}
What I'm expecting is a dictionary:
var schedule = ["monday":[schedule], "tuesday":[schedule], ...]
What I'm getting seems like an array of dictionaries. I have only one day in each weekday object not all days of the week.
var schedule = [["monday":[schedule], "tuesday":[schedule], ...],["monday":[schedule], "tuesday":[schedule], ...]]
So how can i do it? I create a different struct for each day instead of the weekday struct? Doesn't seem logic. Something is not just right. I'm sure there is a smarter solution for parsing this.
json swift codable decodable
add a comment |
I'm trying to parse JSON schedule data of weekdays and every weekday has array of different events/schedules that repeat each weak. So i have a data array that has weekday objects from monday to sunday and weekday has array of events/schedules.
struct Scheduledata: Decodable {
let data: [WeekDay]
}
struct WeekDay: Decodable {
let monday, tuesday, wednesday, thursday, friday, saturday, sunday : [Schedule]?
}
struct Schedule: Decodable {
let id: Int?
let start: String?
let end: String?
let address: String?
let name: String?
let text: String?
let imageURL: String?
let location: CLLocationCoordinate2D?
enum CodingKeys: String, CodingKey {
case text, location, start, end, name, address, id
case imageURL = "image_url"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(Int.self, forKey: .id)
text = try container.decodeIfPresent(String.self, forKey: .text)
imageURL = try container.decodeIfPresent(String.self, forKey: .imageURL)
location = try container.decodeIfPresent(CLLocationCoordinate2D.self, forKey: .location)
start = try container.decodeIfPresent(String.self, forKey: .start)
end = try container.decodeIfPresent(String.self, forKey: .end)
address = try container.decodeIfPresent(String.self, forKey: .address)
name = try container.decodeIfPresent(String.self, forKey: .name)
}
}
extension CLLocationCoordinate2D: Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.init()
longitude = try container.decode(Double.self)
latitude = try container.decode(Double.self)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(longitude)
try container.encode(latitude)
}
}
This is the json object I'm trying to parse
{
"data": [
{
"monday":
},
{
"tuesday": [
{
"id": 1,
"day_id": 2,
"start": "16:30",
"end": "21:00",
"name": "Test Event",
"address": "6 mohamed galal street cairo, heliopolis",
"lat": "30.0866280",
"long": "31.3236130",
"image": "http://80.211.174.200/img/event/1542547661.jpeg",
"title": "Test_Event",
"description": "This is just a test event to test the testable testi test test testit test............................. yes this is a test indeed",
"created_at": "2018-11-18 15:27:41",
"updated_at": "2018-11-18 15:27:41"
}
]
},
{
"wednesday":
},
{
"thursday":
},
{
"friday":
},
{
"saturday":
},
{
"sunday":
}
]
}
What I'm expecting is a dictionary:
var schedule = ["monday":[schedule], "tuesday":[schedule], ...]
What I'm getting seems like an array of dictionaries. I have only one day in each weekday object not all days of the week.
var schedule = [["monday":[schedule], "tuesday":[schedule], ...],["monday":[schedule], "tuesday":[schedule], ...]]
So how can i do it? I create a different struct for each day instead of the weekday struct? Doesn't seem logic. Something is not just right. I'm sure there is a smarter solution for parsing this.
json swift codable decodable
I'm trying to parse JSON schedule data of weekdays and every weekday has array of different events/schedules that repeat each weak. So i have a data array that has weekday objects from monday to sunday and weekday has array of events/schedules.
struct Scheduledata: Decodable {
let data: [WeekDay]
}
struct WeekDay: Decodable {
let monday, tuesday, wednesday, thursday, friday, saturday, sunday : [Schedule]?
}
struct Schedule: Decodable {
let id: Int?
let start: String?
let end: String?
let address: String?
let name: String?
let text: String?
let imageURL: String?
let location: CLLocationCoordinate2D?
enum CodingKeys: String, CodingKey {
case text, location, start, end, name, address, id
case imageURL = "image_url"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(Int.self, forKey: .id)
text = try container.decodeIfPresent(String.self, forKey: .text)
imageURL = try container.decodeIfPresent(String.self, forKey: .imageURL)
location = try container.decodeIfPresent(CLLocationCoordinate2D.self, forKey: .location)
start = try container.decodeIfPresent(String.self, forKey: .start)
end = try container.decodeIfPresent(String.self, forKey: .end)
address = try container.decodeIfPresent(String.self, forKey: .address)
name = try container.decodeIfPresent(String.self, forKey: .name)
}
}
extension CLLocationCoordinate2D: Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.init()
longitude = try container.decode(Double.self)
latitude = try container.decode(Double.self)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(longitude)
try container.encode(latitude)
}
}
This is the json object I'm trying to parse
{
"data": [
{
"monday":
},
{
"tuesday": [
{
"id": 1,
"day_id": 2,
"start": "16:30",
"end": "21:00",
"name": "Test Event",
"address": "6 mohamed galal street cairo, heliopolis",
"lat": "30.0866280",
"long": "31.3236130",
"image": "http://80.211.174.200/img/event/1542547661.jpeg",
"title": "Test_Event",
"description": "This is just a test event to test the testable testi test test testit test............................. yes this is a test indeed",
"created_at": "2018-11-18 15:27:41",
"updated_at": "2018-11-18 15:27:41"
}
]
},
{
"wednesday":
},
{
"thursday":
},
{
"friday":
},
{
"saturday":
},
{
"sunday":
}
]
}
What I'm expecting is a dictionary:
var schedule = ["monday":[schedule], "tuesday":[schedule], ...]
What I'm getting seems like an array of dictionaries. I have only one day in each weekday object not all days of the week.
var schedule = [["monday":[schedule], "tuesday":[schedule], ...],["monday":[schedule], "tuesday":[schedule], ...]]
So how can i do it? I create a different struct for each day instead of the weekday struct? Doesn't seem logic. Something is not just right. I'm sure there is a smarter solution for parsing this.
json swift codable decodable
json swift codable decodable
edited Nov 25 '18 at 0:49
Kegham K.
asked Nov 25 '18 at 0:43
Kegham K.Kegham K.
9311023
9311023
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
Your code has 2 problems:
1: you declared many properties as Optional. When you do that, you hide the errors away. You want this to succeed or to fail during testing so you know where to debug, not sweeping it under the rugs with Optionals.
struct WeekDay: Decodable {
let monday, tuesday, ... : [Schedule]?
}
struct Schedule: Decodable {
let id: Int?
let start: String?
let end: String?
let address: String?
...
}
2: your JSON is really hard to work with. If you have control of the server side, please change that to:
{
"data": [
"monday": ,
"tuesday": [{...}, {...}, ...],
"wednesday": ,
"thursday": ,
"friday": ,
"saturday": ,
"sunday": ,
]
}
Assuming you can't change the JSON, here's how you can decode the bad JSON:
import Foundation
import CoreLocation
struct WeeklySchedule: Decodable {
let monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]
private enum CodingKeys: String, CodingKey {
case data
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Get the content of the `data` key from JSON
var subContainer = try container.nestedUnkeyedContainer(forKey: .data)
// Since you declare `monday`, etc. as `let` properties, they cannot
// be assigned multiple time. And the compiler does not know how
// many times the variable will be assigned to a `while` loop. So we
// need to define some local variables to temporarily hold the values.
var monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]!
while !subContainer.isAtEnd {
let dailySchedule = try subContainer.decode([String: [Schedule]].self)
// The `first` value of a dictionary is non-deterministic since
// dictionaries do not have an order. But if the dictionary
// contains more than one weekday, there's a problem with the
// JSON anyway.
guard let (weekday, schedule) = dailySchedule.first else { continue }
switch weekday {
case "monday": monday = schedule
case "tuesday": tuesday = schedule
case "wednesday": wednesday = schedule
case "thursday": thursday = schedule
case "friday": friday = schedule
case "saturday": saturday = schedule
case "sunday": sunday = schedule
default: break
}
}
self.monday = monday
self.tuesday = tuesday
self.wednesday = wednesday
self.thursday = thursday
self.friday = friday
self.saturday = saturday
self.sunday = sunday
}
}
struct Schedule: Decodable {
let id: Int
let start: String
let end: String
let address: String
let name: String
let text: String
let imageURL: URL
let location: CLLocationCoordinate2D
private enum CodingKeys: String, CodingKey {
case start, end, name, address, id
case imageURL = "image", text = "description"
case lat, long
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.start = try container.decode(String.self, forKey: .start)
self.end = try container.decode(String.self, forKey: .end)
self.address = try container.decode(String.self, forKey: .address)
self.name = try container.decode(String.self, forKey: .name)
self.text = try container.decode(String.self, forKey: .text)
self.imageURL = try container.decode(URL.self, forKey: .imageURL)
let latStr = try container.decode(String.self, forKey: .lat)
let longStr = try container.decode(String.self, forKey: .long)
guard let lat = CLLocationDegrees(latStr), let long = CLLocationDegrees(longStr) else {
fatalError("lat / long is not a number")
}
self.location = CLLocationCoordinate2D(latitude: lat, longitude: long)
}
}
let weeklySchedule = try JSONDecoder().decode(WeeklySchedule.self, from: jsonData)
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53463724%2fparsing-json-with-decodable-issue%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Your code has 2 problems:
1: you declared many properties as Optional. When you do that, you hide the errors away. You want this to succeed or to fail during testing so you know where to debug, not sweeping it under the rugs with Optionals.
struct WeekDay: Decodable {
let monday, tuesday, ... : [Schedule]?
}
struct Schedule: Decodable {
let id: Int?
let start: String?
let end: String?
let address: String?
...
}
2: your JSON is really hard to work with. If you have control of the server side, please change that to:
{
"data": [
"monday": ,
"tuesday": [{...}, {...}, ...],
"wednesday": ,
"thursday": ,
"friday": ,
"saturday": ,
"sunday": ,
]
}
Assuming you can't change the JSON, here's how you can decode the bad JSON:
import Foundation
import CoreLocation
struct WeeklySchedule: Decodable {
let monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]
private enum CodingKeys: String, CodingKey {
case data
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Get the content of the `data` key from JSON
var subContainer = try container.nestedUnkeyedContainer(forKey: .data)
// Since you declare `monday`, etc. as `let` properties, they cannot
// be assigned multiple time. And the compiler does not know how
// many times the variable will be assigned to a `while` loop. So we
// need to define some local variables to temporarily hold the values.
var monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]!
while !subContainer.isAtEnd {
let dailySchedule = try subContainer.decode([String: [Schedule]].self)
// The `first` value of a dictionary is non-deterministic since
// dictionaries do not have an order. But if the dictionary
// contains more than one weekday, there's a problem with the
// JSON anyway.
guard let (weekday, schedule) = dailySchedule.first else { continue }
switch weekday {
case "monday": monday = schedule
case "tuesday": tuesday = schedule
case "wednesday": wednesday = schedule
case "thursday": thursday = schedule
case "friday": friday = schedule
case "saturday": saturday = schedule
case "sunday": sunday = schedule
default: break
}
}
self.monday = monday
self.tuesday = tuesday
self.wednesday = wednesday
self.thursday = thursday
self.friday = friday
self.saturday = saturday
self.sunday = sunday
}
}
struct Schedule: Decodable {
let id: Int
let start: String
let end: String
let address: String
let name: String
let text: String
let imageURL: URL
let location: CLLocationCoordinate2D
private enum CodingKeys: String, CodingKey {
case start, end, name, address, id
case imageURL = "image", text = "description"
case lat, long
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.start = try container.decode(String.self, forKey: .start)
self.end = try container.decode(String.self, forKey: .end)
self.address = try container.decode(String.self, forKey: .address)
self.name = try container.decode(String.self, forKey: .name)
self.text = try container.decode(String.self, forKey: .text)
self.imageURL = try container.decode(URL.self, forKey: .imageURL)
let latStr = try container.decode(String.self, forKey: .lat)
let longStr = try container.decode(String.self, forKey: .long)
guard let lat = CLLocationDegrees(latStr), let long = CLLocationDegrees(longStr) else {
fatalError("lat / long is not a number")
}
self.location = CLLocationCoordinate2D(latitude: lat, longitude: long)
}
}
let weeklySchedule = try JSONDecoder().decode(WeeklySchedule.self, from: jsonData)
add a comment |
Your code has 2 problems:
1: you declared many properties as Optional. When you do that, you hide the errors away. You want this to succeed or to fail during testing so you know where to debug, not sweeping it under the rugs with Optionals.
struct WeekDay: Decodable {
let monday, tuesday, ... : [Schedule]?
}
struct Schedule: Decodable {
let id: Int?
let start: String?
let end: String?
let address: String?
...
}
2: your JSON is really hard to work with. If you have control of the server side, please change that to:
{
"data": [
"monday": ,
"tuesday": [{...}, {...}, ...],
"wednesday": ,
"thursday": ,
"friday": ,
"saturday": ,
"sunday": ,
]
}
Assuming you can't change the JSON, here's how you can decode the bad JSON:
import Foundation
import CoreLocation
struct WeeklySchedule: Decodable {
let monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]
private enum CodingKeys: String, CodingKey {
case data
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Get the content of the `data` key from JSON
var subContainer = try container.nestedUnkeyedContainer(forKey: .data)
// Since you declare `monday`, etc. as `let` properties, they cannot
// be assigned multiple time. And the compiler does not know how
// many times the variable will be assigned to a `while` loop. So we
// need to define some local variables to temporarily hold the values.
var monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]!
while !subContainer.isAtEnd {
let dailySchedule = try subContainer.decode([String: [Schedule]].self)
// The `first` value of a dictionary is non-deterministic since
// dictionaries do not have an order. But if the dictionary
// contains more than one weekday, there's a problem with the
// JSON anyway.
guard let (weekday, schedule) = dailySchedule.first else { continue }
switch weekday {
case "monday": monday = schedule
case "tuesday": tuesday = schedule
case "wednesday": wednesday = schedule
case "thursday": thursday = schedule
case "friday": friday = schedule
case "saturday": saturday = schedule
case "sunday": sunday = schedule
default: break
}
}
self.monday = monday
self.tuesday = tuesday
self.wednesday = wednesday
self.thursday = thursday
self.friday = friday
self.saturday = saturday
self.sunday = sunday
}
}
struct Schedule: Decodable {
let id: Int
let start: String
let end: String
let address: String
let name: String
let text: String
let imageURL: URL
let location: CLLocationCoordinate2D
private enum CodingKeys: String, CodingKey {
case start, end, name, address, id
case imageURL = "image", text = "description"
case lat, long
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.start = try container.decode(String.self, forKey: .start)
self.end = try container.decode(String.self, forKey: .end)
self.address = try container.decode(String.self, forKey: .address)
self.name = try container.decode(String.self, forKey: .name)
self.text = try container.decode(String.self, forKey: .text)
self.imageURL = try container.decode(URL.self, forKey: .imageURL)
let latStr = try container.decode(String.self, forKey: .lat)
let longStr = try container.decode(String.self, forKey: .long)
guard let lat = CLLocationDegrees(latStr), let long = CLLocationDegrees(longStr) else {
fatalError("lat / long is not a number")
}
self.location = CLLocationCoordinate2D(latitude: lat, longitude: long)
}
}
let weeklySchedule = try JSONDecoder().decode(WeeklySchedule.self, from: jsonData)
add a comment |
Your code has 2 problems:
1: you declared many properties as Optional. When you do that, you hide the errors away. You want this to succeed or to fail during testing so you know where to debug, not sweeping it under the rugs with Optionals.
struct WeekDay: Decodable {
let monday, tuesday, ... : [Schedule]?
}
struct Schedule: Decodable {
let id: Int?
let start: String?
let end: String?
let address: String?
...
}
2: your JSON is really hard to work with. If you have control of the server side, please change that to:
{
"data": [
"monday": ,
"tuesday": [{...}, {...}, ...],
"wednesday": ,
"thursday": ,
"friday": ,
"saturday": ,
"sunday": ,
]
}
Assuming you can't change the JSON, here's how you can decode the bad JSON:
import Foundation
import CoreLocation
struct WeeklySchedule: Decodable {
let monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]
private enum CodingKeys: String, CodingKey {
case data
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Get the content of the `data` key from JSON
var subContainer = try container.nestedUnkeyedContainer(forKey: .data)
// Since you declare `monday`, etc. as `let` properties, they cannot
// be assigned multiple time. And the compiler does not know how
// many times the variable will be assigned to a `while` loop. So we
// need to define some local variables to temporarily hold the values.
var monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]!
while !subContainer.isAtEnd {
let dailySchedule = try subContainer.decode([String: [Schedule]].self)
// The `first` value of a dictionary is non-deterministic since
// dictionaries do not have an order. But if the dictionary
// contains more than one weekday, there's a problem with the
// JSON anyway.
guard let (weekday, schedule) = dailySchedule.first else { continue }
switch weekday {
case "monday": monday = schedule
case "tuesday": tuesday = schedule
case "wednesday": wednesday = schedule
case "thursday": thursday = schedule
case "friday": friday = schedule
case "saturday": saturday = schedule
case "sunday": sunday = schedule
default: break
}
}
self.monday = monday
self.tuesday = tuesday
self.wednesday = wednesday
self.thursday = thursday
self.friday = friday
self.saturday = saturday
self.sunday = sunday
}
}
struct Schedule: Decodable {
let id: Int
let start: String
let end: String
let address: String
let name: String
let text: String
let imageURL: URL
let location: CLLocationCoordinate2D
private enum CodingKeys: String, CodingKey {
case start, end, name, address, id
case imageURL = "image", text = "description"
case lat, long
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.start = try container.decode(String.self, forKey: .start)
self.end = try container.decode(String.self, forKey: .end)
self.address = try container.decode(String.self, forKey: .address)
self.name = try container.decode(String.self, forKey: .name)
self.text = try container.decode(String.self, forKey: .text)
self.imageURL = try container.decode(URL.self, forKey: .imageURL)
let latStr = try container.decode(String.self, forKey: .lat)
let longStr = try container.decode(String.self, forKey: .long)
guard let lat = CLLocationDegrees(latStr), let long = CLLocationDegrees(longStr) else {
fatalError("lat / long is not a number")
}
self.location = CLLocationCoordinate2D(latitude: lat, longitude: long)
}
}
let weeklySchedule = try JSONDecoder().decode(WeeklySchedule.self, from: jsonData)
Your code has 2 problems:
1: you declared many properties as Optional. When you do that, you hide the errors away. You want this to succeed or to fail during testing so you know where to debug, not sweeping it under the rugs with Optionals.
struct WeekDay: Decodable {
let monday, tuesday, ... : [Schedule]?
}
struct Schedule: Decodable {
let id: Int?
let start: String?
let end: String?
let address: String?
...
}
2: your JSON is really hard to work with. If you have control of the server side, please change that to:
{
"data": [
"monday": ,
"tuesday": [{...}, {...}, ...],
"wednesday": ,
"thursday": ,
"friday": ,
"saturday": ,
"sunday": ,
]
}
Assuming you can't change the JSON, here's how you can decode the bad JSON:
import Foundation
import CoreLocation
struct WeeklySchedule: Decodable {
let monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]
private enum CodingKeys: String, CodingKey {
case data
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Get the content of the `data` key from JSON
var subContainer = try container.nestedUnkeyedContainer(forKey: .data)
// Since you declare `monday`, etc. as `let` properties, they cannot
// be assigned multiple time. And the compiler does not know how
// many times the variable will be assigned to a `while` loop. So we
// need to define some local variables to temporarily hold the values.
var monday, tuesday, wednesday, thursday, friday, saturday, sunday: [Schedule]!
while !subContainer.isAtEnd {
let dailySchedule = try subContainer.decode([String: [Schedule]].self)
// The `first` value of a dictionary is non-deterministic since
// dictionaries do not have an order. But if the dictionary
// contains more than one weekday, there's a problem with the
// JSON anyway.
guard let (weekday, schedule) = dailySchedule.first else { continue }
switch weekday {
case "monday": monday = schedule
case "tuesday": tuesday = schedule
case "wednesday": wednesday = schedule
case "thursday": thursday = schedule
case "friday": friday = schedule
case "saturday": saturday = schedule
case "sunday": sunday = schedule
default: break
}
}
self.monday = monday
self.tuesday = tuesday
self.wednesday = wednesday
self.thursday = thursday
self.friday = friday
self.saturday = saturday
self.sunday = sunday
}
}
struct Schedule: Decodable {
let id: Int
let start: String
let end: String
let address: String
let name: String
let text: String
let imageURL: URL
let location: CLLocationCoordinate2D
private enum CodingKeys: String, CodingKey {
case start, end, name, address, id
case imageURL = "image", text = "description"
case lat, long
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.start = try container.decode(String.self, forKey: .start)
self.end = try container.decode(String.self, forKey: .end)
self.address = try container.decode(String.self, forKey: .address)
self.name = try container.decode(String.self, forKey: .name)
self.text = try container.decode(String.self, forKey: .text)
self.imageURL = try container.decode(URL.self, forKey: .imageURL)
let latStr = try container.decode(String.self, forKey: .lat)
let longStr = try container.decode(String.self, forKey: .long)
guard let lat = CLLocationDegrees(latStr), let long = CLLocationDegrees(longStr) else {
fatalError("lat / long is not a number")
}
self.location = CLLocationCoordinate2D(latitude: lat, longitude: long)
}
}
let weeklySchedule = try JSONDecoder().decode(WeeklySchedule.self, from: jsonData)
answered Nov 25 '18 at 5:55
Code DifferentCode Different
48.4k776112
48.4k776112
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53463724%2fparsing-json-with-decodable-issue%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown