title: How to extract data from nested JSON with Swift 4 with dynamic keys
body: <p>I have a JSON data structure using unique keys that are created on upload. I can read all of it if I read each dictionary item line by line. However, I'm trying to modify my code to use the Swift 4 codable properties.</p>
  
  <p>Doing the <a href="https://www.raywenderlich.com/119922/swift-tutorial-initialization-part-1" rel="nofollow noreferrer">Ray Wenderlich tutorial</a> and reading the <a href="http://benscheirman.com/2017/06/ultimate-guide-to-json-parsing-with-swift-4/" rel="nofollow noreferrer">Ultimate Guide to JSON Parsing with Swift</a> unfortunately did not propel me to genius status.</p>
  
  <p>The JSON looks like this simple example: 
  NOTE that keys, like "123", "456", "case1", "case2", "u1", "u2" are not known at run time.</p>
  
  <pre><code>   let json = """
      {
      "things" : {
          "123" : {
          "name" : "Party",
          "owner" : "Bob",
          "isActive" : true,
          "cases" : {
              "case1" : {
                  "no" : 1
              },
              "case2" : {
                  "no" : 2
              }
          }
          },
          "456" : {
          "name" : "Bus",
          "owner" : "Joe",
          "isActive" : true
          }
      },
      "users" : {
          "u1" : {
          "name" : "Summer"
          },
          "u2" : {
          "name" : "Daffy"
          }
      }
      }
      """
  </code></pre>
  
  <p>Following <a href="https://stackoverflow.com/questions/45666668/flattening-json-when-keys-are-known-only-at-runtime">this SO question on flattening JSON</a>, I was able to create a decoder for most of my data, but not for the nested dictionaries (in the example, cases is acting like a nested dictionary). I am sure that I am missing something simple. </p>
  
  <p>If I attempt to include the commented out portion, the playground will not run, no error is given.</p>
  
  <pre><code>    struct Thing: Decodable {
      let id: String
      let isActive: Bool
      let name: String
      let owner: String
      //var cases = [Case]()
  
      init(id: String, isActive: Bool, name: String, owner: String){//}, cases: [Case]?) {
          self.id = id
          self.isActive = isActive
          self.name = name
          self.owner = owner
          //self.cases = cases ?? [Case(id: "none", caseNumber: 0)]
      }
  }
  struct User: Decodable {
      let id: String
      let name: String
  }
  struct Case: Decodable {
      let id: String
      let caseNumber: Int
  }
  struct ResponseData: Decodable {
      var things = [Thing]()
      var users = [User]()
  
  
      enum CodingKeys: String, CodingKey {
          case trips
          case users
      }
  
      private struct PhantomKeys: CodingKey {
          var intValue: Int?
          var stringValue: String
          init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
          init?(stringValue: String) { self.stringValue = stringValue }
      }
  
      private enum ThingKeys: String, CodingKey {
          case isActive, name, owner, cases
      }
      private enum UserKeys: String, CodingKey {
          case name
      }
      private enum CaseKeys: String, CodingKey {
          case id
          case caseNumber = "no"
      }
  
      init(from decoder: Decoder) throws {
  
          let outer = try decoder.container(keyedBy: CodingKeys.self)
          let thingcontainer = try outer.nestedContainer(keyedBy: PhantomKeys.self, forKey: .things)
  
          for key in thingcontainer.allKeys {
              let aux = try thingcontainer.nestedContainer(keyedBy: ThingKeys.self, forKey: key)
  
              let name = try aux.decode(String.self, forKey: .name)
              let owner = try aux.decode(String.self, forKey: .owner)
              let isActive = try aux.decode(Bool.self, forKey: .isActive)
  //            let c = try aux.nestedContainer(keyedBy: CaseKeys.self, forKey: .cases)
  //            var cases = [Case]()
  //            for ckey in c.allKeys {
  //                let caseNumber = try c.decode(Int.self, forKey: .caseNumber)
  //                let thiscase = Case(id: ckey.stringValue, caseNumber: caseNumber)
  //                cases.append(thiscase)
  //            }
  
              let thing = Thing(id: key.stringValue, isActive: isActive, name: name, owner: owner)//, cases: cases)
              things.append(thing)
          }
          let usercontainer = try outer.nestedContainer(keyedBy: PhantomKeys.self, forKey: .users)
  
          for key in usercontainer.allKeys {
              let aux = try usercontainer.nestedContainer(keyedBy: UserKeys.self, forKey: key)
              let name = try aux.decode(String.self, forKey: .name)
              let user = User(id: key.stringValue,name: name)
              users.append(user)
          }
      }
  }
  </code></pre>
  
  <p>It works for the things and users, but I have to ignore the cases. See the output of print in comments//.   </p>
  
  <pre><code>let data = json.data(using: .utf8)!
  let things = try JSONDecoder().decode(ResponseData.self, from: data).things
  print(things[0])
  //Thing(id: "456", isActive: true, name: "Bus", owner: "Joe")
  
  let users = try JSONDecoder().decode(ResponseData.self, from: data).users
  print(users[0])
  //User(id: "u1", name: "Summer")
  </code></pre>
  
  <p>I have tried to use the guidance from <a href="https://stackoverflow.com/questions/44549310/how-to-decode-a-nested-json-struct-with-swift-decodable-protocol">this SO question on decoding</a> that seems much cleaner to me, but I have not successfully implemented it.</p>
  
  <p>This code is <a href="https://gist.github.com/monkeywithacupcake/ab1a204f12186a11b28d8554b287cde0" rel="nofollow noreferrer">also a GIST</a></p>
  
  <p>My question is twofold: </p>
  
  <ol>
  <li>How can I get the Case data as a nested array in my Thing?  </li>
  <li>Can you
  suggest a cleaner/shorter way to code this? It feels like I'm
  repeating things, but I have seen this kind of wrapper structure in
  several examples for JSON encoding/decoding.</li>
  </ol>