在[url=http://sarin.iteye.com/blog/804918]上一篇[/url]中,我们使用了XStream来解析XML(HttpClient 4发送请求)制作了一个电子商务小应用,本篇我们来解析另外一种使用广泛的数据交换技术JSON。
本文意在说明JACKSON组件解析JSON格式文本,并结合中央气象台的天气预报API来展示。天气预报的请求地址是:http://m.weather.com.cn/data/101070201.html,这个文本就是城市代码,101070201代表大连市,执行URL,得到一个返回文本,是JSON格式的,如下:
字段言简意赅,城市名,英文名,日期,农历日期,星期,预报时间,城市代码,6个预报温度(华氏温度,摄氏温度),6个预报天气和风力,剩下的就是一些气象指数什么的了,本例我们仅仅拿当日的信息来说明。
这段文本是我们直接通过浏览器请求来的,但是在程序中如何进行?需要借助网络api的帮助,可以直接使用java网络api,也可以使用第三方类库,这里我们使用Apache的Commons组件中的HttpClient3来说明。首先建立项目工程,使用Maven管理:
[img]http://dl.iteye.com/upload/attachment/352991/225fe723-9904-3692-8584-b7bbd078607b.png[/img]
引入的必要的依赖,如下:
[img]http://dl.iteye.com/upload/attachment/352993/e9df8e17-88f0-34d3-aedd-2a98369d1819.png[/img]
Weather类是封装必要数据的Bean,很简单,如下:
下面就是示例代码了,很简单:
其中提供了获取JSON和处理JSON结果的方法,用法都很简单,这样就可以得到天气预报的结果了:
Jackson处理JSON非常简单,但要了解JSON文本的格式,这样就可以遍历了。到这里我们已经可以通过HttpClient和Jackson组件获取到了我们需要的信息,可以在应用中提供天气预报服务了,但是我们最开始使用的是一个城市代码,比如代表大连的101070201,这个数字是网站自己定义的,而没有其它任何实际含义(比如电话区号,行政代码等),那么要么我们在网站直接获得该城市的代码,否则就无法得到城市代码而去查询API了。其实网站也为我们获取城市代码提供了查询方法,如下:
访问http://m.weather.com.cn/data5/city.xml?level=0,(后面level参数可省略)得到一级列表(省、直辖市、自治区),结果用逗号隔开,id和城市名称使用竖线“|”隔开;结果示例如下:
之后我们继续访问http://m.weather.com.cn/data5/city02.xml?level=1,(后面level参数可省略)得到二级列表。其中02是一级省市的id,结果格式和上一层相同,示例如下(河南):
继续访问http://m.weather.com.cn/data5/city1809.xml?level=3,(后面level参数可省略)得到后一级的id,是区域的id,示例如下(洛阳市):
继续访问http://m.weather.com.cn/data5/city180908.xml,获得偃师市的代码:180908|101180908,那么通过101180908就可以获得偃师市的天气预报。
知道这个规则后就可以在页面使用ajax方式请求地址获取到城市代码然后执行拿到天气预报,而在程序中,我们就需要一级一级的请求来获取代码了。
下面就展示一下这个根据字符串地址获取天气的程序:
程序中对城市名称做了一点限制,直辖市等级为省,没有城市名,有城市名可以不用输入到区县,又如:
这都是可以的,程序中对城市名称的处理可能不很准确,真实环境中这省市区这三个字段应该分别提供,截字符串可能会有偏差,本程序测试了大多数情况,是可行的。
本文系作者的实践探索,欢迎交流,希望对使用者有用。
本文意在说明JACKSON组件解析JSON格式文本,并结合中央气象台的天气预报API来展示。天气预报的请求地址是:http://m.weather.com.cn/data/101070201.html,这个文本就是城市代码,101070201代表大连市,执行URL,得到一个返回文本,是JSON格式的,如下:
{"weatherinfo":{"city":"大连","city_en":"dalian","date_y":"2010年11月21日","date":"庚寅年十月十六","week":"星期日","fchh":"18","cityid":"101070201","temp1":"0℃~6℃","temp2":"1℃~9℃","temp3":"4℃~7℃","temp4":"-1℃~6℃","temp5":"-1℃~7℃","temp6":"-2℃~3℃","tempF1":"32℉~42.8℉","tempF2":"33.8℉~48.2℉","tempF3":"39.2℉~44.6℉","tempF4":"30.2℉~42.8℉","tempF5":"30.2℉~44.6℉","tempF6":"28.4℉~37.4℉","weather1":"多云转晴","weather2":"晴","weather3":"多云","weather4":"晴","weather5":"晴转多云","weather6":"多云转晴","img1":"1","img2":"0","img3":"0","img4":"99","img5":"1","img6":"99","img7":"0","img8":"99","img9":"0","img10":"1","img11":"1","img12":"0","img_single":"0","img_title1":"多云","img_title2":"晴","img_title3":"晴","img_title4":"晴","img_title5":"多云","img_title6":"多云","img_title7":"晴","img_title8":"晴","img_title9":"晴","img_title10":"多云","img_title11":"多云","img_title12":"晴","img_title_single":"晴","wind1":"西北风5-6级转北风4-5级","wind2":"北风转南风4-5级","wind3":"南风4-5级转北风5-6级","wind4":"北风5-6级转4-5级","wind5":"北风转南风4-5级","wind6":"南风转北风4-5级","fx1":"西北风","fx2":"北风","fl1":"5-6级转4-5级","fl2":"4-5级","fl3":"4-5级转5-6级","fl4":"5-6级转4-5级","fl5":"4-5级","fl6":"4-5级","index":"凉","index_d":"天气凉,建议着厚外套加毛衣等春秋服装。年老体弱者宜着大衣、呢外套加羊毛衫。","index48":"凉","index48_d":"天气凉,建议着厚外套加毛衣等春秋服装。年老体弱者宜着大衣、呢外套加羊毛衫。","index_uv":"中等","index48_uv":"弱","index_xc":"较适宜","index_tr":"一般","index_co":"较不舒适","st1":"2","st2":"-4","st3":"7","st4":"1","st5":"5","st6":"1","index_cl":"较不宜","index_ls":"基本适宜"}}
字段言简意赅,城市名,英文名,日期,农历日期,星期,预报时间,城市代码,6个预报温度(华氏温度,摄氏温度),6个预报天气和风力,剩下的就是一些气象指数什么的了,本例我们仅仅拿当日的信息来说明。
这段文本是我们直接通过浏览器请求来的,但是在程序中如何进行?需要借助网络api的帮助,可以直接使用java网络api,也可以使用第三方类库,这里我们使用Apache的Commons组件中的HttpClient3来说明。首先建立项目工程,使用Maven管理:
[img]http://dl.iteye.com/upload/attachment/352991/225fe723-9904-3692-8584-b7bbd078607b.png[/img]
引入的必要的依赖,如下:
[img]http://dl.iteye.com/upload/attachment/352993/e9df8e17-88f0-34d3-aedd-2a98369d1819.png[/img]
Weather类是封装必要数据的Bean,很简单,如下:
package weather;
public class Weather {
private String city;// 城市名
private String date;// 日期:yyyy年MM月d日
private String lunarDate;// 农历日期/当日有
private String week;// 星期
private String fcTime;// 预报时间:24制小时数/当日有
private String temperature;// 当日气温
private String weather;// 天气
private String wind;// 风力
// 省略了getter和setter方法
@Override
public String toString() {
return "Weather [city=" + city + ", date=" + date + ", fcTime="
+ fcTime + ", lunarDate=" + lunarDate + ", temperature="
+ temperature + ", weather=" + weather + ", week=" + week
+ ", wind=" + wind + "]";
}
}
下面就是示例代码了,很简单:
package weather;
import java.util.HashMap;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.MappingJsonFactory;
/**
* 天气预报服务,解析JSON
*
* @author Nanlei
*
*/
public class Demo {
private static String URL = "http://m.weather.com.cn/data/101070201.html";// 请求的地址
private static HttpClient client;
private static GetMethod getMethod;
/**
* 静态块初始化所需对象
*/
static {
client = new HttpClient();
getMethod = new GetMethod(URL);
}
/**
* 获取获得的Json结果
*
* @return
*/
public static String getJsonText() {
String jsonText = null;
try {
int status = client.executeMethod(getMethod);
if (status == HttpStatus.SC_OK) {// HTTP 200 OK
jsonText = getMethod.getResponseBodyAsString();// 获取字符串形式的结果,建议使用流式结果
}
} catch (Exception e) {
System.err.println(e);
}
return jsonText;
}
/**
* 处理Json结果并封装Bean
*
* @param jsonText
* @return
*/
public static Weather getWeatherBean(String jsonText) {
// Weather对象
Weather weather = new Weather();
try {
JsonFactory jsonFactory = new MappingJsonFactory();
// Json解析器
JsonParser jsonParser = jsonFactory.createJsonParser(jsonText);
// 跳到结果集的开始
jsonParser.nextToken();
// 接受结果的HashMap
HashMap<String, String> map = new HashMap<String, String>();
// while循环遍历Json结果
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
// 跳转到Value
jsonParser.nextToken();
// 将Json中的值装入Map中
map.put(jsonParser.getCurrentName(), jsonParser.getText());
}
// 将数据封装
weather.setCity(map.get("city"));
weather.setDate(map.get("date_y"));
weather.setLunarDate(map.get("date"));
weather.setWeek(map.get("week"));
weather.setFcTime(map.get("fchh"));
weather.setTemperature(map.get("temp1"));
weather.setWeather(map.get("weather1"));
weather.setWind(map.get("wind1"));
} catch (Exception e) {
System.err.println(e);
}
return weather;
}
/**
* 主函数
*
* @param args
*/
public static void main(String[] args) {
System.out.println(getWeatherBean(getJsonText()));
}
}
其中提供了获取JSON和处理JSON结果的方法,用法都很简单,这样就可以得到天气预报的结果了:
Weather [city=大连, date=2010年11月21日, fcTime=18, lunarDate=庚寅年十月十六, temperature=0℃~6℃, weather=多云转晴, week=星期日, wind=西北风5-6级转北风4-5级]
Jackson处理JSON非常简单,但要了解JSON文本的格式,这样就可以遍历了。到这里我们已经可以通过HttpClient和Jackson组件获取到了我们需要的信息,可以在应用中提供天气预报服务了,但是我们最开始使用的是一个城市代码,比如代表大连的101070201,这个数字是网站自己定义的,而没有其它任何实际含义(比如电话区号,行政代码等),那么要么我们在网站直接获得该城市的代码,否则就无法得到城市代码而去查询API了。其实网站也为我们获取城市代码提供了查询方法,如下:
访问http://m.weather.com.cn/data5/city.xml?level=0,(后面level参数可省略)得到一级列表(省、直辖市、自治区),结果用逗号隔开,id和城市名称使用竖线“|”隔开;结果示例如下:
01|北京,02|上海,03|天津,04|重庆,05|黑龙江,06|吉林,07|辽宁,08|内蒙古,09|河北,10|山西,11|陕西,12|山东,13|新疆,14|西藏,15|青海,16|甘肃,17|宁夏…
之后我们继续访问http://m.weather.com.cn/data5/city02.xml?level=1,(后面level参数可省略)得到二级列表。其中02是一级省市的id,结果格式和上一层相同,示例如下(河南):
1801|郑州,1802|安阳,1803|新乡,1804|许昌,1805|平顶山,1806|信阳,1807|南阳,1808|开封,1809|洛阳,1810|商丘,1811|焦作,1812|鹤壁,1813|濮阳,1814|周口,1815|漯河,1816|驻马店,1817|三门峡,1818|济源
继续访问http://m.weather.com.cn/data5/city1809.xml?level=3,(后面level参数可省略)得到后一级的id,是区域的id,示例如下(洛阳市):
180901|洛阳,180902|新安,180903|孟津,180904|宜阳,180905|洛宁,180906|伊川,180907|嵩县,180908|偃师,180909|栾川,180910|汝阳
继续访问http://m.weather.com.cn/data5/city180908.xml,获得偃师市的代码:180908|101180908,那么通过101180908就可以获得偃师市的天气预报。
知道这个规则后就可以在页面使用ajax方式请求地址获取到城市代码然后执行拿到天气预报,而在程序中,我们就需要一级一级的请求来获取代码了。
下面就展示一下这个根据字符串地址获取天气的程序:
package weather;
import java.io.InputStream;
import java.util.HashMap;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.MappingJsonFactory;
public class Demo2 {
public static void main(String[] args) throws Exception {
String name = "河南洛阳偃师";
StringBuffer cityCode = new StringBuffer();
String provinceName = null;// 省份名称
String cityName = null;// 城市名称
String districtName = null;// 区县名称
if (name.length() >= 6) {
provinceName = name.substring(0, 2);
cityName = name.substring(2);
districtName = name.substring(4);
} else if (name.length() == 5) {
provinceName = name.substring(0, 2);
cityName = name.substring(2);
districtName = "";
} else if (name.length() <= 4) {
provinceName = name.substring(0, 2);
cityName = "";
districtName = name.substring(2);
} else {
System.out.println("城市名称错误");
System.exit(0);
}
// 下面开始根据这三个名称来获取weather.com.cn的城市代码
HttpClient client = new HttpClient();
GetMethod get = new GetMethod("http://m.weather.com.cn/data5/city.xml");// 获取省份
String provinceStr = null;
if (client.executeMethod(get) == HttpStatus.SC_OK) {
provinceStr = get.getResponseBodyAsString();
}
String[] provinceArray = provinceStr.split(",");
String provinceCode = null;
for (int i = 0; i < provinceArray.length; i++) {// 遍历省份
if (provinceArray[i].substring(3).indexOf(provinceName) != -1) {
provinceCode = provinceArray[i].substring(0, 2);
cityCode.append(provinceCode);// 获得省份编码
}
}
// 城市代码
if (Integer.parseInt(provinceCode) <= 4
|| Integer.parseInt(provinceCode) >= 32) {// 直辖市和港澳台
get = new GetMethod("http://m.weather.com.cn/data5/city"
+ provinceCode + "01.xml");
if (client.executeMethod(get) == HttpStatus.SC_OK) {
String[] districtArray = get.getResponseBodyAsString().split(
",");
for (int i = 0; i < districtArray.length; i++) {
if (districtName.indexOf(districtArray[i].substring(7)) != -1) {
cityCode.append("01").append(
districtArray[i].substring(4, 6));// 直辖市或港澳台代码获取结束,6位
}
}
if (cityCode.length() < 6) {
cityCode.append("0101");
}
}
} else {// 省市
get = new GetMethod("http://m.weather.com.cn/data5/city"
+ provinceCode + ".xml");
String[] cityArray = null;
if (client.executeMethod(get) == HttpStatus.SC_OK) {
cityArray = get.getResponseBodyAsString().split(",");// 获取到了城市数组
}
if (StringUtils.isEmpty(cityName)) {// 没有取到城市名
for (int i = 0; i < cityArray.length; i++) {
if (districtName.indexOf(cityArray[i].substring(5)) != -1) {
cityCode.append(cityArray[i].substring(2, 4)).append(
"01");// 省直管地区代码获取结束,6位
}
}
} else if (StringUtils.isNotEmpty(cityName)) {// 取得了城市名
for (int i = 0; i < cityArray.length; i++) {
if (cityName.indexOf(cityArray[i].substring(5)) != -1) {
cityCode.append(cityArray[i].substring(2, 4));
}
}
get = new GetMethod("http://m.weather.com.cn/data5/city"
+ cityCode.toString() + ".xml");// 请求4位城市代码
String[] districtArray = null;
if (client.executeMethod(get) == HttpStatus.SC_OK) {
districtArray = get.getResponseBodyAsString().split(",");
}
for (int i = 0; i < districtArray.length; i++) {
if (districtName.indexOf(districtArray[i].substring(7)) != -1) {
cityCode.append(districtArray[i].substring(4, 6));
}
}
if (cityCode.toString().length() == 4) {
cityCode.append("01");
}
}
}
if (cityCode.length() == 6) {
// 获取城市代码结束,获取天气页面代号
GetMethod weatherCodeGet = new GetMethod(
"http://m.weather.com.cn/data5/city" + cityCode.toString()
+ ".xml");
String weatherCode = "";
if (client.executeMethod(weatherCodeGet) == HttpStatus.SC_OK) {
weatherCode = weatherCodeGet.getResponseBodyAsString()
.substring(7);
}
GetMethod weatherGet = new GetMethod(
"http://m.weather.com.cn/data/" + weatherCode + ".html");
InputStream weatherInfo = null;
if (client.executeMethod(weatherGet) == HttpStatus.SC_OK) {
weatherInfo = weatherGet.getResponseBodyAsStream();
//
HashMap<String, String> map = new HashMap<String, String> ();
JsonFactory jsonFactory = new MappingJsonFactory();
JsonParser jsonParser = jsonFactory
.createJsonParser(weatherInfo);
// 跳到结果集的开始
jsonParser.nextToken();
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
// 跳转到Value
jsonParser.nextToken();
map.put(jsonParser.getCurrentName(), jsonParser.getText());
}
// 封装VO,向页面传递数据
Weather todayWeather = new Weather();
// 当日数据
todayWeather.setCity(map.get("city"));
todayWeather.setDate(map.get("date_y"));
todayWeather.setLunarDate(map.get("date"));
todayWeather.setWeek(map.get("week"));
todayWeather.setFcTime(map.get("fchh"));
todayWeather.setTemperature(map.get("temp1"));
todayWeather.setWeather(map.get("weather1"));
todayWeather.setWind(map.get("wind1"));
System.out.println(todayWeather);
}
} else {
System.out.println("没有获取到天气信息");
}
}
}
程序中对城市名称做了一点限制,直辖市等级为省,没有城市名,有城市名可以不用输入到区县,又如:
String name = "河南洛阳";
String name = "河南洛阳涧西";
这都是可以的,程序中对城市名称的处理可能不很准确,真实环境中这省市区这三个字段应该分别提供,截字符串可能会有偏差,本程序测试了大多数情况,是可行的。
本文系作者的实践探索,欢迎交流,希望对使用者有用。