一、引言
距离上次写接口已经过去了一周,那次接口之旅让我明白打印日志的重要性,于是我自定义了一个Log注解,用于打印接受的参数和返回的参数:自定义注解+AOP,优雅的打印方法接受和返回的参数内容。
上一次接口交互的数据是xml格式的数据,由于我对于dom4j不太熟悉,所以就按部就班的使用常规的形式进行解析,效果还算如人意。但是这次接口交互的数据是JSON,我依然不熟悉,但是FastJSON、Gson、Jackson它们熟悉呀,高效的API,丝滑般的体验,能够一边摸着鱼,一边完成领导的任务,舒服。
当我看到对应的业务要求时,我有一点小郁闷,具体的返回JSON数据如下图。返回了一堆的数据,然而我只需要一个id。当然用API也行,只要一层一层的往下解析就好了,可我觉得太麻烦,要写一堆无用的代码(自己太懒了,不想写代码)。
我理想的状况就是我传入一个key,调用对应的API能够直接返回我要的value值。
以下废话,可直接看第二节
最开始我想到的是树结构,把JSON字符串修改为一棵树,然后根据层级和对应的key直接获取,但是这有几个问题:
- 我不会写树,百度出来也没看太明白(太菜了)
- 相同层级的树可能存在key值一样的情况
- 树结构下的map值和list数组交叉在一起以后,我找起来也不方便
综上所述,我放弃了。
二、代码
上面的第二点很关键,然后想到上次解析xml格式数据的经验,我需要传入指定的路径,以此来定位对应的节点值,然后一层一层的往下找。并且我可以获取单个节点值(单个value),也有可以获取一整个大的节点(直接转化为list集合或者map),说干就干,代码从哪里开始写呢?
终于在我的辛苦百度下,找到了一个差不多的功能的代码,然后修修改改(我能说是我的了吗?哈哈),成为了下面的代码。
如果想要直接运行下面的代码,你需要引入相关的依赖:fastjson(数据转换)、commons-lang3(判空)、slf4j(日志)。主要是第一个,后面两个可以把代码相关部分注释掉即可。如果想要支持多种数据源的操作,只需要写一个简单的策略模式就能实现。
现在:传入指定的JSON字符串 + 传入想要获取的目标key + 传入目标key的value类型 = 你想要的结果
public class JsonToolsFastJson {
private static String jsonStr = "{\"_id\":\"5973782bdb9a930533b05cb2\",\"isActive\":true,\"balance\":\"$1,446.35\",\"age\":32,\"eyeColor\":\"green\",\"name\":\"Logan Keller\",\"gender\":\"male\",\"company\":\"ARTIQ\",\"email\":\"logankeller@artiq.com\",\"phone\":\"+1 (952) 533-2258\",\"friends\":[{\"id\":0,\"name\":\"Colon Salazar\"},{\"id\":1,\"name\":\"French Mcneil\"},{\"id\":2,\"name\":\"Carol Martin\"}],\"mobian\":[{\"id\":0,\"name\":\"Colon Salazar\",\"arr\":[{\"id\":0,\"name\":\"Colon Salazar\"}]}],\"favoriteFruit\":\"banana\"}";
private static final Logger logger = LoggerFactory.getLogger(JsonToolsFastJson.class);
public static void main(String[] args) throws Exception {
JsonToolsFastJson jsonTools = new JsonToolsFastJson();
Object str1 = jsonTools.getObjectByJson(jsonStr, "name", TypeEnum.STRING);
logger.info("str1:" + str1);
Object str3 = jsonTools.getObjectByJson(jsonStr, "friends", TypeEnum.LIST);
logger.info("str3:" + str3);
Object str4 = jsonTools.getObjectByJson(jsonStr, "mobian.arr", TypeEnum.LIST);
logger.info("str4:" + str4);
}
private int i = 0;
public Object getObjectByJson(String jsonStr, String argsPath, TypeEnum argsType) {
if (StringUtils.isBlank(argsPath) || argsType == null) {
logger.info("必填参数argsPath或argsType不能为空");
return null;
}
Object obj = null;
try {
Map maps = JSONObject.parseObject(jsonStr);
if (argsPath.contains(".")) {
obj = getObject(maps, argsPath, argsType);
} else {
if (argsType == TypeEnum.STRING) {
obj = JSONObject.parseObject(jsonStr).get(argsPath);
} else if (argsType == TypeEnum.MAP) {
obj = (Map) JSONObject.parseObject(jsonStr).get(argsPath);
} else if (argsType == TypeEnum.LIST) {
obj = (List) JSONObject.parseObject(jsonStr).get(argsPath);
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return obj;
}
private Object getObject(Object m, String key, TypeEnum type) {
if (m == null) {
System.out.println("over...");
return null;
}
Object o = null;
Map mp = null;
List ls = null;
try {
if (m instanceof Map) {
mp = (Map) m;
for (Iterator ite = mp.entrySet().iterator(); ite.hasNext(); ) {
Map.Entry e = (Map.Entry) ite.next();
if (i < key.split("\\.").length && e.getKey().equals(key.split("\\.")[i])) {
i++;
if (e.getValue() instanceof String) {
if (i == key.split("\\.").length) {
o = e.getValue();
i = 0;
return o;
}
} else if (e.getValue() instanceof Map) {
if (i == key.split("\\.").length) {
if (type == TypeEnum.MAP) {
o = (Map) e.getValue();
i = 0;
return o;
}
} else {
o = getObject((Map) e.getValue(), key, type);
}
return o;
} else if (e.getValue() instanceof List) {
if (i == key.split("\\.").length) {
if (type == TypeEnum.LIST) {
o = (List) e.getValue();
i = 0;
return o;
}
} else {
o = getObject((List) e.getValue(), key, type);
}
return o;
}
}
}
}
if (m instanceof List) {
ls = (List) m;
for (int i = 0; i < ls.size(); i++) {
if (ls.get(i) instanceof Map) {
if (i == key.split("\\.").length) {
if (type == TypeEnum.MAP) {
o = (Map) ls.get(i);
return o;
}
} else {
o = getObject((Map) ls.get(i), key, type);
}
return o;
} else if (ls.get(i) instanceof List) {
if (i == key.split("\\.").length) {
if (type == TypeEnum.LIST) {
o = (List) ls.get(i);
return o;
}
} else {
o = getObject((List) ls.get(i), key, type);
}
return o;
}
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return o;
}
public enum TypeEnum {
STRING,
MAP,
LIST;
}
}
补充:对于数组类型的数据,我认为在实际工作中很少会出现我只获取第一个或者某一个数组种数据的情况,所以该测试代码中如果想要获取的目标数据是一个数组,那么得到的结果就是一整个数组数据。
三、测试
测试效果图:
name对应的直接是一个string类型的value
friends对应的是list里面嵌套的map
mobian对应的是一个list,就list中的单个元素而言,它又是一个map,map里面的arr参数对应的又是一个list
JSON代码如下:
{
"_id": "5973782bdb9a930533b05cb2",
"isActive": true,
"balance": "$1,446.35",
"age": 32,
"eyeColor": "green",
"name": "Logan Keller",
"gender": "male",
"company": "ARTIQ",
"email": "logankeller@artiq.com",
"phone": "+1 (952) 533-2258",
"friends": [{
"id": 0,
"name": "Colon Salazar"
}, {
"id": 1,
"name": "French Mcneil"
}, {
"id": 2,
"name": "Carol Martin"
}],
"mobian": [{
"id": 0,
"name": "Colon Salazar",
"arr": [{
"id": 0,
"name": "Colon Salazar"
}]
}],
"favoriteFruit": "banana"
}
我认为该方法可以当作一个工具方法来使用,现在对于层级很深的数据,我也只需要一行就能解决数据的获取问题,而不再需要一层一层的往下嵌套处理。
若干年后,你离职了,上面的方法有bug,你成功的给同事埋了一个雷,同事打开版本控制工具,看到提交人,大骂一声,xxx垃圾玩意儿。
|