Разбор JSON с декодируемой проблемой - PullRequest
0 голосов
/ 25 ноября 2018

Я пытаюсь проанализировать JSON данные расписания по дням недели, и каждый день недели содержит массив различных событий / расписаний, которые повторяют каждое слабое.Итак, у меня есть массив данных, который содержит объекты дня недели с понедельника по воскресенье, а день недели имеет массив событий / расписаний.

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

Это объект json, который я пытаюсь проанализировать

{
"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": []
    }
]
}

То, что я ожидаю, это словарь:

var schedule = ["monday":[schedule], "tuesday":[schedule], ...]

То, что я получаю, похоже на массив словарей.У меня есть только один день в каждом объекте недели, а не все дни недели.

var schedule = [["monday":[schedule], "tuesday":[schedule], ...],["monday":[schedule], "tuesday":[schedule], ...]]

Так как я могу это сделать?Я создаю различную структуру для каждого дня вместо структуры дня недели?Не похоже на логику.Что-то не совсем правильно.Я уверен, что есть более разумное решение для этого.

1 Ответ

0 голосов
/ 25 ноября 2018

В вашем коде 2 проблемы:

1: вы объявили многие свойства как необязательные.Когда вы делаете это, вы скрываете ошибки.Вы хотите, чтобы это было успешным или неуспешным во время тестирования, чтобы вы знали, где отлаживать, а не скрывать это под коврами с помощью Optionals.

struct WeekDay: Decodable {
    let monday, tuesday, ... : [Schedule]?
}

struct Schedule: Decodable {
    let id: Int?
    let start: String?
    let end: String?
    let address: String?
    ...
}

2: с вашим JSON действительно сложно работать.Если у вас есть контроль над серверной стороной, измените его на:

{
    "data": [
        "monday": [],
        "tuesday": [{...}, {...}, ...],
        "wednesday": [],
        "thursday": [],
        "friday": [],
        "saturday": [],
        "sunday": [],
    ]
}

Если вы не можете изменить JSON, вот как вы можете декодировать плохой 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)
...