1

Need to parse this JSON in such a way that i should be able to access the benefits associated to each plan inside the "enabled" key within "items" node as below:

let items = Items[0].plans.main[0].enabled[0].text

{
  "MyData": {
    "data": {
      "benefits": {
        "B1": {
          "text": "Text1"
        },
        "B2": {
          "text": "Text2"
        },
        "B3": {
          "text": "text3"
        }
      }
    },
    "items": [
      {
        "plans": {
          "main": [
            {
              "name": "plan1",
              "enabled": [
                "B1",
                "B2"
              ],
              "disabled": [
                "B2",
                "B3"
              ]
            }
          ]
        }
      }
    ]
  }
}

I have tried as below to achieve but seems like this is not working

class Main: Codable {
    
    var name: String?
    
    var enabled: [String]?
    var disabled: [String]?
    
    enum CodingKeys: String, CodingKey {
        case name = "name"
        case enabled = "enabled"
        case disabled = "disabled"
    }

class MyData: Codable {
    var benefits: [String: Benefit]?
    
    enum CodingKeys: String, CodingKey {
        case benefits = "benefits"
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let propertyContainer = try container.nestedContainer(keyedBy: CustomDynamicKey.self, forKey: .benefits)
        self.benefits = propertyContainer.decodeValues()
    }

class Benefit: Codable {
    
    var text: String?
    
    enum CodingKeys: String, CodingKey {
        case text = "text"
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        text = try container.decode(String.self, forKey: .text)
    }
}

struct CustomDynamicKey: CodingKey {
    
    var stringValue: String

    init?(stringValue: String) {
        self.stringValue = stringValue
    }
    
    var intValue: Int? { return nil }
    
    init?(intValue: Int) { return nil }
    

extension KeyedDecodingContainer where Key == DynamicKey {
    
    func decodeValues() -> [String : Benefit] {
        var dict = [String : Benefit]()
        for key in allKeys {
            if let md = try? decode(Benefit.self, forKey: key) {
                dict[key.stringValue] = md
            } else {
                print("unsupported key")
            }
        }
        return dict
    }
}

I tried to parse the models individually. However, i am able to access the models separately but i need to map the corresponding the benefit with the respective plan at the time of parsing JSON itself inside the required init() methods using Manual parsing.

  • I'd parse them as `[Sting: Benefit]`, some logic for the `Plan` when needed. I'd make that property `private`, and use a function, for retrieve the allowed one according to the plan. Ie, making a higher level function. – Larme Mar 02 '21 at 19:45

1 Answers1

0

One way could be to use JSONDecoder alongside JSONSerialization. Codables are a bit uncomfortable to use in such situations, and you can make var benefits: [String: Benefit]? optional (which you already have) and remove it from the CodingKeys enum. Then, use JSONSerialization to get the benefits field filled.

See This

Sean Goudarzi
  • 1,244
  • 1
  • 10
  • 23