Search code examples
swiftdecoder

Swift Decoder, trying to decode file with layers


How would I decode this json file with JSONDecoder().decode

{
  "text": "pear",
  "parsed": [
    {
      "food": {
        "foodId": "food_bq6stkiaxkwhxia9q4v7wanjnew0",
        "label": "Pear",
        "nutrients": {
          "ENERC_KCAL": 57,
          "PROCNT": 0.36,
          "FAT": 0.14,
          "CHOCDF": 15.23,
          "FIBTG": 3.1
        },
        "category": "Generic foods",
        "categoryLabel": "food",
        "image": "https://www.edamam.com/food-img/65a/65aec51d264db28bbe27117c9fdaaca7.jpg"
      }
    }
  ],
  "hints": [
    {
      "food": {
        "foodId": "food_bq6stkiaxkwhxia9q4v7wanjnew0",
        "label": "Pear",
        "nutrients": {
          "ENERC_KCAL": 57,
          "PROCNT": 0.36,
          "FAT": 0.14,
          "CHOCDF": 15.23,
          "FIBTG": 3.1
        },
        "category": "Generic foods",
        "categoryLabel": "food",
        "image": "https://www.edamam.com/food-img/65a/65aec51d264db28bbe27117c9fdaaca7.jpg"
      },
      "measures": [
        {
          "uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_unit",
          "label": "Whole",
          "qualified": [
            {
              "qualifiers": [
                {
                  "uri": "http://www.edamam.com/ontologies/edamam.owl#Qualifier_large",
                  "label": "large"
                }
              ]
            },
            {
              "qualifiers": [
                {
                  "uri": "http://www.edamam.com/ontologies/edamam.owl#Qualifier_small",
                  "label": "small"
                }
              ]
            },
            {
              "qualifiers": [
                {
                  "uri": "http://www.edamam.com/ontologies/edamam.owl#Qualifier_medium",
                  "label": "medium"
                }
              ]
            }
          ]
        },
        {
          "uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_serving",
          "label": "Serving"
        },
        {
          "uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_half",
          "label": "Half"
        },
        {
          "uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_slice",
          "label": "Slice"
        },
        {
          "uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_gram",
          "label": "Gram"
        },
        {
          "uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_ounce",
          "label": "Ounce"
        },
        {
          "uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_pound",
          "label": "Pound"
        },
        {
          "uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_kilogram",
          "label": "Kilogram"
        },
        {
          "uri": "http://www.edamam.com/ontologies/edamam.owl#Measure_cup",
          "label": "Cup"
        }

The file goes on and on.

I try to do [String] but it says it expects a dictionary, So I put in a dictionary, then it says it expects another dictionary, This repeats until I have this:

let posts = try! JSONDecoder().decode([String: [String: [String: String]]].self, from: data!)

Then I get the error

"Expected to decode Dictionary<String, Any> but found a string/data instead.", underlyingError: nil))

Though when I go back one to do a string/data I get this error

"Expected to decode String but found a dictionary instead.", underlyingError: nil))

Help?


Solution

  • As mentioned in the comments you can use a quick parser, however you need to understand why is this happening, the issue here is that you're passing [String: [String: [String: String]]] which is not valid skeleton for this key you can start breaking down each layer alone to understand first level for example [String, Any] and then start exporting each key and decode that stand alone, using the above link your models should be something like that.

    // MARK: - Foo
    struct Foo: Codable {
        let text: String
        let parsed: [Parsed]
        let hints: [Hint]
    }
    
    // MARK: - Hint
    struct Hint: Codable {
        let food: Food
        let measures: [Measure]
    }
    
    // MARK: - Food
    struct Food: Codable {
        let foodID, label: String
        let nutrients: Nutrients
        let category, categoryLabel: String
        let image: String
    
        enum CodingKeys: String, CodingKey {
            case foodID = "foodId"
            case label, nutrients, category, categoryLabel, image
        }
    }
    
    // MARK: - Nutrients
    struct Nutrients: Codable {
        let enercKcal: Int
        let procnt, fat, chocdf, fibtg: Double
    
        enum CodingKeys: String, CodingKey {
            case enercKcal = "ENERC_KCAL"
            case procnt = "PROCNT"
            case fat = "FAT"
            case chocdf = "CHOCDF"
            case fibtg = "FIBTG"
        }
    }
    
    // MARK: - Measure
    struct Measure: Codable {
        let uri: String
        let label: String
        let qualified: [Qualified]?
    }
    
    // MARK: - Qualified
    struct Qualified: Codable {
        let qualifiers: [Qualifier]
    }
    
    // MARK: - Qualifier
    struct Qualifier: Codable {
        let uri: String
        let label: String
    }
    
    // MARK: - Parsed
    struct Parsed: Codable {
        let food: Food
    }
    

    And you can use it simply like this:

    let foo = try? newJSONDecoder().decode(Foo.self, from: jsonData)