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
相关产品推荐
相关产品推荐

