我有一个像这样的简单JSON文件。
{
"january": [
{
"name": "New Year's Day",
"date": "2019-01-01T00:00:00-0500",
"isNationalHoliday": true,
"isRegionalHoliday": true,
"isPublicHoliday": true,
"isGovernmentHoliday": true
},
{
"name": "Martin Luther King Day",
"date": "2019-01-21T00:00:00-0500",
"isNationalHoliday": true,
"isRegionalHoliday": true,
"isPublicHoliday": true,
"isGovernmentHoliday": true
}
],
"february": [
{
"name": "Presidents' Day",
"date": "2019-02-18T00:00:00-0500",
"isNationalHoliday": false,
"isRegionalHoliday": true,
"isPublicHoliday": false,
"isGovernmentHoliday": false
}
],
"march": null
}
我正在尝试使用Swift的JSONDecoder
将它们解码为对象。为此,我创建了一个Month
和一个Holiday
对象。
public struct Month {
public let name: String
public let holidays: [Holiday]?
}
extension Month: Decodable { }
public struct Holiday {
public let name: String
public let date: Date
public let isNationalHoliday: Bool
public let isRegionalHoliday: Bool
public let isPublicHoliday: Bool
public let isGovernmentHoliday: Bool
}
extension Holiday: Decodable { }
还有一个单独的HolidayData
模型来保存所有这些数据。
public struct HolidayData {
public let months: [Month]
}
extension HolidayData: Decodable { }
这是我正在解码的地方。
guard let url = Bundle.main.url(forResource: "holidays", withExtension: "json") else { return }
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let jsonData = try decoder.decode(Month.self, from: data)
print(jsonData)
} catch let error {
print("Error occurred loading file: \(error.localizedDescription)")
return
}
但是它始终失败,并出现以下错误。
由于数据格式不正确,因此无法读取。
我猜测它失败了,因为即使holidays
结构中有一个字段,JSON文件中也没有名为Month
的字段。
如何将holidays数组添加到holidays
字段中而不将其包含在JSON中?
答案 0 :(得分:1)
您的JSON结构很难解码,但是可以做到。
这里的关键是,您需要一个CodingKey
枚举,例如:
enum Months : CodingKey, CaseIterable {
case january
case feburary
case march
// ...
}
您可以在init(decoder:)
结构中提供HolidayData
的自定义实现:
extension HolidayData : Decodable {
public init(from decoder: Decoder) throws {
var months = [Month]()
let container = try decoder.container(keyedBy: Months.self)
for month in Months.allCases {
let holidays = try container.decodeIfPresent([Holiday].self, forKey: month)
months.append(Month(name: month.stringValue, holidays: holidays))
}
self.months = months
}
}
还请注意,结构的属性名称与JSON中的键名称具有不同的名称。错字?
答案 1 :(得分:1)
如果要在不编写自定义解码逻辑的情况下解析JSON,则可以按以下步骤进行操作:
public struct Holiday: Decodable {
public let name: String
public let date: Date
public let isBankHoliday: Bool?
public let isPublicHoliday: Bool
public let isMercantileHoliday: Bool?
}
try decoder.decode([String: [Holiday]?].self, from: data)
为此,我不得不制作isBankHoliday
和isMercantileHoliday
Optional
,因为它们并不总是出现在JSON中。
现在,如果要将其解码为上面介绍的结构,则必须编写自定义解码逻辑:
public struct Month {
public let name: String
public let holidays: [Holiday]?
}
extension Month: Decodable { }
public struct Holiday {
public let name: String
public let date: Date
public let isBankHoliday: Bool
public let isPublicHoliday: Bool
public let isMercantileHoliday: Bool
enum CodingKeys: String, CodingKey {
case name
case date
case isBankHoliday
case isPublicHoliday
case isMercantileHoliday
}
}
extension Holiday: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
date = try container.decode(Date.self, forKey: .date)
isBankHoliday = try container.decodeIfPresent(Bool.self, forKey: .isBankHoliday) ?? false
isPublicHoliday = try container.decodeIfPresent(Bool.self, forKey: .isPublicHoliday) ?? false
isMercantileHoliday = try container.decodeIfPresent(Bool.self, forKey: .isMercantileHoliday) ?? false
}
}
public struct HolidayData {
public let months: [Month]
}
extension HolidayData: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let values = try container.decode([String: [Holiday]?].self)
months = values.map { (name, holidays) in
Month(name: name, holidays: holidays)
}
}
}
decoder.decode(HolidayData.self, from: data)
答案 2 :(得分:0)
月份结构与json不匹配。
将月份结构更改为类似以下内容:
public struct Year {
public let January: [Holyday]?
public let February: [Holyday]?
public let March: [Holyday]?
public let April: [Holyday]?
public let May: [Holyday]?
public let June: [Holyday]?
public let July: [Holyday]?
public let August: [Holyday]?
public let September: [Holyday]?
public let October: [Holyday]?
public let November: [Holyday]?
public let December: [Holyday]?
}
extension Year: Decodable { }
请注意,这并不是实现所需目标的最佳实践。
另一种方法是更改json(如果您具有访问权限)以匹配您的结构:
{[
"name":"january",
"holidays": [
{
"name": "New Year's Day",
"date": "2019-01-01T00:00:00-0500",
"isNationalHoliday": true,
"isRegionalHoliday": true,
"isPublicHoliday": true,
"isGovernmentHoliday": true
},
{
"name": "Martin Luther King Day",
"date": "2019-01-21T00:00:00-0500",
"isNationalHoliday": true,
"isRegionalHoliday": true,
"isPublicHoliday": true,
"isGovernmentHoliday": true
}
]],[
"name":"february",
"holidays": [
{
"name": "Presidents' Day",
"date": "2019-02-18T00:00:00-0500",
"isNationalHoliday": false,
"isRegionalHoliday": true,
"isPublicHoliday": false,
"isGovernmentHoliday": false
}
]],[
"name":"march",
"holidays": null
]
}