博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JsonPath:从多层嵌套Json中解析所需要的值
阅读量:5265 次
发布时间:2019-06-14

本文共 4950 字,大约阅读时间需要 16 分钟。

问题

应用中,常常要从嵌套的JSON串中解析出所需要的数据。通常的做法是,先将JSON转换成Map, 然后一层层地判空和解析。可使用 JsonPath 来解决这个问题。

给定一个 JSON 串如下所示

{"code":200,"msg":"ok","list":[{"id":20,"no":"1000020","items":[{"name":"n1","price":21,"infos":{"feature":""}}]}],"metainfo":{"total":20,"info":{"owner":"qinshu","parts":[{"count":13,"time":{"start":1230002456,"end":234001234}}]}}}

从中解析出 code, total, count 的值。

基本方案

基本方案就是自己手动将JSON转为Map,然后一层层判空和解析,如下代码所示:

public class JsonUtil {  private static final ObjectMapper MAPPER = new ObjectMapper();  static {    // 为保持对象版本兼容性,忽略未知的属性    MAPPER.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);    // 序列化的时候,跳过null值    MAPPER.getSerializationConfig().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);    // date类型转化    SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    MAPPER.setDateFormat(fmt);  }  /**   * 将一个json字符串解码为java对象   *   * 注意:如果传入的字符串为null,那么返回的对象也为null   *   * @param json json字符串   * @param cls  对象类型   * @return 解析后的java对象   * @throws RuntimeException 若解析json过程中发生了异常   */  public static 
T toObject(String json, Class
cls) { if (json == null) { return null; } try { return MAPPER.readValue(json, cls); } catch (Exception e) { return null; } } /** * 读取JSON字符串为MAP */ @SuppressWarnings("unchecked") public static Map
readMap(String json) { return toObject(json, HashMap.class); } /** * 对于正确JSON及存在的Path下获取到最终指定值并转成字符串,其他情况一律返回 null * @param json JSON串 * @param path 点分隔的字段路径 * @return 相应字段的字符串值 */ public static String readVal(String json, String path) { if (json == null || path == null) { return null; } Map
map = readMap(json); if (map == null) { // log.warn("parse json failed: " + json); return null; } String[] subpaths = path.split("\\."); return readVal(map, subpaths); } private static String readVal(Map
map, String path) { return readVal(map, path.split("\\.")); } private static String readVal(Map
map, String[] subpaths) { Object val = map; try { for (String subpath: subpaths) { if (val != null && val instanceof Map) { val = ((Map)val).get(subpath); } else { // log.warn("subpath may not exists in " + map); return null; } } return val == null ? null: val.toString(); } catch (Exception ex) { return null; } }

realVal 的目标就是:对于正常情况下获取到最终指定值并转成字符串,其他情况一律返回 null. readVal 上层函数接受一个JSON串和一个点分割的Path,进行参数校验后交给下层 readVal 函数;下层 readVal 函数对每次取出的值进行判空和取值,如果OK就一直进行到取出最终值;否则要么抛出异常,要么直接返回 null.

对于只需要从嵌套Map中取值的需求,基本是满足了,不过若要从List,Map混合的JSON串中取值,就不够用了。此时,可采用成熟库 JsonPath 来完成这件事。

JsonPath

在网上搜索 jsonpath maven 即可在 找到 jsonpath 的最新版本。工程中引入

com.jayway.jsonpath
json-path
2.4.0

现在就可以使用JsonPath 了。 基本语法可参考这篇文章:

可以为 JsonPath 包装一层函数, 与现有客户端使用相同。

public static String readValUsingJsonPath(String json, String path) {    if (json == null || path == null) {      return null;    }    try {      Object val = JsonPath.read(json, "$." + path);      return val == null ? null : val.toString();    } catch (Exception ex) {      return null;    }  }

编写单测如下:

public class JsonUtilTest extends CommonForTest {  String json = "{\"code\":200,\"msg\":\"ok\",\"list\":[{\"id\":20,\"no\":\"1000020\",\"items\":[{\"name\":\"n1\",\"price\":21,\"infos\":{\"feature\":\"\"}}]}],\"metainfo\":{\"total\":20,\"info\":{\"owner\":\"qinshu\",\"parts\":[{\"count\":13,\"time\":{\"start\":1230002456,\"end\":234001234}}]}}}";  // 常用的 Json Path 可以缓存起来重用,类似正则里的 Pattern p = Pattern.compile('regexString')  JsonPath codePath = JsonPath.compile("$.code");  JsonPath totalPath = JsonPath.compile("$.metainfo.total");  @Test  public void testReadVal() {    eq(null, JsonUtil.readVal(null, "code"));    eq(null, JsonUtil.readVal(json, null));    eq("200", JsonUtil.readVal(json, "code"));    eq("20", JsonUtil.readVal(json, "metainfo.total"));    eq("qinshu", JsonUtil.readVal(json, "metainfo.info.owner"));    eq(null, JsonUtil.readVal("invalid json", "code"));    eq(null,JsonUtil.readVal(json, "metainfo.extra.feature"));    eq(null, JsonUtil.readValUsingJsonPath(null, "code"));    eq(null, JsonUtil.readValUsingJsonPath(json, null));    eq("200", JsonUtil.readValUsingJsonPath(json, "code"));    eq("20", JsonUtil.readValUsingJsonPath(json, "metainfo.total"));    eq("qinshu", JsonUtil.readValUsingJsonPath(json, "metainfo.info.owner"));    eq(null, JsonUtil.readValUsingJsonPath("invalid json", "code"));    eq(null,JsonUtil.readValUsingJsonPath(json, "metainfo.extra.feature"));    eq(200, codePath.read(json));    eq(20, totalPath.read(json));    eq("qinshu", JsonPath.read(json, "$.metainfo.info.owner"));    eq("n1", JsonPath.read(json, "$.list[0].items[0].name"));    eq(13, JsonPath.read(json, "$.metainfo.info.parts[0].count"));  }}

可见 jsonPath 的功能更加强大,也更加健壮。

小结

多熟悉现有库,不轻易造重复轮子。

转载于:https://www.cnblogs.com/lovesqcc/p/7821641.html

你可能感兴趣的文章
2014年辛星完全解读Javascript第一节
查看>>
装配SpringBean(一)--依赖注入
查看>>
daydayup2 codeforces143C
查看>>
ANT打包J2EE项目war包
查看>>
UESTC-我要长高 DP优化
查看>>
java选择文件时提供图像缩略图[转]
查看>>
当DIV内出现滚动条,fixed实效怎么办?
查看>>
方维分享系统二次开发, 给评论、主题、回复、活动 加审核的功能
查看>>
Matlab parfor-loop并行运算
查看>>
Oracle HRMS API's
查看>>
mysql_real_escape_string() vs addslashes() vs addcslashes()
查看>>
string与stringbuilder的区别
查看>>
2012-01-12 16:01 hibernate注解以及简单实例
查看>>
iOS8统一的系统提示控件——UIAlertController
查看>>
PAT甲级——1101 Quick Sort (快速排序)
查看>>
python创建进程的两种方式
查看>>
1.2 基础知识——关于猪皮(GP,Generic Practice)
查看>>
迭代器Iterator
查看>>
java易错题----静态方法的调用
查看>>
php建立MySQL数据表
查看>>