JSON数据解析——彩云天气api

彩云天气API

首先在彩云天气官网注册一个账号,注册地址是:

https://dashboard.caiyunapp.com/

注册之后可查看API文档

天气app所需数据

1、地区数据

访问地址接口可查询到全球绝大多数地区的数据信息

https://api.caiyunapp.com/v2/place?query=北京&token={token}&lang=zh_CN

query参数指定的是要查询的关键字(如地名),token传入刚刚申请到的令牌值。服务器会返回我们一段JSON格式的数据,我们所需获取的数据有name(该地区的名字)、location(该地区的经纬度)、formatted_address(该地区的地址)

{"status":"ok","query":"北京","places":[{"name":"北京南站","formatted_address":"中国 北京市 丰台区 永外大街车站路12号","location":{"lat":39.865246,"lng":116.378517}},{"name":"北京西站","formatted_address":"中国 北京市 丰台区 莲花池东路118号","location":{"lat":39.89491,"lng":116.322056}},{"name":"北京站","formatted_address":"中国 北京市 东城区 毛家湾胡同甲13号","location":{"lat":39.902842,"lng":116.427341}},{"name":"北京北站","formatted_address":"中国 北京市 西城区 北滨河路1号","location":{"lat":39.944876,"lng":116.353063}},{"name":"北京东站(地铁站)","formatted_address":"中国 北京市 朝阳区 (在建)28号线","location":{"lat":39.902267,"lng":116.482682}}]
}

其展示效果如下:

2、实时天气数据

实时天气信息API接口:

https://api.caiyunapp.com/v2.5/{token}/101.6656,39.2072/realtime

token仍是刚刚传入的令牌值,101.6656,39.2072分别是维度和经度,中间用逗号隔开,这样服务器就会把该地区的实时天气信息以JSON格式返回给我们,我们从中提取需要的数据,realtime中包含的就是当前地区的实时天气信息,其中temperature表示当前的温度,skycon表示当前的天气情况,air_quality中包含一些空气质量的数据,这里使用aqi的值作为空气质量指数显示在界面上

{"status":"ok","result":{"realtime":{"temperature":17.0,"skycon":"PARTLY_CLOUDY_DAY","air_quality":{"aqi":{"chn":78}}}}
}

其展示效果如下:

3、未来几天的天气数据

未来几天的天气信息API接口

https://api.caiyunapp.com/v2.5/{token}/116.378517,39.865246/daily.json

这个接口返回的数据也比较复杂,我们依旧只需提取需要的数据
daily包含的就是当前地区未来几天的天气信息,temperature表示未来几天的温度值,skycon表示未来几天的天气情况,life_index中包含一些生活指数,coldRish表示感冒指数,CarWashing表示洗车指数,ultraviolet表示紫外线指数,dressing表示穿衣指数

{“status:"  "ok","result": {"daily": {"temperature": [ {"max":18.0,"min":9.0},...],"skycon":[{"date":"2022-03-28T00:00+08:00","value":"PARTLY_CLOUDY_DAY"},...]"life_index":{"coldRisk":[{"desc":"极易发"},...],"carWashing"[{"desc":"较不适宜"},...],"ultraviolet":[{"desc":"强"},...],"dressing":[{"desc:"冷""},...]}}}
}

其展示效果如下:

使用retrofit请求api获取数据

以上述中地区部分为例

{"status":"ok","query":"北京","places":[{"name":"北京南站","formatted_address":"中国 北京市 丰台区 永外大街车站路12号","location":{"lat":39.865246,"lng":116.378517}},{"name":"北京西站","formatted_address":"中国 北京市 丰台区 莲花池东路118号","location":{"lat":39.89491,"lng":116.322056}},{"name":"北京站","formatted_address":"中国 北京市 东城区 毛家湾胡同甲13号","location":{"lat":39.902842,"lng":116.427341}},{"name":"北京北站","formatted_address":"中国 北京市 西城区 北滨河路1号","location":{"lat":39.944876,"lng":116.353063}},{"name":"北京东站(地铁站)","formatted_address":"中国 北京市 朝阳区 (在建)28号线","location":{"lat":39.902267,"lng":116.482682}}]
}

分析这段json数据:

  • 第一层是一个花括号,即jsonObject对象,其中有status、query属性以及一个places的JSON数组(中括号为JSONArray数组)
  • 第二层places的JSON数组,其中有name、formatted_address、location
  • 第三层location有lat和lng两个属性

kotlin代码实现请求数据

我们在定义这一部分数据模型时,对每一层都需要有一个数据类,按照以上分析的JSON格式来定义
新建一个PlaceResponse.kt文件,并在这个文件中编写如下代码

/*** 第一层:status和places的JSON数组*/
data class PlaceResponse(val status: String, val places: List<Place>)
/*** 第二层:name、location、formatted_address* 由于JSON中的一些字段命名可能与kotlin的命名规范不一致,所以使用了@SerializedName注解* @ SerializedName注解使JSON字段和kotlin字段之间建立映射关系*/
data class Place(val name: String, val location: Location,@SerializedName("formatted_address") val address: String)
/*** 第三层:lng、lat*/
data class Location(val lng: String, val lat: String)

定义好数据模型之后,我们可以开始编写网络层相关的代码了。首先定义一个用于访问彩云天气城市搜索API的Retrofit接口

还记得上面那个测试地区JSON数据的API接口吗?就是使用刚刚的接口,不过我们需要向其中传入我们的“query”和“token”以便可以通过搜索框查到大部分地区的数据

interface PlaceService {/*** 当调用searchPlaces时,Retrofit就会自动发起一个GET请求,去访问GET注解中配置的地址* 其中token和lang参数都是不变的,可以直接固定写在注解中* query参数是需要动态指定的,这里使用@Query注解的方式来实现* * 另外searchPlaces的返回值被声明成Call<PlaceResponse>,这样JSON数据就会自动解析成PlaceResponse对象*/@GET("v2/place?token=${SunnyWeatherApplication.TOKEN}&lang=zh_CN")fun searchPlaces(@Query("query") query: String) : Call<PlaceResponse>
}

现在,我们就可以开始测试进行连接通信了
新建一个Test测试类,写上主函数进行测试,需要注意的是,kotlin的主函数需要在上方加上@JvmStatic注解

class Test {companion object{//BASE_URL不会变,直接传入我们所需的彩云天气的URL用于指定Retrofit的根路径private const val BASE_URL = "https://api.caiyunapp.com/"//main函数入口@JvmStaticfun main(args: Array<String>) {//从控制台输入要查询的地区名称val placeStr = readLine()//获取PlaceService接口的动态代理对象val retrofit = Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build()//创建API接口对象val placeService = retrofit.create(PlaceService::class.java)//创建一个请求对象val call: Call<PlaceResponse> = placeService.searchPlaces(placeStr!!)//开始进行连接请求call.enqueue(object : Callback<PlaceResponse> {override fun onResponse(call: Call<PlaceResponse>,response: Response<PlaceResponse>) {val placeResponse = response.body();if (placeResponse?.status == "ok"){val places = response.body()?.placesfor (place in places!!){val name = place.nameval address = place.addressval location = place.locationval lng = location.lngval lat = location.latprintln("地名:${name},  地址:${address},  经纬度(${lng},${lat})")}}}override fun onFailure(call: Call<PlaceResponse>, t: Throwable) {TODO("Not yet implemented")println("error")}})}}
}

以下就是服务器返回的数据被自动解析成JSON对象的结果

我们对数据进行进一步提取后就完成我们本次的网络请求了

上面Test类只是进行一个简单测试,在实际项目(以《第一行代码》彩云天气开发为实例进行学习)中我们不可能每次都去写一个单独的对象去获取Service接口

因此在项目中,为了更好的使用Service接口,Retrofit构建器一般会使用以下写法

新建一个ServiceCreator 单例类

object ServiceCreator {//BASE_URL不会变,直接传入我们所需的彩云天气的URL用于指定Retrofit的根路径private const val BASE_URL = "https://api.caiyunapp.com/"private val retrofit = Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build()/*** 提供一个外部可见的create方法并接收一个class类型参数* 这样经过封装之后,通过参数的不同可以创建相应的Service接口,而不用为每一个类都单独写一个构造器*/fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)inline fun <reified T> create(): T = create(T::class.java)
}

接下来需要定义一个统一的网络数据源访问入口,对所有的网络请求的API进行封装。

object SunnyWeatherNetwork {//使用ServiceCreator创建一个placeService接口的动态代理对象private val placeService = ServiceCreator.create(PlaceService::class.java)/*** 当外部调用SunnyWeatherNetwork的searchPlaces时,retrofit会立即发出网络请求* 同时当前的协程也会被阻塞住,知道服务器响应我们的请求之后* await()函数会将解析出来的数据模型对象取出并返回*///定义searchPlaces函数并调用searchPlaces()方法以发起搜索城市数据请求suspend fun searchPlaces(query: String) = placeService.searchPlaces(query).await()private suspend fun <T> Call<T>.await(): T {//suspend挂起函数关键字//await()是一个挂起函数,给它声明一个泛型T,并将await()函数定义成call<T>的扩展函数return suspendCoroutine { continuation ->enqueue(object : Callback<T> {//直接调用enqueue()方法让Retrofit发起网络请求override fun onResponse(call: Call<T>, response: Response<T>) {val body = response.body()if (body != null) continuation.resume(body)else continuation.resumeWithException(RuntimeException("response body is null"))}override fun onFailure(call: Call<T>, t: Throwable) {continuation.resumeWithException(t)}})}}
}

这样每次需要使用某个API接口时,只需要在SunnyWeatherNetwork 中创建相关的接口对象传入对应的类型参数就可以获取到了
那么现在我们对获取地区数据进行测试,其实只需要通过SunnyWeatherNetwork.searchPlaces()就能得到地区数据了

因为searchPlaces()被设置为suspend挂起,因此给刚刚的main()加上一个runBlocking(调用了 runblocking 的线程会阻塞直到内部的协程执行完毕)这样就可以执行了

companion object{@JvmStaticfun main(args: Array<String>) = runBlocking{//从控制台输入要查询的地区名称val placeStr = readLine()/*** 在SunnyWeatherNetwork中已经创建了placeService接口的动态代理对象* 若要使用别的接口,也只需要在SunnyWeatherNetwork添加方法即可*/val placeResponse = SunnyWeatherNetwork.searchPlaces(placeStr!!)if (placeResponse.status == "ok"){val places = placeResponse.placesfor (place in places){val name = place.nameval address = place.addressval location = place.locationval lng = location.lngval lat = location.latprintln("地名:${name},  地址:${address},  经纬度(${lng},${lat})")}}}
}

其实现在真正发送请求只需要一行代码:

val placeResponse = SunnyWeatherNetwork.searchPlaces(placeStr!!)

而且现在调用其他的API接口都可以按照这种模式进行编写

彩云天气JSON数据解析相关推荐

  1. android天气json数据,android JSON解析数据 android解析天气预报

    概要 笔者近期做到对天气预报JSON数据解析,在此小记. 天气预报接口:http://wthrcdn.etouch.cn/weather_mini?citykey=101200101 JSON数据如下 ...

  2. android studio json数据解析汇总(备忘)

    之前写过一个,但是写不太全,结果自己都看不懂,希望这个以后能看懂吧. 以两个例子说明json数据解析,一个是最简单的,一个是比较难的. 必须知道的:JSONObject和JSONArray JSONO ...

  3. 基于QT的【第一个项目】设计+所有组件配合使用+网络编程局域网通信+文件IO操作+登录界面和头像+多界面跳转+JSON数据解析+表情包制作

    基于QT的第一个项目+所有组件配合使用+网络编程局域网通信+文件IO操作+登录界面和头像+多界面跳转+JSON数据解析+表情包制作 第一阶段 网络编程局域网TCP/IP聊天QT实现 main.c ma ...

  4. linux下json数据解析,JSON数据解析 - iOS学习笔记_Linux编程_Linux公社-Linux系统门户网站...

    在之前的<iOS学习--xml数据解析(九)>http://www.linuxidc.com/Linux/2014-02/97020p9.htm 介绍了xml数据解析,这一篇简单介绍一下J ...

  5. Android JSON数据解析(GSON方式)

    要创建和解析JSON数据,也可以使用GSON来完成.GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库.使用GSON,可以很容易的将一串JSON数据转换为一个Jav ...

  6. Rxjava、Retrofit返回json数据解析异常处理

    每个App都避免不了要进行网络请求,从最开始的用谷歌封装的volley到再到android-async-http再到OKHttpUtils再到现在的Retrofit和RxJava,从我自己用后的体验来 ...

  7. 29-30Python多线程、多线程、使用threading模块创建线程;JSON数据解析、编码为JSON类型转换对应表、Python类型转换对应表、json.dumps与json.loads

    29Python3多线程 多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序中的任务放到后台去处理. 用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些 ...

  8. Android学习笔记44:JSON数据解析

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,为Web应用开发提供了一种理想的数据交换格式. 本文将主要介绍在Android ...

  9. Android json数据解析及简单例子

    JSON的定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性.业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据 ...

最新文章

  1. python读取excel日期内容读出来是数字-Python xlrd读取excel日期类型的2种方法
  2. windows C++ Opengl基础框架源码
  3. C#规范整理·异常与自定义异常
  4. filewriter判断是否关闭_IO流详解
  5. Webmax 2.0开启无限量下载
  6. 计算机管理员怎么转让,钉钉怎么转让主管理员身份? 钉钉更换主管理员的技巧...
  7. idea中安装uml工具插件
  8. ios 切换多任务后台界面应用闪屏
  9. VMware出现“该虚拟机似乎正在使用中”问题
  10. Vue中导出json【基于file-saver】
  11. 李志敏 华中农业大学计算机学院,华中农业大学计算机教育论坛举行
  12. 新手剪辑师秒变大神 高级感视频剪辑的几种常用技巧
  13. apache ii评分怎么评_如何正确进行APACHE II评分
  14. HTML识别文本空格回车换行展示
  15. python开发基础之数据类型、字符编码、文件操作
  16. 51单片机c语言程序控制,51单片机C语言程序设计源代码
  17. 金立m3是相当于Android,对这些手机进行深度测评后,原来金立M3的性价比更高
  18. stc8a控制MG90S舵机
  19. Java调用阿里SMTP服务器实现邮件发送
  20. python 今日头条增加流量_今日头条怎么提高推荐量?

热门文章

  1. 三维模型旋转-python
  2. python sklearn 线性回归 报错_(转)Python- sklearn之最小二乘法
  3. CrossOver Linux2022虚拟机工具如何下载使用教程
  4. c语言程序设计字符组,C语言程序设计入门:字符串
  5. 2019SCUT_三七互娱杯 C - HRY and fibonacci
  6. 程序员必备的编程助手 SmartCoder助你轻松集成HMS Core
  7. LinearLayout中控件之间的距离
  8. 【Deep Learning】Squeeze-and-Excitation Networks(SENet-2017年ImageNet冠军)
  9. WAP WEB
  10. 7-88 晨阳哥哥之世界末日 (15分)