解码包含布尔值的枚举
Posted
技术标签:
【中文标题】解码包含布尔值的枚举【英文标题】:Decoding an enum containing bools 【发布时间】:2019-11-05 01:15:43 【问题描述】:我有这个 JSON 文件。
[
"name": "January",
"holidays": [
"name": "New Year's Day",
"date": "2019-01-01T00:00:00-0500",
"type":
"isNationalHoliday": true,
"isRegionalHoliday": true,
"isPublicHoliday": true,
"isGovernmentHoliday": true
,
"name": "Martin Luther King Day",
"date": "2019-01-21T00:00:00-0500",
"type":
"isNationalHoliday": true,
"isRegionalHoliday": true,
"isPublicHoliday": true,
"isGovernmentHoliday": true
]
,
"name": "February",
"holidays": [
"name": "Presidents' Day",
"date": "2019-02-18T00:00:00-0500",
"type":
"isNationalHoliday": false,
"isRegionalHoliday": true,
"isPublicHoliday": false,
"isGovernmentHoliday": false
]
,
"name": "March",
"holidays": null
]
我创建了一个 Month
结构来解码 JSON 中的字典。
public struct Month
public let name: String
public let holidays: [Holiday]?
extension Month: Decodable
还有一个 Year
结构来包含它们。
public struct Year
public let months: [Month]
extension Year: Decodable
public init(from decoder: Decoder) throws
let container = try decoder.singleValueContainer()
let values = try container.decode([Month].self)
months = values
我的Holiday
结构有点复杂,因为有一个名为HolidayType
的枚举,我想在其中解码JSON 中type
字段下的值。
public struct Holiday
public let name: String
public let date: Date
public let type: HolidayType
extension Holiday: Decodable
public enum HolidayType
case isNationalHoliday
case isRegionalHoliday
case isPublicHoliday
case isGovernmentHoliday
enum CodingKeys: String, CodingKey
case isNationalHoliday
case isRegionalHoliday
case isPublicHoliday
case isGovernmentHoliday
extension HolidayType: Decodable
public init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: CodingKeys.self)
self = try container.decode(HolidayType.self, forKey: .isNationalHoliday)
self = try container.decode(HolidayType.self, forKey: .isRegionalHoliday)
self = try container.decode(HolidayType.self, forKey: .isPublicHoliday)
self = try container.decode(HolidayType.self, forKey: .isGovernmentHoliday)
这是我加载文件和解码的地方。
if let url = Bundle.main.url(forResource: "holidays", withExtension: "json")
do
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let year = try decoder.decode(Year.self, from: data)
print(year.months)
catch let error
print("Error occurred decoding JSON: \(error)")
else
print("Error occurred loading file")
但它失败并出现以下错误。
typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "索引 0", intValue: 0), CodingKeys(stringValue: "holidays", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "type", intValue: nil), CodingKeys(stringValue: "isNationalHoliday", intValue: nil)], debugDescription: "预计解码 字典但找到了一个数字。”,基础错误: 无))
我不知道如何解决这个问题。我还上传了一个演示项目here。
【问题讨论】:
除了下面提到的枚举问题,注意顶层对象是一个数组,所以要解码[Month].self
而不是Year.self
。
@MartinR 他有Year
的自定义解码。
您的Year
结构是多余的,只需解码let months = try decoder.decode([Month].self, from: data); print(months)
。而仅采用Decodable
的extension
s 也是多余的。如果您不是结构或类的所有者,则仅采用协议的空扩展才有意义
@Sulthan:你说得对,我没注意到。
@Sulthan 恕我直言,如果有另一个属性 name
或 number
但不要在层次结构的顶层包装单个属性是有意义的。
【参考方案1】:
"isNationalHoliday", intValue: nil)], debugDescription: "本应解码 Dictionary 但找到了一个数字。", underlyingError: nil))
isNationalHoliday
是 bool 值而不是枚举类型,isRegionalHoliday, isPublicHoliday, isGovernmentHoliday
以此类推
你需要
// MARK: - Element
struct Root: Codable
let name: String
let holidays: [Holiday]?
// MARK: - Holiday
struct Holiday: Codable
let name: String
let date: Date
let type: TypeClass
// MARK: - TypeClass
struct TypeClass: Codable
let isNationalHoliday, isRegionalHoliday, isPublicHoliday, isGovernmentHoliday: Bool
let year = try decoder.decode([Root].self, from: data)
【讨论】:
【参考方案2】:您不能使用枚举来表示多个布尔值。如果你想保持你的类型不变,我建议使用带有自定义解码的OptionSet
:
struct Year: Decodable
let months: [Month]
init(from decoder: Decoder) throws
let container = try decoder.singleValueContainer()
months = try container.decode([Month].self)
struct Month: Decodable
let name: String
let holidays: [Holiday]?
struct Holiday: Decodable
let name: String
let date: Date
let type: HolidayType
struct HolidayType: OptionSet, Decodable
let rawValue: Int
static let national = HolidayType(rawValue: 1 << 0)
static let regional = HolidayType(rawValue: 1 << 1)
static let `public` = HolidayType(rawValue: 1 << 2)
static let government = HolidayType(rawValue: 1 << 3)
init(rawValue: Int)
self.rawValue = rawValue
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: CodingKeys.self)
self = try CodingKeys.allCases
.filter try container.decode(Bool.self, forKey: $0)
.map $0.type
.reduce([] as HolidayType) $0.union($1)
private enum CodingKeys: String, CodingKey, CaseIterable
case isNationalHoliday
case isRegionalHoliday
case isPublicHoliday
case isGovernmentHoliday
var type: HolidayType
switch self
case .isNationalHoliday:
return .national
case .isRegionalHoliday:
return .regional
case .isPublicHoliday:
return .public
case .isGovernmentHoliday:
return .government
或者,您可以使用计算变量转换您的类型,而不是自定义解析:
struct Holiday: Decodable
let name: String
let date: Date
private let type: HolidayTypeHolder
var types: [HolidayType]
return type.types
enum HolidayType: String
case national, regional, `public`, `government`
private struct HolidayTypeHolder: Decodable
let isNationalHoliday, isRegionalHoliday, isPublicHoliday, isGovernmentHoliday: Bool
var types: [HolidayType]
var types: [HolidayType] = []
if isNationalHoliday
types.append(.national)
if isRegionalHoliday
types.append(.regional)
if isPublicHoliday
types.append(.public)
if isGovernmentHoliday
types.append(.government)
return types
【讨论】:
谢谢!使用OptionSet
s 对我来说是一个全新的领域。从来不知道你可以让它们表现得像枚举。
@Isuru 更准确地说,你可以让他们表现得像Set<enum>
。声明枚举 HolidayType
然后使用 Set<HolidayType>
作为类型没有太大区别。以上是关于解码包含布尔值的枚举的主要内容,如果未能解决你的问题,请参考以下文章