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

Google Books API部分查询JSON解码失败问题求助

问题原因

Google Books API返回的JSON数据里,部分书籍条目可能缺少VolumeInfo中的某些字段(比如出版商、描述、页数等),但你定义的结构体把这些字段都设成了非可选类型(必须存在)。搜索"hello"时刚好所有返回的书籍都包含所有必填字段,所以解码成功;而"weather"的结果里有缺失字段的条目,直接导致解码失败。

解决方案

1. 把结构体中可能缺失的字段改成可选类型

修改VolumeInfo和ImageLinks,给可能为空的字段加上?:

struct VolumeInfo: Codable {
    let title: String
    let authors: [String]? // 部分书籍可能没有作者
    let publisher, publishedDate, description: String?
    let pageCount: Int?
    let imageLinks: ImageLinks?
}

struct ImageLinks: Codable {
    let thumbnail: String?
}

2. 修复图片URL的拼接错误

你的URL拼接语法有误,把"\(baseURL)\. (bookID)"改成正确的字符串插值写法:

let urlString = "\(baseURL)\(bookID)&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api"

3. 捕获具体的解码错误(推荐)

把原来吞掉错误的try?换成try,放在do-catch块里,能打印出具体的解码问题,方便调试:

do {
    let (data, _) = try await URLSession.shared.data(from: url)
    print("Received Data: \(String(data: data, encoding: .utf8) ?? "Data is empty")")
    let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
    results = decodedResponse.items
    print("Results Count: \(results.count)")
} catch {
    print("Error loading or decoding data: \(error)")
}

4. UI中处理可选字段

因为字段变成了可选类型,展示时要处理空值情况,比如:

Text("Authors: \(item.volumeInfo.authors?.joined(separator: ", ") ?? "Unknown")")
完整修改后的代码
import SwiftUI

struct Response: Codable {
    let kind: String
    let totalItems: Int
    let items: [Result]
}

struct Result: Codable, Identifiable { 
    let id: String  
    let etag: String
    let selfLink: String
    let volumeInfo: VolumeInfo
}

struct VolumeInfo: Codable {
    let title: String
    let authors: [String]?
    let publisher, publishedDate, description: String?
    let pageCount: Int?
    let imageLinks: ImageLinks?
}

struct ImageLinks: Codable {
    let thumbnail: String?
}

struct IndustryIdentifier: Codable {
    let type: TypeEnum
    let identifier: String
}

enum TypeEnum: String, Codable {
    case isbn10 = "ISBN_10"
    case isbn13 = "ISBN_13"
}

struct BookSearchView: View {
    
    @State private var results = [Result]()
    
    var body: some View {
        VStack {
            List(results) { item in 
                HStack {
                    let baseURL = "https://books.google.com/books/content?id="
                    let bookID = item.id
                    let urlString = "\(baseURL)\(bookID)&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api"
                    
                    AsyncImage(url: URL(string: urlString)) { phase in
                        switch phase {
                        case .empty:
                            ProgressView()
                        case .success(let image):
                            image.resizable().frame(width: 50, height: 75)
                        case .failure:
                            Image(systemName: "book").frame(width: 50, height: 75)
                        @unknown default:
                            EmptyView()
                        }
                    }
                    
                    VStack(alignment: .leading) {
                        Text(item.volumeInfo.title)
                            .font(.headline)
                        Text("Authors: \(item.volumeInfo.authors?.joined(separator: ", ") ?? "Unknown")")
                            .font(.subheadline)
                        if let publisher = item.volumeInfo.publisher {
                            Text("Publisher: \(publisher)").font(.caption)
                        }
                        Divider()
                        Text("\(item.id)").font(.footnote)
                    }
                }
            }
            .task {
                await loadData()
            }
        }
    }
    
    func loadData() async {
        guard let url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=weather") else {
            print("Invalid URL")
            return
        }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            print("Received Data: \(String(data: data, encoding: .utf8) ?? "Data is empty")")
            let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
            results = decodedResponse.items
            print("Results Count: \(results.count)")
        } catch {
            print("Error loading or decoding data: \(error)")
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 22:14:58