2021SC@SDUSC


目录

  • JSONPath
  • JSONPath的定义
  • JSONPath的语法
  • JSONPath在Fastjson中的使用
  • JSONPath代码解析
  • 总结


JSONPath

JSONPath的定义

正如XPath对XML的解析一样,JSONPath的定义,简单说来,就是对JSON文档的一种解析。通过JSONPath可以轻松的对JSON文档获取指定“路径”的数据,在繁杂的、肉眼难以阅读的json字符串中精准找到需要的部分。

JSONPath的语法

  1. $表示文档的根元素
  2. @表示文档的当前元素
  3. .node_name表示匹配下级节点
  4. index减速数组中的元素
  5. start:end :step支持数组切片语法
  6. *作为通配符,匹配成员的所有子元素
  7. ()使用表达式

有两个需要注意的点:

JSONPath的索引从0开始计数
JSONPath中字符串使用单引号表示

JSONPath在Fastjson中的使用

使用Fastjson中的JSONPath解析器,首先需要将json字符串转换成JSONObject类型的对象。然后调用JSONPath.eval()方法,获取指定的字符串。

package com.xiaobu.note.json.fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;

public class FastJsonDemo1 {

    public static void main(String[] args) {
        String jsonStr = "{ \"store\": {\"book\": [{ \"category\": \"reference\"," +
                "\"author\": \"Nigel Rees\",\"title\": \"Sayings of the Century\"," +
                "\"price\": 8.95},{ \"category\": \"fiction\",\"author\": \"Evelyn Waugh\"," +
                "\"title\": \"Sword of Honour\",\"price\": 12.99,\"isbn\": \"0-553-21311-3\"" +
                "}],\"bicycle\": {\"color\": \"red\",\"price\": 19.95}}}";
        // 先解析JSON数据为JSONObject,然后就能直接使用JSONPath了。
        JSONObject jsonObject = JSON.parseObject(jsonStr);
        System.out.println("book数目:"+ JSONPath.eval(jsonObject, "$.store.book.size()") );
        System.out.println("第一本书的title:"+JSONPath.eval(jsonObject,"$.store.book[0].title"));
        System.out.println("第一本书的category和author:"+JSONPath.eval(jsonObject,"$.store.book[0]['category','author']"));
        System.out.println("price>10的书:"+JSONPath.eval(jsonObject,"$.store.book[price>10]"));
        System.out.println("price>8的书的标题:"+JSONPath.eval(jsonObject,"$.store.book[price>8]"));
        System.out.println("price>7的书:"+JSONPath.eval(jsonObject,"$.store.book[price>7]"));
        System.out.println("price>7的书的标题:"+JSONPath.eval(jsonObject,"$.store.book[price>7].title"));
       //不带单引号会出现Exception in thread "main" java.lang.UnsupportedOperationException 异常
        System.out.println("书的标题为Sayings of the Century:"+JSONPath.eval(jsonObject,"$.store.book[title='Sayings of the Century']"));
        System.out.println("bicycle的所有属性:"+JSONPath.eval(jsonObject,"$.store.bicycle.*"));
        System.out.println("bicycle:"+JSONPath.eval(jsonObject,"$.store.bicycle"));

    }
}

结果:

book数目:2
第一本书的title:Sayings of the Century
第一本书的category和author:[reference, Nigel Rees]
price>10的书:[{"author":"Evelyn Waugh","price":12.99,"isbn":"0-553-21311-3","category":"fiction","title":"Sword of Honour"}]
price>8的书的标题:[{"author":"Evelyn Waugh","price":12.99,"isbn":"0-553-21311-3","category":"fiction","title":"Sword of Honour"}]
price>7的书:[{"author":"Nigel Rees","price":8.95,"category":"reference","title":"Sayings of the Century"}, {"author":"Evelyn Waugh","price":12.99,"isbn":"0-553-21311-3","category":"fiction","title":"Sword of Honour"}]
price>7的书的标题:[Sayings of the Century, Sword of Honour]
书的标题为Sayings of the Century:[{"author":"Nigel Rees","price":8.95,"category":"reference","title":"Sayings of the Century"}]
bicycle的所有属性:[red, 19.95]
bicycle:{"color":"red","price":19.95}

可以看到,在这段难以阅读的json字符串中,我们轻松取到了书籍数目、第一本书的书名等关键信息。
这只是eval方法的用法,下面介绍一下这个类其他的几个关键方法的作用:

求值,静态方法
public static Object eval(Object rootObject, String path);

计算Size,Map非空元素个数,对象非空元素个数,Collection的Size,数组的长度。其他无法求值返回-1
public static int size(Object rootObject, String path);

是否包含,path中是否存在对象
public static boolean contains(Object rootObject, String path) { }

是否包含,path中是否存在指定值,如果是集合或者数组,在集合中查找value是否存在
public static boolean containsValue(Object rootObject, String path, Object value) { }

修改制定路径的值,如果修改成功,返回true,否则返回false
public static boolean set(Object rootObject, String path, Object value) {}

在数组或者集合中添加元素
public static boolean array_add(Object rootObject, String path, Object… values);

JSONPath代码解析

分析一下JSONPath中eval方法的作用机制。

public static Object eval(Object rootObject, String path) {
        JSONPath jsonpath = compile(path);
        return jsonpath.eval(rootObject);
    }

这是我们调用的eval方法,可以看到它又调用了同名方法eval:

public Object eval(Object rootObject) {
        if (rootObject == null) {
            return null;
        } else {
            this.init();
            Object currentObject = rootObject;

            for(int i = 0; i < this.segments.length; ++i) {
                currentObject = this.segments[i].eval(this, rootObject, currentObject);
            }

            return currentObject;
        }
    }

此时,可以看到再次调用了eval方法,我们再次跟进:

public Object eval(JSONPath path, Object rootObject, Object currentObject) {
            int size = JSONPath.SizeSegement.instance.eval(path, rootObject, currentObject);//获取rootObject的尺寸,即JSONObject对象数量
            int start = this.start >= 0 ? this.start : this.start + size;
            int end = this.end >= 0 ? this.end : this.end + size;//获取起始位置和终止位置
            int array_size = (end - start) / this.step + 1;//获取数组大小
            if (array_size == -1) {
                return null;
            } else {
                List<Object> items = new ArrayList(array_size);

                for(int i = start; i <= end && i < size; i += this.step) {
                    Object item = path.getArrayItem(currentObject, i);
                    items.add(item);//遍历数组,将符合条件的Object对象添加到item中,作为结果返回
                }

                return items;
            }
        }

显然,Fastjson做的工作就是将JSONObject对象层层遍历,通过我们给定的JSONPath路径,按照我们的解析规则将遍历后的结果放进一个List对象中并返回给调用者。由于事先确定了起始位置和终止位置,遍历过程中会减少很多次对不会出现在结果集中的数据的访问,大大加快了解析速度。这也是Fastjson速度快的原因——通过增加条件来减少对数据的访问。

总结

使用JSONPath类提供的静态方法能够非常快速地对json字符串进行路径解析,将指定位置的json字符串以对象的形式返回,方便了系统的调用者。JSONPath与XPath的路径解析规则差不多,熟练使用这种解析方式,再用上Fastjson提供的静态方法,可以将在需要使用JSONPath的项目中将解析速度大大提升。