Swift Codable with dynamic keys

13,671

Solution 1

Assuming you left out the { and } that would surround this block and are required for this to be valid JSON, the following is the simplest solution to getting things parsed, you really don't need to deal with CodingKey at all since your names match the keys in the JSON, so the synthesized CodingKey will work just fine:

public struct Schedule: Codable {
    public let periods : [String:[Inner]]
}

public struct Inner: Codable {
    public let firstName: String
    public let lastName: String
}

let schedule = try? JSONDecoder().decode(Schedule.self, from: json)

print(schedule?.periods.keys)

print(schedule?.periods["2018-06-07"]?[0].lastName)

The key is that the outer JSON is a JSON Object (dictionary/map) with a single key periods The value of that key is another map of arrays. Just break it down like that and everything falls out pretty much automatically.

Solution 2

Ok, so I figured it out like this:

public struct Schedule: Codable {
    public let periods: Periods
}

public struct Periods: Codable {
    public var innerArray: [String: [Inner]]
    
    public struct Inner: Codable {
        public let firstName: String
        public let lastName: String
    }
    
    private struct CustomCodingKeys: CodingKey {
        var stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        var intValue: Int?
        init?(intValue: Int) {
            return nil
        }
    }
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CustomCodingKeys.self)
        
        self.innerArray = [String: [Inner]]()
        for key in container.allKeys {
            let value = try container.decode([Inner].self, forKey: CustomCodingKeys(stringValue: key.stringValue)!)
            self.innerArray[key.stringValue] = value
        }
    }
}

As result I got dictionary like this: ["2018-06-06": [Inner]] where key is this Date String, and value Inner.

Share:
13,671
Masta
Author by

Masta

Updated on June 14, 2022

Comments

  • Masta
    Masta about 2 years

    I have JSON structure as:

    "periods": {
        "2018-06-07": [
          {
            "firstName": "Test1",
            "lastName": "Test1"
          }
        ],
        "2018-06-06": [
          {
            "firstName": "Test1",
            "lastName": "Test1"
          }
        ]
    }
    

    I tried to parse it like this:

    public struct Schedule: Codable {
        public let periods: Periods
    }
    
    public struct Periods: Codable {
        public let unknown: [Inner]
    
        public struct Inner: Codable {
            public let firstName: String
            public let lastName: String
        }
    
        private struct CustomCodingKeys: CodingKey {
            var stringValue: String
            init?(stringValue: String) {
                self.stringValue = stringValue
            }
    
            var intValue: Int?
            init?(intValue: Int) {
                return nil
            }
        }
    
        public init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CustomCodingKeys.self)
            self.unknown = try container.decode([Inner].self, forKey: CustomCodingKeys(stringValue: "2018-06-06")
        }
    }
    

    But I can get result for only one value (2018-06-06). I have multiple dates here that I want to parse. Is this possible?

  • Niloufar
    Niloufar over 5 years
    Dear Masta what is CustomCodingKeys?
  • Yogesh Nikam Patil
    Yogesh Nikam Patil over 4 years
    I want maintain dictionary insertion order..How do I achieve this?
  • 9to5ios
    9to5ios almost 4 years
    how to get all 2018-06-06 keys ?