Unraveling an enum with boolean values

I'm currently working with a JSON file that contains information about holidays.

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

To work with this data, I've created a struct called Month to represent each month and its corresponding holidays.

public struct Month {
    public let name: String
    public let holidays: [Holiday]?
}

extension Month: Decodable { }

In addition, I have another struct named Year which encompasses all the months and their holidays.

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

For handling holidays specifically, I've defined a struct called Holiday. This struct includes a property named type, represented by an enum called HolidayType.

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

When attempting to load and decode the JSON file, I encountered an error message:

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: "Expected to decode Dictionary but found a number instead.", underlyingError: nil))

I've shared a demo project demonstrating the issue here.

Answer №1

Representing multiple booleans using an enum is not possible. To maintain your types without alteration, it is suggested to utilize an OptionSet with customized decoding:

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

Alternatively, instead of custom parsing, you can convert your types by using a computed variable:

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

Answer №2

"isNationalHoliday", intValue: nil)], debugDescription: "Expected to decode Dictionary but found a number instead.", underlyingError: nil))

isNationalHoliday is a boolean value and not an enum type, the same applies for

isRegionalHoliday, isPublicHoliday, isGovernmentHoliday.

You should use the following structure:

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Merge arrays with identical names within the same object into one cohesive object containing all elements

I just started using Vue and I'm not entirely sure if it's possible to achieve what I have in mind. Here is the structure I have: { "items":[ { "total":1287, "currency":"USD", "name":"string", "itemID":"", "pro ...

The use of event.returnValue is outdated and no longer supported. It is recommended to use the standard event.preventDefault() method instead. You may encounter

Hey there :) Currently, I am utilizing JQuery 1.9.1.js to search records using JSON. I am able to retrieve the search list locally, but when attempting to publish it on Windows Server 2008 and IIS 7, I face issues as it throws an error stating "event.ret ...

Passing JSON information from a website to a Node.js server

<script type="text/javascript" src="data.json"></script> var mydata = JSON.parse(data); data = '[{"yer" : "Besiktas", "lat" : "41.044161", "lng" : "29.001056"},{"yer" : "Eminönü", "lat" : "41.017513", "lng" : "28.970939"},{"yer" : "Zeyt ...

Generating HTML content using Angular 8 and JSON data

Currently, I am managing an Angular Storybook that consists of various components. Within the stories.ts file of a component, there is a JSON snippet containing properties such as the content of a DIV element, shown below... { "accordionLink": ' ...

Integrating HTTP JSON responses into HTML using Ionic 2, Angular 2, TypeScript, and PHP: A comprehensive guide

Currently in the midst of developing my first Ionic 2 app, however, my understanding of typescript is still limited.. I aim to execute the authenticate() method within my constructor and then to: Retrieve the entire JSON response into the textarea and/o ...

jQuery: Issue Encountered with POST Request when Offline (iOS & Chrome)

After developing an HTML5 web application with offline capabilities using AppCache, the process flow is as follows: Online: While connected to the network, the app loads base information in advance. Offline: Users can take the app offline on their tablet ...

Is the user currently browsing the 'Home screen webpage' or using the Safari browser?

In JavaScript, is there a method to determine if the user has accessed the website from their home screen after adding it to their home screen, or if they are browsing via Safari as usual? ...

Incorporating a File Attachment within a JSON Structure

Can a file attachment be included in a JSON Object? I am working on an HTML Form with text field inputs and a file attachment, and I would like to send all this form data (including the file attachment) as a JSON Object to the server. Are there any specif ...

Displaying data on the user interface in Angular by populating it with information from the form inputs

I am working on a project where I need to display data on the screen based on user input received via radio buttons, and apply specific conditions. Additionally, I require assistance in retrieving the id of an object when the name attribute is chosen from ...

Displaying JSON data in an HTML table cell format

Hey everyone, I need some help with the following task: I am working on displaying a list of log lines in an HTML table. Some of these lines will contain JSON strings, and I want to format the JSON data within the table when the HTML file is loaded from ...

Learn the process of dynamically populating an HTML table with data using JavaScript and JSON

I have developed a code snippet to dynamically add an HTML table without using jQuery. The code serves as an application from the server to the client, where the client receives a JSON object to parse into a string. Here is how you can add an HTML table ...

Transform object into JSON format

Is there a way to transform an object into JSON and display it on an HTML page? let userInfo = { firstName: "O", lastName: "K", email: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2b44476b445b05484446">[ema ...

Retrieving ng-repeat object in Angular

How can I retrieve the current object from an ng-repeat on ng-click without using $index? The $index method is giving me the wrong index due to my use of orderBy. Ideally, I would like to be able to click on the object (thumbnail) and have $scope.activePer ...

Address Book on Rails

Hello, I'm relatively new to this and would be grateful for any assistance. My goal is to utilize the data saved by a user in their address book, and then offer them the option to use that address for delivery. Below is my Address controller: class A ...

Problem encountered when trying to show the Jquery Ajax response on an HTML page

I'm facing a challenge with loading a page that needs to display values with dynamic pagination. To retrieve these values, I am making a REST call which returns a JSON object. Although I can see the JSON output in the browser console, I am unable to d ...

Switching the displayed image depending on the JSON data received

As a beginner in javascript and jQuery, I am working on displaying JSON results in the browser. My goal is to generate dynamic HTML by incorporating the JSON data. Below is an example of the JSON structure: [{"JobName":"JobDoSomething","JobStatus":2,"JobS ...

Searching text on an iPhone using regex patterns

Currently, I have been utilizing the following regular expression: <li class=\"b_algo\"><h2><a href=\"(.*?)\" In my search within NSString, I am interested in modifying this search to also include : <li class=\ ...

What is the correct way to show a JavaScript response on the screen?

Here is the JavaScript code I used to call the server API: <script type='text/javascript'> call_juvlon_api(apikey, 'getAvailableCredits', '', function(response) { document.getElementById('show').innerHT ...

Guide on transforming Div content to json format with the use of jquery

I am trying to figure out how to call the div id "con" when the export button is clicked in my HTML code. I want it to display JSON data in the console. If anyone has any suggestions or solutions, please help! <html> <div id ="con"> < ...

How can you eliminate a specific element from an HTML document?

Utilizing localStorage can be tricky when it comes to keeping the JSON file hidden from being displayed on the HTML page. One approach I used involves sending the JSON file to the client once and then performing all logic using that file. To prevent the JS ...