Swift:错误:keyNotFound(CodingKeys(stringValue:“成人”,intValue:nil)

Chu*_*ZHB 5 swift jsondecoder combine

我确实在堆栈溢出中看到了很多类似的问题,但似乎没有人与我的情况相似。我是组合框架的新手,我花了整个下午才弄清楚出了什么问题,但仍然卡在这里......

\n\n

Xcode 给出了以下错误,我所做的是使用 TMDB 的 API 并将其解码到我的 Actor 模型中。并且在这条线上失败了let result = try self.decoder.decode(TMDBActorsResult.self, from: output.data)。你能给我一些提示这是怎么回事吗adult

\n\n
\n

错误:keyNotFound(CodingKeys(stringValue:“成人”,intValue:nil),Swift.DecodingError.Context(codingPath:[CodingKeys(stringValue:“结果”,intValue:nil),_JSONKey(stringValue:“索引0”,intValue: 0), CodingKeys(stringValue: "knownFor", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "没有与密钥关联的值 CodingKeys(stringValue: \\"adult\\" , intValue: nil) (\\"adult\\").",underlyingError: nil))

\n
\n\n

我还检查了 url 是否正常工作,这是 TMDB API 的返回数据:

\n\n

在此输入图像描述

\n\n

在此输入图像描述

\n\n

/// 演员模型

\n\n
import SwiftUI\n\n\nstruct TMDBActorsResult: Codable {\n    let page: Int?\n    let results: [Actor]\n    let totalResults: Int?\n    let totalPages: Int?\n}\n\nstruct Actor: Codable {\n    let profilePath: String?\n    let adult: Bool\n    let id: Int?\n    let name: String?\n    let popularity: CGFloat?\n    let knownFor: [Production]?\n    let knownForDepartment: String?\n    let gender: Int?\n}\n\n// MARK: Used for two objects with media type = (Movie or TV)\nstruct Production: Codable {\n    let posterPath: String?\n    let adult: Bool\n    let overview: String?\n    let releaseDate: String?\n    let originalTitle: String?\n    let genreIds: [Int]?\n    let id: Int?\n    let mediaType: String?\n    let originalLanguage: String?\n    let title: String?\n    let backdropPath: String?\n    let popularity: Double?\n    let voteCount: Int?\n    let video: Bool\n    let voteAverage: Double?\n    let firstAirDate: String?\n    let originCountry: [String]?\n    let name: String?\n    let originalName: String?\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

/// JSON解码部分。

\n\n
import Foundation\nimport Combine\n\nenum HTTPError: LocalizedError {\n    case statusCode\n    case post\n}\n\nstruct WebService {\n\n    private var decoder: JSONDecoder = {\n        let decoder = JSONDecoder()\n        decoder.keyDecodingStrategy = .convertFromSnakeCase\n        return decoder\n    }()\n\n    private var session: URLSession = {\n        let config = URLSessionConfiguration.default\n        config.urlCache = URLCache.shared\n        config.waitsForConnectivity = true\n        config.requestCachePolicy = .reloadIgnoringLocalCacheData\n        return URLSession(configuration: config, delegate: nil, delegateQueue: nil)\n    }()\n\n    private func createPublisher<T: Codable>(for url: URL) -> AnyPublisher<T, Error> {\n        print("Pblisher URL: \\(url)")\n        return session.dataTaskPublisher(for: url)\n            .tryMap { output in\n                guard let response = output.response as? HTTPURLResponse, response.statusCode == 200 else {\n                    print("Response: \\(output.response)")\n\n                    do {\n                        let ss = try self.decoder.decode(Response.self, from: output.data)\n                        print("ss:  \\(ss)")\n                    } catch {\n                        print(error)\n                    }\n                    throw HTTPError.statusCode\n                }\n\n                do {\n                    let result = try self.decoder.decode(TMDBActorsResult.self, from: output.data)\n                    print("Result: \\(result)")\n                } catch {\n                    print("ERROR: \\(error)")\n                }\n                return output.data\n            }\n            .decode(type: T.self, decoder: decoder)\n            .eraseToAnyPublisher()\n    }\n\n    func getSectionsPublisher() -> AnyPublisher<TMDBActorsResult, Error> {\n        createPublisher(for: TMDBClient.Endpoints.popularActors.url).eraseToAnyPublisher()\n    }\n\n}\n\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

///更新,正如vadian\的评论,我尝试将我的数据模型修改为使用枚举关联值,如下面的代码所示,但它给了我错误Instance method \'decode(_:forKey:)\' requires that \'MediaType\' conform to \'Decodable\'type我发布的不同之处在于我的模型在项目中使用,但不在根媒体结构中使用。

\n\n
let jsonString = """\n[{"name": "Popular Movies",\n"description": "Basic movie description",\n"items": [ { "id": 15, "budget": 10, "type": "movies","name": "Sample movie", "movieSPT": ""}]\n},\n{"name": "Popular TV Shows",\n"description": "Basic shows description",\n"items": [ { "id": 15, "adult": false, "type": "tvshows","title": "Sample show", "showSPT": ""}]\n}\n]\n"""\nlet data = Data(jsonString.utf8)\n\n\nstruct Movie : Decodable {\n    let id: Int\n    let name, movieSPT: String\n    let type: String\n    let budget: Int\n}\n\nstruct TVShow : Decodable {\n    let id: Int\n    let title, showSPT: String\n    let type: String\n    let adult: Bool\n}\n\nenum MediaType {\n    case movie([Movie]), tvShow([TVShow])\n}\n\nstruct Media : Decodable {\n    let name : String\n    let description : String\n    let items : MediaType\n\n    private enum CodingKeys : String, CodingKey { case name, description, type, items }\n\n    init(from decoder : Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.name = try container.decode(String.self, forKey: .name)\n        self.description = try container.decode(String.self, forKey: .description)\n        self.items = try container.decode(MediaType.self, forKey: .items)\n        // type = try container.decode(String.self, forKey: .type)\n        if items.type == "movies" {\n            let movieData = try container.decode([Movie].self, forKey: .items)\n            // print("AAA: \\(movieData)")\n            items = .movie(movieData)\n        } else { // add better error handling\n            let showData = try container.decode([TVShow].self, forKey: .items)\n            items = .tvShow(showData)\n        }\n\n    }\n}\n\ndo {\n    let result = try JSONDecoder().decode([Media].self, from: data)\n    print(result)\n} catch {\n    print(error)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

添加 JSON Dataknown_for是一个[对象],该对象可以是 Movie 类型或 TV 类型。

\n\n
{\n  "page": 1,\n  "total_results": 10000,\n  "total_pages": 500,\n  "results": [\n    {\n      "popularity": 57.168,\n      "known_for_department": "Acting",\n      "name": "Thassapak Hsu",\n      "id": 1910848,\n      "profile_path": "/1fmjgN8EvDj1TiEJk2Zs4y0T40O.jpg",\n      "adult": false,\n      "known_for": [\n        {\n          "original_name": "\xe8\x90\x8c\xe5\xa6\xbb\xe9\xa3\x9f\xe7\xa5\x9e",\n          "genre_ids": [\n            35,\n            10765,\n            10766\n          ],\n          "media_type": "tv",\n          "name": "Cinderella Chef",\n          "origin_country": [],\n          "vote_count": 5,\n          "first_air_date": "2018-04-23",\n          "backdrop_path": "/rnzmWKEiWPb8GC1mlqQojj8ccWj.jpg",\n          "original_language": "zh",\n          "id": 79574,\n          "vote_average": 9,\n          "overview": "",\n          "poster_path": "/xb40Li6ff1BK0pVOxV4lutssCrR.jpg"\n        },\n        {\n          "original_name": "\xe5\xa4\x96\xe6\x98\x9f\xe5\xa5\xb3\xe7\x94\x9f\xe6\x9f\xb4\xe5\xb0\x8f\xe4\xb8\x83",\n          "genre_ids": [\n            35,\n            10765\n          ],\n          "media_type": "tv",\n          "name": "My Girlfriend is an Alien",\n          "origin_country": [\n            "CN"\n          ],\n          "vote_count": 2,\n          "first_air_date": "2019-08-19",\n          "backdrop_path": "/kCl7piWv3pypgYfyLFi7ZgFGlYV.jpg",\n          "original_language": "zh",\n          "id": 92779,\n          "vote_average": 9,\n          "overview": "The alien girl Chai Xiaoqi tells the story of Fang Xiaoqi, the overbearing president of the alien girl who died from the \\"Cape Town Planet\\", who was suffering from the \\"rainy weather heterosexual amnesia\\". A high-energy hilarious and romantic cross-star love story. The female host Chai Xiaoqi is not only an alien, but also a true-handed witch. Once she inhales the hormones emitted by the males in the earth, she will fall into the \\"flowery state\\" and suffer from various diseases. The fun and ridiculously ridiculous romance will restore the singularity of the girl in the perfection of the girl. In order to survive on the human earth, Chai Xiaoqi will use his various super powers to solve one accident after another, like a roller coaster. The ups and downs will make the audience hooked. The male lord is cold and is an alternative overbearing president. When it rains, he will forget the opposite sex that appears around him. For this reason, he and the female host will launch various \\"fighting and fighting\\" laughter dramas. The experience of high sweetness and romance is expected to be Strongly slammed the girl\'s heart when it was broadcast.",\n          "poster_path": "/5e2owvs9TWVsuIacTFxJGPp6KVW.jpg"\n        },\n        {\n          "original_name": "Devil Lover \xe0\xb9\x80\xe0\xb8\x9c\xe0\xb8\xa5\xe0\xb8\xad\xe0\xb9\x83\xe0\xb8\x88..\xe0\xb9\x83\xe0\xb8\xab\xe0\xb9\x89\xe0\xb8\x99\xe0\xb8\xb2\xe0\xb8\xa2\xe0\xb8\x9b\xe0\xb8\xb5\xe0\xb8\xa8\xe0\xb8\xb2\xe0\xb8\x88",\n          "id": 74640,\n          "media_type": "tv",\n          "name": "Devil Lover \xe0\xb9\x80\xe0\xb8\x9c\xe0\xb8\xa5\xe0\xb8\xad\xe0\xb9\x83\xe0\xb8\x88..\xe0\xb9\x83\xe0\xb8\xab\xe0\xb9\x89\xe0\xb8\x99\xe0\xb8\xb2\xe0\xb8\xa2\xe0\xb8\x9b\xe0\xb8\xb5\xe0\xb8\xa8\xe0\xb8\xb2\xe0\xb8\x88",\n          "vote_count": 0,\n          "vote_average": 0,\n          "first_air_date": "2015-10-07",\n          "poster_path": "/moThN7iERydEHI2RbfrmhCp69R4.jpg",\n          "genre_ids": [\n            35\n          ],\n          "original_language": "th",\n          "backdrop_path": "/iRYOwW6DRIRwDYVmRWA8nbfaV2c.jpg",\n          "overview": "",\n          "origin_country": [\n            "TH"\n          ]\n        }\n      ],\n      "gender": 2\n    },\n    {\n      "popularity": 39.35,\n      "known_for_department": "Acting",\n      "gender": 1,\n      "id": 2487703,\n      "profile_path": "/jRdDoFoHq36hg4kYxxiLa5DRYUW.jpg",\n      "adult": false,\n      "known_for": [\n        {\n          "poster_path": "/d9PhCnofBEeQGR3lwywTjWKBiXj.jpg",\n          "id": 449924,\n          "vote_count": 346,\n          "video": false,\n          "media_type": "movie",\n          "adult": false,\n          "backdrop_path": "/ekP6EVxL81lZ4ivcqPsoZ72rY0h.jpg",\n          "genre_ids": [\n            28,\n            18,\n            36\n          ],\n          "original_title": "\xe8\x91\x89\xe5\x95\x8f4",\n          "original_language": "cn",\n          "title": "Ip Man 4: The Finale",\n          "vote_average": 6,\n          "overview": "Following the death of his wife, Ip Man travels to San Francisco to ease tensions between the local kung fu masters and his star student, Bruce Lee, while searching for a better future for his son.",\n          "release_date": "2019-12-20"\n        }\n      ],\n      "name": "Vanda Lee"\n    },\n    {\n      "popularity": 28.664,\n      "known_for_department": "Acting",\n      "gender": 1,\n      "id": 556435,\n      "profile_path": "/5MgWM8pkUiYkj9MEaEpO0Ir1FD9.jpg",\n      "adult": false,\n      "known_for": [\n        {\n          "release_date": "2019-05-30",\n          "id": 496243,\n          "vote_count": 5120,\n          "video": false,\n          "media_type": "movie",\n          "vote_average": 8.6,\n          "title": "Parasite",\n          "genre_ids": [\n            35,\n            18,\n            53\n          ],\n          "original_title": "\xea\xb8\xb0\xec\x83\x9d\xec\xb6\xa9",\n          "original_language": "ko",\n          "adult": false,\n          "backdrop_path": "/TU9NIjwzjoKPwQHoHshkFcQUCG.jpg",\n          "overview": "All unemployed, Ki-taek\'s family takes peculiar interest in the wealthy and glamorous Parks for their livelihood until they get entangled in an unexpected incident.",\n          "poster_path": "/7IiTTgloJzvGI1TAYymCfbfl3vT.jpg"\n        },\n....\n
Run Code Online (Sandbox Code Playgroud)\n

vad*_*ian 2

错误说:

array ( )的adult第一项 ( ) 中的key没有值。Index 0knownFor[Production]Index 0resultsActor

请检查一下,截图仅显示[...]


关于您的编辑:

您无法以这种方式解码嵌套字典,请尝试这个

struct TMDBActorsResult: Decodable {
    let page: Int
    let results: [Actor]
    let totalResults: Int
    let totalPages: Int
}

struct Actor: Decodable {
    let popularity: Double
    let knownForDepartment: String
    let gender: Int
    let profilePath: String
    let name: String?
    let id: Int
    let adult: Bool
    let knownFor: [Production]
}

enum MediaType : String, Decodable {
    case tv, movie
}

// MARK: Used for two objects with media type = (Movie or TV)
enum Production : Decodable {
    case movie(Movie), tvShow(TVShow)

    private enum CodingKeys : String, CodingKey { case mediaType }

    init(from decoder : Decoder) throws {
        let dictionaryContainer = try decoder.container(keyedBy: CodingKeys.self)
        let mediaType = try dictionaryContainer.decode(MediaType.self, forKey: .mediaType)
        let container = try decoder.singleValueContainer()
        switch mediaType {
            case .tv: self = .tvShow(try container.decode(TVShow.self))
            case .movie: self = .movie(try container.decode(Movie.self))
        }
    }
}

struct Movie: Decodable {
    let posterPath: String
    let adult: Bool
    let voteCount: Int
    let video: Bool
    let backdropPath: String
    let genreIds: [Int]
    let originalTitle: String
    let originalLanguage: String
    let title: String
    let voteAverage: Double
    let overview: String
    let releaseDate: String
}

struct TVShow: Decodable {
    let originalName: String
    let genreIds: [Int]
    let name: String
    let originCountry: [String]
    let backdropPath: String
    let voteCount: Int
    let firstAirDate: String
    let originalLanguage: String
    let overview: String
    let posterPath: String
    let id: Int
}
Run Code Online (Sandbox Code Playgroud)