使用Swift Decoder协议解码JSON时遇到的问题

问题描述 投票:0回答:1

我试图使用Swift Decoder协议来解析一些JSON,但是我得到了一些我无法理解的错误。我得到的JSON是有问题的,因为并不是所有的键都一定存在,所以我给相应结构的属性设置了默认值,并设置了一个初始化器,但我怀疑我的方法是错误的。

在 "food "数组中,有些项的属性是 "ndbNumber"(String),有些项的属性是fdcId(Int)和gtinUpc(String)。除此之外,这些项目都是相同的,都代表了搜索返回的食品。

这是最外层JSON级别的结构。

public struct ReturnedFoods: Decodable {
    public let count: Int
    public let all: [ReturnedFood]

    enum CodingKeys: String, CodingKey {
        case count = "totalHits"
        case all = "foods"
    }
}

而这个是下一层的食物项目的结构。

public struct ReturnedFood: Codable {

    public var ndbNumber: String = ""
    public var fdcId : Int = -1
    public var description: String = ""
    public var dataType : String = ""
    public var brandOwner: String = ""
    public var gtinUpc : String = ""
    public var ingredients : String = ""
    public var publishedDate : String = ""
    public var score : Float = -1

    public init(from decoder: Decoder) throws  {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        if let ndbNumber = try values.decodeIfPresent(String.self, forKey: .ndbNumber) {
            self.ndbNumber = ndbNumber
        }
        if let fdcId = try values.decodeIfPresent(Int.self, forKey: .ndbNumber) {
            self.fdcId = fdcId
        }
        if let description = try values.decodeIfPresent(String.self, forKey: .description) {
            self.description = description
        }
        if let dataType = try values.decodeIfPresent(String.self, forKey: .dataType) {
            self.dataType = dataType
        }
        if let brandOwner = try values.decodeIfPresent(String.self, forKey: .brandOwner) {
            self.brandOwner = brandOwner
        }
        if let gtinUpc = try values.decodeIfPresent(String.self, forKey: .gtinUpc) {
            self.gtinUpc = gtinUpc
        }
        if let ingredients = try values.decodeIfPresent(String.self, forKey: .ingredients) {
            self.ingredients = ingredients
        }
        if let publishedDate = try values.decodeIfPresent(String.self, forKey: .publishedDate) {
            self.publishedDate = publishedDate
        }
        if let score = try values.decodeIfPresent(Float.self, forKey: .score) {
            self.score = score
        }

    }

}

我仔细检查了所有的类型都是正确的 所以我怀疑问题出在键的缺失上。

这是我得到的错误:typeMismatch(Swift.Int, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "food", intValue: nil), _JSONKey(stringValue: "Index 4", intValue: 4), CodingKeys(stringValue: "ndbNumber", intValue: nil)], debugDescription: "预期解码Int,但发现了一个stringdata。", underlyingError: nil))

JSON非常长,所以我只贴出其中的一小部分。

  "foodSearchCriteria": {
        "dataType": [
            "Foundation",
            "Branded"
        ],
        "query": "cheddar",
        "generalSearchInput": "cheddar",
        "pageNumber": 1,
        "requireAllWords": false
    },
    "totalHits": 12839,
    "currentPage": 1,
    "totalPages": 257,
    "foods": [
        {
            "fdcId": 566851,
            "description": "CHEDDAR",
            "dataType": "Branded",
            "gtinUpc": "828280001766",
            "publishedDate": "2019-04-01",
            "brandOwner": "FISCALINI",
            "ingredients": "PASTEURIZED COW'S MILK, CHEESE CULTURES, ENZYMES (MICROBIAL RENNET), SALT.",
            "foodNutrients": [
                {
                    "nutrientId": 1079,
                    "nutrientName": "Fiber, total dietary",
                    "nutrientNumber": "291",
                    "unitName": "G",
                    "derivationCode": "LCCD",
                    "derivationDescription": "Calculated from a daily value percentage per serving size measure",
                    "value": 0
                },
                {
                    "nutrientId": 1087,
                    "nutrientName": "Calcium, Ca",
                    "nutrientNumber": "301",
                    "unitName": "MG",
                    "derivationCode": "LCCD",
                    "derivationDescription": "Calculated from a daily value percentage per serving size measure",
                    "value": 714
                },
                {
                    "nutrientId": 1089,
                    "nutrientName": "Iron, Fe",
                    "nutrientNumber": "303",
                    "unitName": "MG",
                    "derivationCode": "LCCD",
                    "derivationDescription": "Calculated from a daily value percentage per serving size measure",
                    "value": 0
                },
                {
                    "nutrientId": 1104,
                    "nutrientName": "Vitamin A, IU",
                    "nutrientNumber": "318",
                    "unitName": "IU",
                    "derivationCode": "LCCD",
                    "derivationDescription": "Calculated from a daily value percentage per serving size measure",
                    "value": 1071
                },
                {
                    "nutrientId": 1162,
                    "nutrientName": "Vitamin C, total ascorbic acid",
                    "nutrientNumber": "401",
                    "unitName": "MG",
                    "derivationCode": "LCCD",
                    "derivationDescription": "Calculated from a daily value percentage per serving size measure",
                    "value": 0
                },
                {
                    "nutrientId": 1003,
                    "nutrientName": "Protein",
                    "nutrientNumber": "203",
                    "unitName": "G",
                    "derivationCode": "LCCS",
                    "derivationDescription": "Calculated from value per serving size measure",
                    "value": 26.79
                },
                {
                    "nutrientId": 1004,
                    "nutrientName": "Total lipid (fat)",
                    "nutrientNumber": "204",
                    "unitName": "G",
                    "derivationCode": "LCCS",
                    "derivationDescription": "Calculated from value per serving size measure",
                    "value": 32.14
                },
                {
                    "nutrientId": 1005,
                    "nutrientName": "Carbohydrate, by difference",
                    "nutrientNumber": "205",
                    "unitName": "G",
                    "derivationCode": "LCCS",
                    "derivationDescription": "Calculated from value per serving size measure",
                    "value": 3.57
                },
                {
                    "nutrientId": 1008,
                    "nutrientName": "Energy",
                    "nutrientNumber": "208",
                    "unitName": "KCAL",
                    "derivationCode": "LCCS",
                    "derivationDescription": "Calculated from value per serving size measure",
                    "value": 400
                },
                {
                    "nutrientId": 1093,
                    "nutrientName": "Sodium, Na",
                    "nutrientNumber": "307",
                    "unitName": "MG",
                    "derivationCode": "LCCS",
                    "derivationDescription": "Calculated from value per serving size measure",
                    "value": 679
                },
                {
                    "nutrientId": 1253,
                    "nutrientName": "Cholesterol",
                    "nutrientNumber": "601",
                    "unitName": "MG",
                    "derivationCode": "LCCS",
                    "derivationDescription": "Calculated from value per serving size measure",
                    "value": 111
                },
                {
                    "nutrientId": 1257,
                    "nutrientName": "Fatty acids, total trans",
                    "nutrientNumber": "605",
                    "unitName": "G",
                    "derivationCode": "LCCS",
                    "derivationDescription": "Calculated from value per serving size measure",
                    "value": 1.25
                },
                {
                    "nutrientId": 1258,
                    "nutrientName": "Fatty acids, total saturated",
                    "nutrientNumber": "606",
                    "unitName": "G",
                    "derivationCode": "LCCS",
                    "derivationDescription": "Calculated from value per serving size measure",
                    "value": 21.43
                }
            ],
            "allHighlightFields": "",
            "score": 721.74396

这里有一个完整JSON格式的链接: https:/api.nal.usda.govfdcv1foodssearch?query=cheddar&dataType=Foundation,Branded&api_key=DEMO_KEY。

我希望能得到任何解决这个问题的帮助。

json swift codable decodable
1个回答
0
投票

你的键有类型不匹配的问题。foodsndbNumber,用这个结构来解码。

struct ReturnedFoods: Codable {
    let foodSearchCriteria: FoodSearchCriteria
    let totalHits, currentPage, totalPages: Int
    let foods: [Food]
}

struct FoodSearchCriteria: Codable {
    let dataType: [String]
    let query, generalSearchInput: String
    let pageNumber: Int
    let requireAllWords: Bool
}

struct Food: Codable {
    let fdcID: Int
    let description, dataType, gtinUpc, publishedDate: String
    let brandOwner, ingredients: String
    let foodNutrients: [FoodNutrient]
    let allHighlightFields: String
    let score: Double

    enum CodingKeys: String, CodingKey {
        case fdcID = "fdcId"
        case description, dataType, gtinUpc, publishedDate, brandOwner, ingredients, foodNutrients, allHighlightFields, score
    }
}

struct FoodNutrient: Codable {
    let nutrientID: Int
    let nutrientName, nutrientNumber, unitName: String
    let derivationCode: DerivationCode
    let derivationDescription: String
    let value: Double

    enum CodingKeys: String, CodingKey {
        case nutrientID = "nutrientId"
        case nutrientName, nutrientNumber, unitName, derivationCode, derivationDescription, value
    }
}

enum DerivationCode: String, Codable {
    case lccd = "LCCD"
    case lccs = "LCCS"
}
© www.soinside.com 2019 - 2024. All rights reserved.