Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(编码路径:)

Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath:)

我正在尝试将 categoriesalias 的字符串值解码为枚举。我收到以下错误。我做错了什么?

Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "businesses", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "categories", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "alias", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found a string/data instead.", underlyingError: nil))))   

代码如下:

let json = """
    {
    "businesses": [
        {
            "id": "2iwT3iutZvmqzmu7oOkWFw",
            "alias": "dos-caminos-new-york-7",
            "name": "Dos Caminos",
            "image_url": "https://s3-media2.fl.yelpcdn.com/bphoto/eHvXPqv6iLcTXfT486z6JA/o.jpg",
            "is_closed": false,
            "url": "https://www.yelp.com/biz/dos-caminos-new-york-7?adjust_creative=RzPd81IBxDPwaWBeNhRk8w&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=RzPd81IBxDPwaWBeNhRk8w",
            "review_count": 598,
            "categories": [
                {
                    "alias": "mexican",
                    "title": "Mexican"
                },
                {
                    "alias": "bars",
                    "title": "Bars"
                }
            ],
            "rating": 3.5,
            "coordinates": {
                "latitude": 40.7593727,
                "longitude": -73.9853281
            },
            "transactions": [
                "pickup",
                "delivery"
            ],
            "price": "$$",
            "location": {
                "address1": "1567 Broadway",
                "address2": "",
                "address3": "",
                "city": "New York",
                "zip_code": "10036",
                "country": "US",
                "state": "NY",
                "display_address": [
                    "1567 Broadway",
                    "New York, NY 10036"
                ]
            },
            "phone": "+12129181330",
            "display_phone": "(212) 918-1330",
            "distance": 55.57057440108928
        }
     ]
    }
    """.data(using: .utf8)!

    struct Restaurant: Decodable {
        let name: String
        let price: String?
        let phone: String
        let distance: Double
        let imageUrl: String
        let url: String
        let categories: [Category]
        let coordinates: Coordinates

        enum CodingKeys: String, CodingKey {
            case name
            case price
            case phone
            case distance
            case url
            case imageUrl = "image_url"
            case coordinates
            case categories
        }

        struct Coordinates: Decodable {
            let latitude: Double
            let longitude: Double
        }

        struct Category: Decodable {
            let alias: CategoryType

            enum CategoryKeys: String, CodingKey {
                case alias
            }

            enum CategoryType: Decodable {
                case pizza
                case burgers
                case chinese
                case mexican
                case other(alias: String)

                init(alias: String) {
                    switch alias {
                    case "pizza": self = .pizza
                    case "burgers": self = .burgers
                    case "chinese": self = .chinese
                    case "mexican": self = .mexican
                    default: self = .other(alias: alias)
                    }
                }

                init(from decoder: Decoder) throws {
                    let container = try decoder.container(keyedBy: CategoryKeys.self)
                    if let alias = try container.decodeIfPresent(String.self, forKey: .alias) {
                        self = CategoryType(alias: alias)
                    } else {
                        self = .other(alias: "")
                    }
                }
            }
        }
    }

    struct Response: Decodable {
        let businesses: [Restaurant]
    }

    do {
        let result = try JSONDecoder().decode(Response.self, from: json)
        print(result.businesses.first?.name)
    } catch {
        print("error")
    }

问题是你如何解码 CategoryType。当你应该使用 SingleValueDecodingContainer 时,你却使用了 KeyedDecodingContainer。在解码过程的这一点上,您正在尝试解码 alias 的值,它只是一个值(String)。 KeyedDecodingContainers 仅用于解码基于键的数据结构,如字典。

只需稍微更改 CategoryTypeinit(from:) 初始值设定项,如下所示:

init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    let aliasRawValue = try container.decode(String.self)
    self.init(alias: aliasRawValue)
}

... 并删除不必要的 CategoryKeys 枚举,因为我们不再使用 KeyedDecodingContainer