2

我有这个 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,我想type在 JSON 中的字段下解码值。

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: "Index 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: "预期解码 Dictionary 但找到了一个数字。",底层错误:零))

我不知道如何解决这个问题。我还在这里上传了一个演示项目。

4

2 回答 2

3

您不能使用枚举来表示多个布尔值。如果您想保持类型不变,我建议您使用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
    }
}
于 2019-06-22T14:29:49.510 回答
-1

"isNationalHoliday", intValue: nil)], debugDescription: "期望解码 Dictionary 但找到了一个数字。", 基础错误: nil))

isNationalHoliday是布尔值而不是枚举类型,依此类推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)
于 2019-06-22T14:11:08.530 回答