4

My questions is what is the best method I could use to decode a JSON that has many different keys? Right now, I am creating a var for every single one. This seems inefficient/hard coding, and I have about 8 different menus like this to decode.

Here is my JSON: (long, I know)

{
  "Brunch": {
    "BEAR FIT": [
      "Savory Tofu, Spinach, Tomato Scramble"
    ], 
    "BEAR FUSION": [
      "Roasted Red & Gold Beets", 
      "Baked Ham", 
      "Parslied Potatoes", 
      "Roasted Zuchhini Squash Medley", 
      "Vegan Celebration Roast", 
      "Steamed Rice", 
      "Brown Rice"
    ], 
    "BEAR NECESSITIES": [
      "Rolled Oatmeal", 
      "Italian Wedding Soup"
    ], 
    "BEAR SWEETS": [
      "Blueberry Muffin", 
      "Cranberry Orange Scone", 
      "Assorted Danish"
    ], 
    "BREAKFAST PLATE": [
      "Hashbrown Tri Patty", 
      "Spiced French Toast", 
      "Breakfast Veggie Patty"
    ], 
    "GOLDEN GRILL": [
      "Waffle Bar Toppings"
    ], 
    "ITALIAN CORNER": [
      "Omelet Bar"
    ], 
    "PASTAS": [
      "Breadsticks", 
      "Pasta", 
      "Whole Wheat Pasta", 
      "Creamy Alfredo Sauce", 
      "Beef Bolognese Sauce"
    ], 
    "SMOOTHIES": [
      "Peach Smoothie"
    ], 
    "SPECIALTY SALADS": [
      "Macaroni Salad"
    ]
  }, 
  "Dinner": {
    "BEAR FIT": [
      "Vegetable Caribbean Blend", 
      "Three Bean Chili", 
      "Brown Rice"
    ], 
    "BEAR FUSION": [
      "Chicken Vindaloo", 
      "Chana Masala", 
      "Brown Rice", 
      "Steamed Rice"
    ], 
    "BEAR NECESSITIES": [
      "Garden Vegetable Soup with Tomato", 
      "Italian Wedding Soup"
    ], 
    "BEAR SWEETS": [
      "Raspberry Sammie", 
      "Chocolate Chunk Brownie", 
      "Pumpkin Pie"
    ], 
    "CAL-ZONE": [
      "Mushroom & Olive Flatbread", 
      "Meat Lovers Pizza", 
      "Pepperoni Pizza"
    ], 
    "GOLDEN GRILL": [
      "Fish Sandwich", 
      "Malibu Burger", 
      "Shoestring Fries"
    ], 
    "PASTAS": [
      "Breadsticks", 
      "Pasta", 
      "Whole Wheat Pasta", 
      "Creamy Alfredo Sauce", 
      "Beef Bolognese Sauce"
    ], 
    "SPECIALTY SALADS": [
      "Macaroni Salad"
    ], 
    "THE BIG C": [
      "Hawaiian BBQ Pork", 
      "Blackened Tilapia with Lemon Pepper", 
      "Teriyaki Tofu", 
      "Steamed Rice", 
      "Suateed Cabbage", 
      "Cinnamon Glazed Carrot"
    ]
  }
}

Here is my parser that I have now:

struct WeekendMenu: Decodable {
    struct Brunch: Decodable {
        var BEAR_FIT: [String]
        var BEAR_FUSION: [String]
        var BEAR_NECESSITIES: [String]
        var BEAR_SWEETS: [String]
        var BREAKFAST_PLATE: [String]
        var GOLDEN_GRILL: [String]
        var ITALIAN_CORNER: [String]
        var PASTAS: [String]
        var SMOOTHIES: [String]
        var SPECIALTY_SALADS: [String]
        private enum CodingKeys: String, CodingKey {
            case BEAR_FIT  = "BEAR FIT"
            case BEAR_FUSION  = "BEAR FUSION"
            case BEAR_NECESSITIES  = "BEAR NECESSITIES"
            case BEAR_SWEETS  = "BEAR SWEETS"
            case BREAKFAST_PLATE  = "BREAKFAST PLATE"
            case GOLDEN_GRILL  = "GOLDEN GRILL"
            case ITALIAN_CORNER  = "ITALIAN CORNER"
            case PASTAS  = "PASTAS"
            case SMOOTHIES  = "SMOOTHIES"
            case SPECIALTY_SALADS  = "SPECIALTY SALADS"
        }
    }
    struct Dinner: Decodable {
        //TODO
    }
    var brunch: String
    var dinner: String
    private enum CodingKeys: String, CodingKey {
        case brunch = "Brunch"
        case dinner = "Dinner"
    }
}
winstonj
  • 53
  • 1
  • 3
  • Usually struct is created to check incoming JSON if it fits desirable structure. You can use dictionary as well to decode JSON in it and get values for desired keys, like in old times – tereks Apr 30 '18 at 04:06

1 Answers1

2

In this case I recommend to decode the JSON as dictionaries [String:[String]]

struct WeekendMenu: Decodable {
    private enum CodingKeys : String, CodingKey { case brunch = "Brunch", dinner = "Dinner" }

    let brunch: [String:[String]]
    let dinner: [String:[String]]
}

Enumerate the dictionaries, sort the keys to get the same order

let result = try JSONDecoder().decode(WeekendMenu.self, from: data)
for key in result.brunch.keys.sorted() {
    print(key, result.brunch[key]!)
}
for key in result.dinner.keys.sorted() {
    print(key, result.dinner[key]!)
}

Alternatively write a custom initializer to decode the dictionaries into a custom struct Category, the key becomes the name, the value becomes the dishes array.

struct WeekendMenu: Decodable {
    private enum CodingKeys : String, CodingKey { case brunch = "Brunch", dinner = "Dinner" }

    let brunch: [Category]
    let dinner: [Category]

     init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let brunch = try container.decode([String:[String]].self, forKey: .brunch)
        let brunchKeys = brunch.keys.sorted()
        self.brunch = brunchKeys.map { Category(name: $0, dishes: brunch[$0]!) }
        let dinner = try container.decode([String:[String]].self, forKey: .dinner)
        let dinnerKeys = dinner.keys.sorted()
        self.dinner = dinnerKeys.map { Category(name: $0, dishes: dinner[$0]!) }
    }
}

struct Category {
    let name : String
    let dishes : [String]
}
vadian
  • 274,689
  • 30
  • 353
  • 361