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

调用Google Books API遇kotlinx.serialization解码错误求助

问题:Google Books API数据解析失败(Kotlinx Serialization)

调用Google Books API的https://www.googleapis.com/books/v1/volumes/?q=jazz+history接口获取爵士历史相关书籍数据,该接口返回包含kind、totalItems和items数组的JSON结构。但在Android项目中使用kotlinx.serialization解析时触发致命异常,已用Python的json库验证API响应是合法JSON,但Android端解析失败。

错误日志

FATAL EXCEPTION: main
Process: com.example.bookshelf, PID: 7875
kotlinx.serialization.json.internal.JsonDecodingException: Expected start of the array '[', but had 'EOF' instead at path: $
JSON input: .....\",
        \"quoteSharingAllowed\": false
      }
    }
  ]
}

相关代码

BookshelfApiService.kt

package com.example.bookshelf.network

import retrofit2.http.GET
import retrofit2.http.Path

interface BookshelfApiService {
    @GET("?q=jazz+history")
    suspend fun getBooks(): List<BookshelfBook>

    @GET("{volume_id}")
    suspend fun getVolume(@Path("id") id: String): BookshelfVolume
}

BookshelfBook.kt

package com.example.bookshelf.network

import kotlinx.serialization.Serializable

@Serializable
data class BookshelfBook(
    val id: String
)

BookshelfVolume.kt

package com.example.bookshelf.network

import kotlinx.serialization.Serializable

@Serializable
data class BookshelfVolume(
    val imageLinks: Map<String, String>
)

BookshelfRepository.kt

package com.example.bookshelf.data

import com.example.bookshelf.network.BookshelfApiService
import com.example.bookshelf.network.BookshelfBook
import com.example.bookshelf.network.BookshelfVolume

interface BookshelfRepository {
    suspend fun getBookshelfBooks(): List<BookshelfBook>
    suspend fun getBookshelfVolume(id: String): BookshelfVolume
}

class NetworkBookshelfRepository(
    private val bookshelfApiService: BookshelfApiService
) : BookshelfRepository {
    override suspend fun getBookshelfBooks(): List<BookshelfBook> =
        bookshelfApiService.getBooks()

    override suspend fun getBookshelfVolume(id: String): BookshelfVolume =
        bookshelfApiService.getVolume(id)
}

BookshelfViewModel.kt

package com.example.bookshelf.ui.model

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewmodelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.bookshelf.BookshelfApplication
import com.example.bookshelf.data.BookshelfRepository
import com.example.bookshelf.network.BookshelfVolume
import kotlinx.coroutines.launch
import java.io.IOException

sealed interface BookshelfUiState {
    data class Success(val volumes: List<BookshelfVolume>): BookshelfUiState
    object Loading: BookshelfUiState
    object Error: BookshelfUiState
}

class BookshelfViewModel(
    private val bookshelfRepository: BookshelfRepository
) : ViewModel() {
    var bookshelfUiState: BookshelfUiState by mutableStateOf(BookshelfUiState.Loading)
        private set

    init {
        getBookshelfVolumes()
    }

    fun getBookshelfVolumes() {
        viewModelScope.launch {
            bookshelfUiState = try {
                val books = bookshelfRepository.getBookshelfBooks().map { it.id }
                val volumes = books.map { id ->
                    bookshelfRepository.getBookshelfVolume(id)
                }
                BookshelfUiState.Success(volumes)
            } catch (e: IOException) {
                BookshelfUiState.Error
            }
        }
    }

    companion object {
        val Factory: ViewModelProvider.Factory = viewModelFactory {
            initializer {
                val application = (this[APPLICATION_KEY] as BookshelfApplication)
                val bookshelfRepository = application.container.bookshelfRepository
                BookshelfViewModel(bookshelfRepository = bookshelfRepository)
            }
        }
    }
}

AppContainer.kt

package com.example.bookshelf.data

import com.example.bookshelf.network.BookshelfApiService
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit

interface AppContainer {
    val bookshelfRepository: BookshelfRepository
}

class DefaultAppContainer : AppContainer {

    private val baseUrl =
        "https://www.googleapis.com/books/v1/volumes/"

    private val retrofit = Retrofit.Builder()
        .addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
        .baseUrl(baseUrl)
        .build()

    private val retrofitService: BookshelfApiService by lazy {
        retrofit.create(BookshelfApiService::class.java)
    }

    override val bookshelfRepository: BookshelfRepository by lazy {
        NetworkBookshelfRepository(retrofitService)
    }
}

疑问

  • 如何更好地调试该问题?
  • 错误是否由Google API返回的数据导致,还是我代码中存在遗漏?

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:22:02