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

Kotlin序列化映射变量时Retrofit解析天气API数据为空

问题:使用@SerialName注解后Retrofit无法正确序列化API数据

我编写了调用Open-Meteo天气API的代码,试图通过Kotlinx Serialization的@SerialName注解将JSON字段映射到数据类的驼峰命名变量,但日志显示解析后数据为空。如果移除@SerialName注解,直接使用与JSON字段完全一致的变量名,数据就能正常存储。已确认Gradle中的插件和依赖均为最新版本。


Retrofit实例代码

object RetrofitInstance {
    private const val BASE_URL = "https://api.open-meteo.com/"

    val instance: WeatherAPI by lazy {
        val retrofit = Retrofit.Builder()
            .baseUrl(RetrofitInstance.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        retrofit.create(WeatherAPI::class.java)
    }
}

API调用代码

api.getWeatherData(
    location.latitude,
    location.longitude,
    "temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code,wind_speed_10m,wind_direction_10m",
    "visibility",
    "temperature_2m_max,temperature_2m_min,sunrise,sunset,daylight_duration,uv_index_max",
    "auto",
    1
).enqueue(object : Callback<WeatherResults> { // 改为WeatherResults
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onResponse(call: Call<WeatherResults>, response: Response<WeatherResults>) {
        // 必要时根据响应码提供更详细的错误信息
        if (response.isSuccessful) {
            response.body()?.let {
                weatherCache.updateCache(it) // 更新天气缓存
                Log.d("API Call", "Full API Response: ${response.body()}")
                onSuccess(it)
            } ?: onFailure(Throwable("响应体为空"))
        } else {
            onFailure(Throwable("错误: ${response.code()}"))
        }
    }

    override fun onFailure(call: Call<WeatherResults>, t: Throwable) {
        onFailure(t)
    }
})

数据类代码

WeatherResults

import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName

@Serializable
data class WeatherResults(
    val current: Current? = null,
    val daily: Daily? = null,
    val hourly: Hourly? = null
)

Current

import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName

@Serializable
data class Current(
    @SerialName("temperature_2m") val temperature: Double?,  // 预期应正常工作
    @SerialName("apparent_temperature") val apparentTemperature: Double?,
    @SerialName("relative_humidity_2m") val relativeHumidity: Int?,  // 匹配JSON字段
    @SerialName("weather_code") val weatherCode: Int?,
    @SerialName("wind_speed_10m") val windSpeed: Double?,
    @SerialName("wind_direction_10m") val windDirection: Double?,
    val visibility: Int?,
    val time: String
)

Daily

import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName

@Serializable
data class Daily(
    val time: List<String?>?,
    @SerialName("weather_code") val weatherCode: List<Int?>?,
    @SerialName("temperature_2m_max") val temperatureMax: List<Double?>?,
    @SerialName("temperature_2m_min") val temperatureMin: List<Double?>?,
    @SerialName("precipitation_probability_max") val precipitationProbability: List<Int?>?,
    @SerialName("apparent_temperature_max") val apparentTemperatureMax: List<Double?>?,
    @SerialName("apparent_temperature_min") val apparentTemperatureMin: List<Double?>?,
    val sunrise: List<String?>?,
    val sunset: List<String?>?,
    @SerialName("sunshine_duration") val sunshineDuration: List<Double?>?,
    @SerialName("uv_index_max") val uvIndexMax: List<Double?>?
)

问题原因及解决办法

问题核心是序列化库不兼容:你在Retrofit中使用的是GsonConverterFactory,但数据类用的是Kotlinx Serialization的@Serializable和@SerialName注解——Gson无法识别这些注解,导致字段匹配失败,解析后数据为空;而移除注解使用与JSON一致的变量名时,Gson的默认字段匹配规则生效,所以能正常解析。

有两种解决方式:

方式1:改用Kotlinx Serialization的Converter

替换Retrofit中的GsonConverterFactory为Kotlinx Serialization的转换器,同时确保依赖包含kotlinx-serialization-json和retrofit2-kotlinx-serialization-converter。

修改后的Retrofit实例代码:

import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType

object RetrofitInstance {
    private const val BASE_URL = "https://api.open-meteo.com/"
    private val json = Json { ignoreUnknownKeys = true } // 忽略API返回的未知字段,避免解析失败

    val instance: WeatherAPI by lazy {
        val retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
            .build()

        retrofit.create(WeatherAPI::class.java)
    }
}

方式2:改用Gson的注解

如果不想更换序列化库,将数据类中的@Serializable和@SerialName替换为Gson的@SerializedName注解,保留data class结构即可。

示例:

import com.google.gson.annotations.SerializedName

data class Current(
    @SerializedName("temperature_2m") val temperature: Double?,
    @SerializedName("apparent_temperature") val apparentTemperature: Double?,
    // 其他字段同理修改
    val visibility: Int?,
    val time: String
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:45:20