PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Saturday, December 3, 2022

[FIXED] How to encode enum with Arrays of Custom Objects

 December 03, 2022     jsonencoder, swift     No comments   

Issue

Thanks for your help in advance! I am relatively new to SwiftUI and have been struggling with encoding an enum with Arrays of custom objects. Here is the code:

struct ChartData: Codable {
    var data: DataType
...
    private enum CodingKeys : String, CodingKey { case id, title, type, info, label_x, label_y, last_update, data }

    func encode(to encoder: Encoder) throws {
       var container = encoder.container(keyedBy: CodingKeys.self)
...
        try container.encode(self.data, forKey: .data)
}
}

enum DataType: Codable{
    case plot([ChartPlotData])
    case slice([ChartSliceData])
    case number([ChartNumberData])
}

struct ChartPlotData: Codable {
    var chart_id: String
    var order_plot: Int
    var label_plot: String?
    var points_x: [String]
    var points_y: [Double]
    
    private enum CodingKeys : String, CodingKey { case chart_id, order_plot, label_plot, points_x, points_y }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.chart_id = try container.decode(String.self, forKey: .chart_id)
        self.order_plot = try container.decode(Int.self, forKey: .order_plot)
        self.label_plot = try? container.decode(String.self, forKey: .label_plot)
        do{
            self.points_x = try container.decode([String].self, forKey:.points_x)
        }catch{
            let xs = try container.decode([Double].self, forKey:.points_x)
            self.points_x = xs.map { String($0) }
        }
        self.points_y = try container.decode([Double].self, forKey:.points_y)
    }
}

struct ChartSliceData: Codable {
    var chart_id: String
    var order_slice: Int
    var label_slice: String
    var value_slice: Double
}

struct ChartNumberData: Codable {
    var chart_id: String
    var number: Double
    var unit: String?
}

I am attempting to cache JSON in UserDefaults so as to avoid having to make extraneous API calls. Using JSONEncoder, I am left with the following JSON snippet (excerpted from a much longer string):

            "data" : {
              "plot" : {
                "_0" : [
                  {
                    "label_plot" : "China",
                    "points_y" : [
                      0,
                      0,
                      0,
...

However, I am looking to get an encoding like this:

            "data" : [
                  {
                    "label_plot" : "China",
                    "points_y" : [
                      0,
                      0,
                      0,
...

Any help would be greatly appreciated! Thanks so much!


Solution

This can be solved by adding an extra item in the CodingKeys enum for encoding and decoding the type used for the DataType enum and then using a switch to encode and decode the right type of array.

Here is the full ChartData struct although with some properties and code removed for brevity

struct ChartData: Codable {
    var id: Int
    var title: String
    var data: DataType
   
    enum CodingKeys: String, CodingKey {
        case id, title, data, type
    }
   
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(title, forKey: .title)
        
        switch data {
        case .number(let values): 
            try container.encode("number", forKey: .type)
            try container.encode(values, forKey: .data)
        case .plot(let values): 
            try container.encode("plot", forKey: .type)
            try container.encode(values, forKey: .data)
        case .slice(let values): 
            try container.encode("slice", forKey: .type)
            try container.encode(values, forKey: .data)
        }
    }
    
    init(from decoder: Decoder) throws {
        var container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        title = try container.decode(String.self, forKey: .title)
        
        let type = try container.decode(String.self, forKey: .type)
        switch type {
        case "number":
            let values = try container.decode([ChartNumberData].self, forKey: .data)
            data = .number(values)
                // case ...
        default:
            fatalError("Unsupported type for DataType: \(type)")
        }
    }
}


Answered By - Joakim Danielson
Answer Checked By - Cary Denson (PHPFixing Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing