【问题标题】:Trouble decoding JSON using the Swift Decoder protocol使用 Swift 解码器协议解码 JSON 时遇到问题
【发布时间】:2020-06-03 16:08:15
【问题描述】:

我正在尝试使用 Swift 解码器协议来解析一些 JSON,但是我遇到了一些我无法理解的错误。我返回的 JSON 是有问题的,因为并非所有键都肯定存在,所以我已经给出了相应结构默认值的属性并设置了一个初始化程序,但我怀疑我的方法是错误的。

在“foods”数组中,有些项目具有属性“ndbNumber”(字符串),有些项目具有 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: "foods", intValue: nil), _JSONKey(stringValue: "Index 4", intValue: 4), CodingKeys(stringValue: "ndbNumber", intValue: nil)], debugDescription: "期望解码 Int 但找到了一个字符串/数据。",底层错误: 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.gov/fdc/v1/foods/search?query=cheddar&dataType=Foundation,Branded&api_key=DEMO_KEY

对于解决此问题的任何帮助,我将不胜感激。

【问题讨论】:

  • 请阅读错误信息。它清楚地表明数组foods 的第5 项中键ndbNumber 的值不是Int。但是有一个明显的复制和粘贴错误:您必须为密钥.fdcId 而不是.ndbNumber 解码fdcId
  • 你是对的,这是一个愚蠢的复制和粘贴错误。感谢您注意到它。现在可以了。
  • 当您的 json 有时缺少参数时,您可以简单地将它们声明为可选项:public var description: String?

标签: json swift codable decodable


【解决方案1】:

您的键 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"
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-25
    • 2020-01-13
    • 2020-02-09
    • 1970-01-01
    • 2012-12-20
    • 1970-01-01
    • 2018-10-30
    • 2015-03-23
    相关资源
    最近更新 更多