Home > Software engineering >  Swift custom decoding - joining properties
Swift custom decoding - joining properties

Time:12-21

I am trying to decode an object like MyPets with the following object below. I tried using 4 CodingKeys cat, dog, catName and dogName but since the names are not in the MyPets struct, they aren't available to decode and then join with the others to fill out the Dog and Cat structs. I also tried a nestedContainer for the name properties with no luck. Any ideas? Thank you!

struct MyPets: Decodable {
  let dog: Dog
  let cat: Cat

  // enum CodingKeys: String, CodingKey {
  //   case dog 
  //   case cat
  //   case catName
  //   case dogName
  // }

   public init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    // values only has dog and cat
   }
}

struct Dog {
  let name: String
  let weight: Int
  let food: String
}

struct Cat {
  let name: String
  let weight: Int
  let food: String
}

With the following object:

{
   "catName": "fluffy", 
   "dogName": "softy", 
   "cat": {
       "food": "fancyFeast", 
       "weight": "8"
   }, 
   "dog": {
       "food": "kibble", 
       "weight": "9"
   }
}

I want to do something like this to recreate the Cat struct

let catName = (try values.decode(String.self, forKey: .catName))
self.cat = Cat(name: catName, etc...)

CodePudding user response:

This can be used by having two different CodingKey enums, one for the actual properties and one that we decode to local variables.

struct MyPets: Decodable {
    var dog: Dog
    var cat: Cat

    enum CodingKeys: String, CodingKey {
        case dog
        case cat
    }

    enum HiddenKeys: String, CodingKey {
        case dogName
        case catName
    }

Then we make use of them in init(from:) by creating two different containers

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    dog = try container.decode(Dog.self, forKey: .dog)
    cat = try container.decode(Cat.self, forKey: .cat)

    let otherContainer = try decoder.container(keyedBy: HiddenKeys.self)

    let dogName = try otherContainer.decode(String.self, forKey: .dogName)
    let catName = try otherContainer.decode(String.self, forKey: .catName)

    self.dog.name = dogName
    self.cat.name = catName
}

Dog and Cat (not shown here) needs to conform to Decodable as well and use their own CodingKey enum

struct Dog: Decodable {
    var name: String = ""
    let weight: Int
    let food: String

    enum CodingKeys: String, CodingKey {
        case weight, food
    }
}
  • Related