彩云天气API数据解析实战:从JSON到Kotlin数据模型的完整指南

彩云天气API数据解析实战:从JSON到Kotlin数据模型的完整指南
1. 彩云天气API简介与准备工作彩云天气是国内领先的高精度气象数据服务提供商其API为开发者提供了实时天气、分钟级降水预报、空气质量等丰富数据。实测下来这套API的响应速度和数据准确性都非常可靠特别适合集成到移动应用中。首先需要注册开发者账号获取API密钥访问彩云天气开发者平台https://dashboard.caiyunapp.com/创建应用后在控制台找到你的Token形如TAkhjf8d1nlSlspN建议将Token存储在Android项目的local.properties文件中CAIYUN_API_TOKEN你的实际Token值注意免费版API每天有1万次调用限额对于个人开发者完全够用。如果商用建议选择付费套餐2. 分析API返回的JSON数据结构我们先看一个实时天气接口的典型响应{ status: ok, result: { realtime: { temperature: 23.5, skycon: PARTLY_CLOUDY_DAY, air_quality: { aqi: { chn: 65 } } }, minutely: { description: 未来30分钟无降水, probability: [0.1, 0.15, 0.2...] } } }这个JSON有几个关键特征多层嵌套结构result → realtime → air_quality混合数据类型数值、字符串、数组字段命名使用下划线风格如air_quality状态码字段status始终在最外层我建议先用Postman测试接口观察完整响应。彩云API文档中提供了这些字段的详细说明skycon天气现象编码CLEAR_DAY表示晴天probability降水概率数组每分钟一个数据点aqi.chn中国标准的空气质量指数3. 创建Kotlin数据模型类针对上述JSON我们需要建立三层数据模型3.1 外层响应结构data class WeatherResponse( SerializedName(status) val status: String, SerializedName(result) val result: WeatherResult )3.2 中间层结构data class WeatherResult( SerializedName(realtime) val realtime: RealtimeWeather, SerializedName(minutely) val minutely: MinutelyForecast? )3.3 最内层细节模型data class RealtimeWeather( SerializedName(temperature) val temp: Float, SerializedName(skycon) val skyCondition: String, SerializedName(air_quality) val airQuality: AirQuality ) data class AirQuality( SerializedName(aqi) val index: AqiIndex ) data class AqiIndex( SerializedName(chn) val chineseIndex: Int )几个关键点使用SerializedName处理Kotlin属性名与JSON字段名的映射可空类型如MinutelyForecast?对应JSON中可能缺失的字段嵌套类要保持与JSON相同的层级关系4. 配置Retrofit网络请求4.1 添加依赖在build.gradle中添加implementation com.squareup.retrofit2:retrofit:2.9.0 implementation com.squareup.retrofit2:converter-gson:2.9.04.2 创建Retrofit实例object WeatherApiClient { private const val BASE_URL https://api.caiyunapp.com/ private val retrofit by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() } val service: WeatherService by lazy { retrofit.create(WeatherService::class.java) } }4.3 定义API接口interface WeatherService { GET(v2.5/{token}/realtime) suspend fun getRealtimeWeather( Path(token) token: String, Query(longitude) lng: Double, Query(latitude) lat: Double ): WeatherResponse }这里使用了Kotlin协程的suspend函数比回调方式更简洁。实际调用时代码非常直观val response WeatherApiClient.service.getRealtimeWeather( token 你的Token, longitude 116.404, latitude 39.915 )5. 处理复杂嵌套结构的技巧当遇到类似分钟级降水预报这种复杂结构时minutely: { status: ok, description: 未来两小时不会下雨, probability: [0.1, 0.15, 0.2, ...], datasource: radar }对应的Kotlin模型可以这样设计data class MinutelyForecast( SerializedName(description) val summary: String, SerializedName(probability) val rainProbabilities: ListFloat, SerializedName(datasource) val source: String ) { fun shouldCarryUmbrella(): Boolean { return rainProbabilities.any { it 0.5f } } }我特别喜欢在这种数据类中添加业务方法比如上面的shouldCarryUmbrella()让数据模型不仅仅是简单的DTO。6. 异常处理与数据验证实际使用中发现几个需要注意的点添加状态校验逻辑if (response.status ! ok) { throw ApiException(服务器返回错误状态) }处理空数据情况val precipitation response.result.minutely?.rainProbabilities ?: emptyList()数值范围校验data class RealtimeWeather( val temp: Float, // ...其他字段... ) { init { require(temp in -50f..50f) { 温度值超出合理范围 } } }7. 完整调用示例结合以上所有内容一个完整的ViewModel实现如下class WeatherViewModel : ViewModel() { private val _weatherData MutableStateFlowWeatherData?(null) val weatherData: StateFlowWeatherData? _weatherData suspend fun fetchWeather(lng: Double, lat: Double) { try { val response WeatherApiClient.service .getRealtimeWeather(BuildConfig.CAIYUN_TOKEN, lng, lat) if (response.status ok) { _weatherData.value response.result.toUiModel() } else { // 处理错误状态 } } catch (e: Exception) { // 处理网络异常 } } } private fun WeatherResult.toUiModel(): WeatherData { return WeatherData( temperature realtime.temp, weatherIcon when(realtime.skyCondition) { CLEAR_DAY - R.drawable.ic_sunny RAIN - R.drawable.ic_rain else - R.drawable.ic_cloudy }, // 其他字段转换... ) }8. 性能优化建议使用缓存减少API调用GET(v2.5/{token}/realtime) suspend fun getRealtimeWeather( Header(Cache-Control) cacheControl: String max-age600, // 其他参数... ): WeatherResponse按需请求字段GET(v2.5/{token}/realtime) suspend fun getLiteWeather( Query(fields) fields: String temperature,skycon, // 其他参数... ): WeatherResponse使用Kotlin的密封类处理不同天气状态sealed class WeatherState { data class Rainy(val intensity: Float) : WeatherState() data class Sunny(val uvIndex: Int) : WeatherState() object Cloudy : WeatherState() }这套方案在我负责的天气应用项目中运行良好JSON解析耗时稳定在5ms以内内存占用也很理想。最复杂的部分是处理那些深层嵌套的空气质量数据但通过合理的数据类设计最终代码保持了很好的可读性。