You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI/Combine解析JSON报错:预期Array<Any>却找到字典如何解决

报错原因及解决方案

核心报错原因

你碰到的Expected to decode Array<Any> but found a dictionary instead错误和Combine无关,是解码类型和接口返回结构不匹配导致的:

  • 你调用的是TMDB的单电影详情接口https://api.themoviedb.org/3/movie/550,返回的JSON根结构是单个字典对象
  • 但你在decode操作中指定的解析类型是[FilmModel].self也就是数组类型,系统尝试把返回的字典解析成数组自然会报错。

其他隐藏解码问题

除了类型不匹配,你还有两处会导致解码失败的问题:

  1. 主模型FilmModel的CodingKeys拼写错误:imdbID对应的rawValue写的是imbd_id,但JSON里的实际键是imdb_id,b和d顺序反了
  2. 所有子模型都没有配置CodingKeys做驼峰命名和下划线命名的映射:比如ProductionCompany的logoPath对应JSON的logo_path、originCountry对应origin_country,ProductionCountry的iso3166_1对应iso_3166_1,SpokenLanguage的englishName对应english_name、iso639_1对应iso_639_1,没有映射的话这些字段都会解码失败。

修复步骤

1. 修正数据服务层的解码逻辑

把解码类型从数组改为单个FilmModel,收到返回值后再存入数组即可:

class FilmDataService {
    @Published var films: [FilmModel] = []
    var filmSubscription: AnyCancellable?

    init() {
        getFilms()
    }

    private func getFilms() {
        guard let url = URL(string: "https://api.themoviedb.org/3/movie/550?api_key=<key>") else { return }
        
        filmSubscription = URLSession.shared.dataTaskPublisher(for: url)
            .subscribe(on: DispatchQueue.global(qos: .default))
            .tryMap { (output) -> Data in
                guard let response = output.response as? HTTPURLResponse,
                      response.statusCode >= 200 && response.statusCode < 300 else {
                    throw URLError(.badServerResponse)
                }
                return output.data
            }
            // 这里把[FilmModel].self改成FilmModel.self
            .decode(type: FilmModel.self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink { (completion) in
                switch completion {
                case .finished:
                    break
                case .failure(let error):
                    print(error)
                }
            } receiveValue: { [weak self] (returnedFilm) in
                // 把单个模型塞进数组
                self?.films = [returnedFilm]
                self?.filmSubscription?.cancel()
            }
    }
}

2. 修正FilmModel的CodingKeys拼写

enum CodingKeys: String, CodingKey {
    case adult
    case backdropPath = "backdrop_path"
    case budget
    case genres
    case homepage
    case id
    // 把原来的imbd_id改成imdb_id
    case imdbID = "imdb_id"
    case originalLanguage = "original_language"
    case originalTitle = "original_title"
    case overview
    case popularity
    case posterPath = "poster_path"
    case productionCompanies = "production_companies"
    case productionCountries = "production_countries"
    case releaseDate = "release_date"
    case revenue
    case runtime
    case spokenLanguages = "spoken_languages"
    case status, tagline, title
    case video
    case voteAverage = "vote_average"
    case voteCount = "vote_count"
}

3. 给所有子模型添加CodingKeys映射

struct ProductionCompany: Codable {
    let id: Int
    let logoPath: String?
    let name, originCountry: String

    enum CodingKeys: String, CodingKey {
        case id
        case logoPath = "logo_path"
        case name
        case originCountry = "origin_country"
    }
}

struct ProductionCountry: Codable {
    let iso3166_1, name: String

    enum CodingKeys: String, CodingKey {
        case iso3166_1 = "iso_3166_1"
        case name
    }
}

struct SpokenLanguage: Codable {
    let englishName, iso639_1, name: String

    enum CodingKeys: String, CodingKey {
        case englishName = "english_name"
        case iso639_1 = "iso_639_1"
        case name
    }
}

如果后续你需要获取电影列表,可以调用TMDB的/movie/popular这类列表接口,返回的根结构里results字段才是电影数组,到时候再对应调整解析逻辑即可。

内容的提问来源于stack exchange,提问作者trevordevelops

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.11 12:24:01