Search code examples
jsonswiftparsingswift5jsondecoder

Swift 5 Parsing strange json format


I'm trying to parse JSON but keep getting incorrect format error. The JSON I get back from FoodData Central (the USDA's Nutrition API) is as follows:

{
    dataType = "Survey (FNDDS)";
    description = "Chicken thigh, NS as to cooking method, skin not eaten";
    fdcId = 782172;
    foodNutrients =     (
                {
            amount = "24.09";
            id = 9141826;
            nutrient =             {
                id = 1003;
                name = Protein;
                number = 203;
                rank = 600;
                unitName = g;
            };
            type = FoodNutrient;
        },
                {
            amount = "10.74";
            id = "9141827";
            nutrient =             {
                id = 1004;
                name = "Total lipid (fat)";
                number = 204;
                rank = 800;
                unitName = g;
            };
            type = FoodNutrient;
        }
    );
}

My Structs:

struct Root: Decodable {
     let description: String
     let foodNutrients: FoodNutrients
}

struct FoodNutrients: Decodable {
     // What should go here???
}

From the JSON, it looks like foodNutrients is an array of unnamed objects, each of which has the values amount: String, id: String, and nutrient: Nutrient (which has id, name etc...) However, forgetting the Nutrient object, I can't even parse the amounts.

struct FoodNutrients: Decodable {
     let amounts: [String]
}

I don't think its an array of string, but I have no idea what the () in foodNutrients would indicate.

How would I go about parsing this JSON. I'm using Swift 5 and JSONDecoder. To get the JSON I use JSONSerializer, then print out the JSON above.


Solution

  • The () in foodNutrients indicates that it holds an array of objects - in that case FoodNutrient objects. Therefore your root object should look like this:

    struct Root: Decodable {
        let description: String
        let foodNutrients: [FoodNutrient]
    }
    

    Now the foodNutrient is except for the nutrient object straightforward:

    struct FoodNutrient: Decodable {
        let id: Int // <-- in your example it is an integer and in the second object a string, choose the fitting one from the API
        let amount: String
        let nutrient: Nutrient
    }
    

    And the nutrient object should look like this:

    struct Nutrient: Decodable {
        let name: String
        let number: Int
        let rank: Int
        let unitName: String 
    }
    

    Using Decodable is a good and easy way to serialize JSON. Hope that helps. Happy coding :)