【问题标题】:Parsing JSON with decodable issue解析带有可解码问题的 JSON
【发布时间】:2018-11-25 00:43:53
【问题描述】:

我正在尝试解析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], ...]]

那我该怎么做呢?我每天创建一个不同的结构而不是工作日结构?好像不合逻辑。有些事情是不对的。我确信有一个更智能的解决方案来解析它。

【问题讨论】:

    标签: json swift codable decodable


    【解决方案1】:

    您的代码有 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)
    

    【讨论】:

      猜你喜欢
      • 2018-06-23
      • 1970-01-01
      • 2023-04-09
      • 2023-03-12
      • 2020-08-19
      • 1970-01-01
      • 1970-01-01
      • 2018-11-20
      • 1970-01-01
      相关资源
      最近更新 更多