Parsing JSON with decodable issue












1















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.










share|improve this question





























    1















    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.










    share|improve this question



























      1












      1








      1


      0






      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.










      share|improve this question
















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 25 '18 at 0:49







      Kegham K.

















      asked Nov 25 '18 at 0:43









      Kegham K.Kegham K.

      9311023




      9311023
























          1 Answer
          1






          active

          oldest

          votes


















          0














          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)





          share|improve this answer























            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
            });


            }
            });














            draft saved

            draft discarded


















            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









            0














            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)





            share|improve this answer




























              0














              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)





              share|improve this answer


























                0












                0








                0







                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)





                share|improve this answer













                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)






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 25 '18 at 5:55









                Code DifferentCode Different

                48.4k776112




                48.4k776112
































                    draft saved

                    draft discarded




















































                    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.




                    draft saved


                    draft discarded














                    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





















































                    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







                    Popular posts from this blog

                    Ottavio Pratesi

                    Tricia Helfer

                    15 giugno