前言
java如此庞大的生态之下,json工具类有很多,我常用的是阿里巴巴的fastJson。在最开始使用的时候,我反序列化,用的是这个方法
public static <T> T parseObject(String text, Class<T> clazz)
需要传两个参数,第一个是json字符串,第二个是需要序列化成的对象的class,这样的好处是能直接转成对象,然后操作对象,对象也能复用。但是有有个缺点:需要预先把对象创建出来,如果json层次很深,那就需要定义很多的类,举个例子:
{
"username": "张三",
"age": "25",
"certificates": [
{
"title": "英语4级",
"date": "2019-12-10"
},
{
"title": "计算机二级",
"date": "2018-12-10"
},
{
"title": "教师资格证",
"date": "2019-6-10"
}
],
"address": {
"province": "四川省",
"city": "成都市",
"county": "天赋新区"
}
}
这个简简单单的json,就需要定义三个类:information、certificate、address,如果更复杂一点的json,需要定义的类更多(这个时候只有羡慕前端同事哈哈)。有的时候我们可能只是需要拿到或者操作json里面的一点点内容,比如:我只想拿到拿到张三同学获得了多少个certificate,为了拿到一个统计的和,就需要定义三个类,也太不爽了吧。
parseObject(String text)
明明只需要一点东西,却需要定义很多类,让人很不爽,所以引入我们今天的主角之一:
public static JSONObject parseObject(String text)
可以看到,这个方法只有一个参数,String类型的字符串,有的同学就像思考了,那要将这个字符串反序列化成什么对象呢?返回的这个JSONObject 又是什么东西呢?
善于思考是好事,但只思考不实际行动也没用是吧,咱们直接上代码测试
String jsonStr = "{\"username\":\"张三\",\"age\":\"25\",\"certificate\":[{\"title\":\"英语4级\",\"date\":\"2019-12-10\"},{\"title\":\"计算机二级\",\"date\":\"2018-12-10\"},{\"title\":\"教师资格证\",\"date\":\"2019-6-10\"}],\"address\":{\"province\":\"四川省\",\"city\":\"成都市\",\"county\":\"天赋新区\"}}";
JSONObject information = JSONObject.parseObject(jsonStr);
System.out.println(information);
不测试不知道,一测试吓一跳,好家伙,打印出来的是原来那个json字符串,那到底有没有序列化呢?
System.out.println(Object x)是我们早期学习和调试常用的手段之一,但其实有时候这个方法并不好使,因为我们System.out.println(Object x),其实是调用了x.toString这个方法去展示x这个对象的,所以说一个类,我重写了toString,你再去System.out.println,那么只能看到我想让你看到的。那怎么办?看源码撒,去看看JSONObject 内部是怎么设计的。
要看一个类,该怎么下手呢?还记得最开始学习java、学习面向对象的时候吗?类是由什么组成的?属性和方法,属性是什么?属性是描述这个类的静态属性,比如一个人的身高体重。方法是什么?方法是描述这个类的动态属性,比如吃饭,吃饭是动态的,吃什么,怎么吃,还得看吃的是什么,也就是传递的参数……所以,我们也从属性和方法下手,看看JSONObject 有哪些属性和方法。
JSONObject的属性
方法太多,咱们先忽略,先看属性,what?居然只有一个Map<String, Object>属性?我的jsonStr可是有好多属性、好多K-V键值对的,等等,K-V键值对,我好像想到了什么,map不也是以K-V键值对的形式来存储数据的吗?哦,我好像懂了,咱们再结合断点看看
的确就是咱们想的那样,JSONObject 内部就是用Map来存储数据的,把json字符串中的键和值,转成Map中K-V键值对。有的同学又发言了,json字符串中,value还可能是一个对象、一个数组,这怎么转?这也很简单嘛,你看JSONObject 中Map的泛型<String, Object>,value是Object,json串中value是一个对象,那Map中value就是JSONObject 呗,json串中value是数组,那Map中value就是List或数组呗(原理是这样,但其实是一个自己实现的List:JSONArray)
JSONObject的方法
咱们理清了内部结构之后,再来看看JSONObject对外提供了哪些方法来操作map里面K-V,方法我就不看源码了,太多太杂,我这样看方法
用一个JSONObject对象去点,然后根据工具的提示,看方法的名称、参数、返回值和个人经验去推测这些方法的作用,这样效率比较高。比如
entrySet,返回了一个Set<Map.Entry<String, Object>>,应该就是把json串里面所有的k-v用Set装起来,为啥用Set呢?因为key肯定不能重复,其他的方法,应该也能很简单的推测出来。
stream流
这个是java 8推出的新特性,简单理解就是简化循环,结合方法引用,lambda表达式,用起来很爽。但是维护起来不好维护。给大家贴个比较极端的例子
stream流用起来是很爽,一行,一直点下去,通畅爽透。维护起来可就是恶魔了,建议大伙儿stream不要写太复杂了。
stream流里内容也很多,一时半会写不完,抽空写个系列,今天咱们就不说了。
实战: stream处理 fastJSON反序列化的JSONObject对象
json串是类似这样的:
{
"comprehensiveRank": {
"year": "2021",
"valueArr": [
{
"value": "12"
}
]
},
"all_land": "45",
"land_use": "45",
"land_use_rate": 100,
"total_importAndExport_lastyear": "45",
"total_importAndExport_thisyear": "45",
"importAndExport_rate": 0,
"total_company_value_lastyear": "45",
"total_company_value_thisyear": "45",
"company_value_rate": 0,
"importAndExport_unit_area_lastyear": "45",
"importAndExport_unit_area_thisyear": "45",
"importAndExport_unit_area_rate": 0,
"total_import_lastyear": "45",
"total_import_thisyear": "45",
"total_import_rate": 0,
"total_export_lastyear": "45",
"total_export_thisyear": "45",
"total_export_rate": 0,
"high_tech_comprehensive_rate": "45",
"sichuan_province_rate": 55,
"total_importAndExport_value": 47,
"importAndExportValue": {
"valueArr": [
{
"year": "2021",
"value": "23"
},
{
"year": "2020",
"value": "29"
},
{
"year": "2030",
"value": "47"
},
{
"year": "2035",
"value": "86"
},
{
"year": "2010",
"value": "29"
},
{
"year": "2034",
"value": "44"
},
{
"year": "2060",
"value": "34"
}
]
},
"national_average": 23.5,
"total_unit_area": "3455",
"note": "12"
}
需求也很简单,把importAndExportValue中的valueArr按照year排序,然后取前五,将和设置到total_importAndExport_value中,将平均值设置到national_average中,其他的不管。
要是把对象定义出来,反序列化成对象,一顿get、set、集合循环,也很简单,但做这么一点事情,我不想定义那么多对象,直接序列化成JSONObject然后用stream操作。
代码如下:
String s = "{\"comprehensiveRank\":{\"year\":\"2021\",\"valueArr\":[{\"value\":\"12\"}]},\"all_land\":\"45\",\"land_use\":\"45\",\"land_use_rate\":100,\"total_importAndExport_lastyear\":\"45\",\"total_importAndExport_thisyear\":\"45\",\"importAndExport_rate\":0,\"total_company_value_lastyear\":\"45\",\"total_company_value_thisyear\":\"45\",\"company_value_rate\":0,\"importAndExport_unit_area_lastyear\":\"45\",\"importAndExport_unit_area_thisyear\":\"45\",\"importAndExport_unit_area_rate\":0,\"total_import_lastyear\":\"45\",\"total_import_thisyear\":\"45\",\"total_import_rate\":0,\"total_export_lastyear\":\"45\",\"total_export_thisyear\":\"45\",\"total_export_rate\":0,\"high_tech_comprehensive_rate\":\"45\",\"sichuan_province_rate\":55,\"total_importAndExport_value\":47,\"importAndExportValue\":{\"valueArr\":[{\"year\":\"2021\",\"value\":\"23\"},{\"year\":\"2020\",\"value\":\"29\"},{\"year\":\"2030\",\"value\":\"47\"},{\"year\":\"2035\",\"value\":\"86\"},{\"year\":\"2010\",\"value\":\"29\"},{\"year\":\"2034\",\"value\":\"44\"},{\"year\":\"2060\",\"value\":\"34\"}]},\"national_average\":23.5,\"total_unit_area\":\"3455\",\"note\":\"12\"}";
// 解析成对象
JSONObject jsonCockpit = JSONObject.parseObject(s);
// 拿到valueArr,这是个数组
List<JSONArray> jsonArrays = jsonCockpit.entrySet().stream().filter(x -> "importAndExportValue".equals(x.getKey()))
.map(x -> (JSONObject) x.getValue()).collect(Collectors.toList()).get(0).entrySet().stream()
.filter(x -> "valueArr".equals(x.getKey()))
.map(x -> (JSONArray) x.getValue()).collect(Collectors.toList());
// 按照year降序
List<JSONObject> sort = jsonArrays.get(0).stream().map(x -> (JSONObject) x).sorted((x, y) -> {
List<Map.Entry<String, Object>> collectX = x.entrySet().stream()
.filter(x1 -> "year".equals(x1.getKey())).collect(Collectors.toList());
List<Map.Entry<String, Object>> collectY = y.entrySet().stream()
.filter(y1 -> "year".equals(y1.getKey())).collect(Collectors.toList());
return collectY.get(0).getValue().toString().compareTo(collectX.get(0).getValue().toString());
}).collect(Collectors.toList());
// 求前五个之和
long sum = sort.stream().limit(Math.min(sort.size(), 5L))
.mapToLong(x -> Long.parseLong(x.entrySet().stream().filter(y -> "value".equals(y.getKey()))
.collect(Collectors.toList()).get(0).getValue().toString())).sum();
// 设置到原本反序列化之后的对象中
jsonCockpit.entrySet().forEach(x -> {
if ("total_importAndExport_value".equals(x.getKey())) {
x.setValue(sum);
}
if ("national_average".equals(x.getKey())) {
x.setValue((sum + 0F) / Math.min(sort.size(), 5F));
}
});
System.out.println(jsonCockpit);
看起来有点头大哈,我可是写了注释的,用到的东西也没多少。就stream里面的filter、map、sorted、limit、mapToLong