Swift.DecodingError.typeMismatch (Swift.Dictionary , Swift.DecodingError.Context (codingPath :) - PullRequest
0 голосов
/ 05 марта 2020

Я пытаюсь декодировать строковые значения для alias в categories в перечисление. И я получаю ошибку ниже. Что я делаю не так?

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")
    }

1 Ответ

0 голосов
/ 05 марта 2020

Проблема в том, как вы декодируете CategoryType. Вы используете KeyedDecodingContainer, когда вы должны использовать SingleValueDecodingContainer. На этом этапе процесса декодирования вы пытаетесь декодировать значение alias, которое представляет собой всего лишь одно значение (String). KeyedDecodingContainer s используются только для декодирования основанных на ключах структур данных, таких как словари.

Просто немного измените инициализатор init(from:) вашего CategoryType, например, так:

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

... и удалите ненужное перечисление CategoryKeys, так как мы больше не используем KeyedDecodingContainer.

...