JsonPath学习笔记

概述

介绍

JsonPath是用来提取json文档中内容的,跟xpath用来提取html或xml中内容一样,是一种路径表达式。

而这里说的是Java语言中的json-path包,其他语言如JavaScript也是也可以使用JsonPath的,但是相关解析的第三包不一样,下面所用的包是Java语言支持的,但json路径表达式的语法是通用的。

如果使用Gson等进行解析json,提取里面的个别值,代码会很多比较麻烦,而JsonPath只需要一个简单的路径表达式就可以提取到值,代码简洁。

一般情况下,做爬虫的对提取json中内容的需求比较大。

它的官网地址:JsonPath

安装

它的maven坐标如下:


<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.6.0</version>
</dependency>

如果报错Caused by: java.lang.NoClassDefFoundError: net/minidev/json/writer/JsonReaderI则需要加上如下依赖:


<dependency>
    <groupId>net.minidev</groupId>
    <artifactId>asm</artifactId>
    <version>1.0.2</version>
    <scope>test</scope>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.2.1</version>
<scope>test</scope>
</dependency>

入门

JsonPath中的根成员对象是$。JsonPath获取数据有两种表示方法:

  • 点表示法:$.store.book[0].title
  • 括号表示法:$['store']['book'][0]['title']

入门示例如下:

public class Test {
    @Test
    public void hello() {
        String json = "{\"msg\":\"hello world\"}";
        String msg = JsonPath.read(json, "$.msg");
        System.out.println(msg);
    }
}    

语法

用于测试的json字符串如下:

{
    "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
            },
            {
                "category": "fiction",
                "author": "Herman Melville",
                "title": "Moby Dick",
                "isbn": "0-553-21311-3",
                "price": 8.99
            },
            {
                "category": "fiction",
                "author": "J. R. R. Tolkien",
                "title": "The Lord of the Rings",
                "isbn": "0-395-19395-8",
                "price": 22.99
            }
        ],
        "bicycle": {
            "color": "red",
            "price": 19.95
        }
    },
    "expensive": 10
}

操作符

JsonPath支持的操作符如下表:

操作说明
$查询根元素。这将启动所有路径表达式。
@当前节点由过滤谓词处理。
*通配符,必要时可用任何地方的名称或数字。
..深层扫描。必要时在任何地方可以使用名称。
.<name>点,表示子节点
['<name>' (, '<name>')]括号表示子项
[<number> (, <number>)]数组索引或索引,索引从0开始
[start:end]数组切片操作
[?(<expression>)]过滤表达式。 表达式必须求值为一个布尔值。

示例如下:

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 获取json中store下book下的所有author值
        List<String> authors = JsonPath.read(json, "$.store.book[*].author");
        System.out.println(authors);// ["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
    }

    @Test
    public void test02() {
        // 获取所有json中所有author的值
        List<String> authors = JsonPath.read(json, "$..author");
        System.out.println(authors);// ["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
    }

    @Test
    public void test03() {
        // 获取所有的书籍和自行车
        Object obj = JsonPath.read(json, "$.store.*");
        System.out.println(obj);// [[{"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},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],{"color":"red","price":19.95}]
    }

    @Test
    public void test04() {
        // 获取json中store下所有price的值
        List<Float> prices = JsonPath.read(json, "$.store..price");
        System.out.println(prices);// [8.95,12.99,8.99,22.99,19.95]
    }

    @Test
    public void test05() {
        // 获取json中book数组的第3本书,注意索引是从0开始的,但也需要注意返回的是一个数组而非一个Map集合
        List<Map<String, Object>> book = JsonPath.read(json, "$..book[2]");
        System.out.println(book);// [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
    }

    @Test
    public void test06() {
        // 获取倒数第二本书,即支持倒序索引,从-1开始
        List<Map<String, Object>> book = JsonPath.read(json, "$..book[-2]");
        System.out.println(book);// [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
    }

    @Test
    public void test07() {
        // 获取前两本书,即在数组中索引为0和1的两本书
        List<Map<String, Object>> books = JsonPath.read(json, "$..book[0,1]");
        System.out.println(books);// [{"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}]
    }

    @Test
    public void test08() {
        // 获取从索引0(包括)到索引2(排除)的所有图书,即[0,2)这样的关系
        List<Map<String, Object>> books = JsonPath.read(json, "$..book[:2]");
        System.out.println(books);// [{"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}]
    }

    @Test
    public void test09() {
        // 获取从索引1(包括)到索引2(排除)的所有图书,即[1,2)这样的关系
        List<Map<String, Object>> books = JsonPath.read(json, "$..book[1:2]");
        System.out.println(books);// [{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}]
    }

    @Test
    public void test10() {
        // 获取json中book数组的最后两个值
        List<Map<String, Object>> books = JsonPath.read(json, "$..book[-2:]");
        System.out.println(books);// [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
    }

    @Test
    public void test11() {
        // 获取json中book数组的第3个到最后一个的区间值,包括最后一个
        List<Map<String, Object>> books = JsonPath.read(json, "$..book[2:]");
        System.out.println(books);// [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
    }

    @Test
    public void test12() {
        // 获取json中book数组中包含isbn的所有值
        List<Map<String, Object>> books = JsonPath.read(json, "$..book[?(@.isbn)]");
        System.out.println(books);// [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
    }

    @Test
    public void test13() {
        // 获取json中book数组中price<10的所有值
        List<Map<String, Object>> books = JsonPath.read(json, "$..book[?(@.price < 10)]");
        System.out.println(books);// [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
    }
}

函数

JsonPath还支持一些函数,函数可以在路径的尾部调用,函数的输出是路径表达式的输出,该函数的输出是由函数本身所决定的。如下表:

函数描述输出
min()提供数字数组的最小值Double
max()提供数字数组的最大值Double
avg()提供数字数组的平均值Double
stddev()提供数字数组的标准偏差值Double
length()提供数组的长度Integer
sum()提供数组中所有原生的总和Double
keys()提供所有的键名Set
concat(X)Provides a concatinated version of the path output with a new itemlike input
append(X)向路径表达式的输出数组新增一项like input

示例如下:

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 获取有几本书
        int length = JsonPath.read(json, "$..book.length()");
        System.out.println(length);
    }

    @Test
    public void test02() {
        // 获取最小书价
        double min = JsonPath.read(json, "$..book..price.min()");
        System.out.println(min);// 8.95
    }

    @Test
    public void test03() {
        // 获取最大书价
        double max = JsonPath.read(json, "$..book..price.max()");
        System.out.println(max);// 22.99
    }

    @Test
    public void test04() {
        // 获取平均书价
        double avg = JsonPath.read(json, "$..book..price.avg()");
        System.out.println(avg);// 13.48
    }

    @Test
    public void test05() {
        // 获取书价的标准偏差值,这是统计学上的概念,几乎用不到
        Object obj = JsonPath.read(json, "$..book..price.stddev()");
        System.out.println(obj);
    }

    @Test
    public void test06() {
        // 获取所有书的总价
        double sum = JsonPath.read(json, "$..book..price.sum()");
        System.out.println(sum);// 53.92
    }

    @Test
    public void test07() {
        // 获取第二本书所有的属性键名
        Set<String> set = JsonPath.read(json, "$.store.book[2].keys()");
        System.out.println(set);// [category, author, title, isbn, price]
    }
}

过滤器运算符

过滤器是用于筛选数组的逻辑表达式。一个典型的过滤器将是[?(@.age > 18)],其中@表示正在处理的当前项。 可以使用逻辑运算符&&||(分别表示)创建更复杂的过滤器。 字符串文字必须用单引号或双引号括起来([?(@.color == 'blue')] 或者 [?(@.color == "blue")]).

操作符描述
==left等于right(注意1不等于’1’)
!=不等于
<小于
<=小于等于
>大于
>=大于等于
=~匹配正则表达式,如[?(@.name =~ /foo.*?/i)]
in左边存在于右边,如[?(@.size in ['S', 'M'])]
nin左边不存在于右边
subsetof左边是一个右边的子集合,如[?(@.sizes subsetof ['S','M','L'])]
anyof左边的集合与右边的集合相交,如[?(@.sizes anyof ['M','L'])]
noneof左右的集合与右边的集合不相交,如[?(@.sizes noneof ['M','L'])]
size左边(数组或字符串)长度应该匹配右边的数值
empty左边(数组或字符串)为空

示例如下:

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 查找category等于"fiction"的书籍
        List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.category=='fiction')]");
        System.out.println(books);// [{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
    }

    @Test
    public void test02() {
        // 查找category不等于"fiction"的书籍
        List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.category!='fiction')]");
        System.out.println(books);// [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}]
    }

    @Test
    public void test03() {
        // 查找price小于10的书籍
        List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.price<10)]");
        System.out.println(books);// [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
    }

    @Test
    public void test04() {
        // 查找price大于10的书籍
        List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.price>10)]");
        System.out.println(books);// [{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
    }

    @Test
    public void test05() {
        // 利用正则表达式匹配title中包含"of the"的书籍
        List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.title =~ /.*of the.*/i)]");
        System.out.println(books);// [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
    }

    @Test
    public void test06() {
        // 查询category存在于["reference","abc"]的书籍
        List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.category in ['reference','abc'])]");
        System.out.println(books);// [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}]
    }

    @Test
    public void test07() {
        // 查询category不存在于["reference","abc"]的书籍
        List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.category nin ['reference','abc'])]");
        System.out.println(books);// [{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
    }

    @Test
    public void test08() {
        // ['S','M']是['S','M','L']的子集,所以被匹配了
        String json = "{\"sizes\":[\"S\",\"M\"]}";
        Object result = JsonPath.read(json, "$[?(@.sizes subsetof ['S','M','L'])]");
        System.out.println(result);// [{"sizes":["S","M"]}]
    }

    @Test
    public void test09() {
        // ['S','M']与['M','L']发生相交,其中'M'是相交的值
        String json = "{\"sizes\":[\"S\",\"M\"]}";
        Object result = JsonPath.read(json, "$[?(@.sizes anyof ['M','L'])]");
        System.out.println(result);// [{"sizes":["S","M"]}]
    }

    @Test
    public void test10() {
        // ['S','X']与['M','L']不发生相交,所以被匹配了
        String json = "{\"sizes\":[\"S\",\"X\"]}";
        Object result = JsonPath.read(json, "$[?(@.sizes noneof ['M','L'])]");
        System.out.println(result);
    }

    @Test
    public void test11() {
        // 匹配title只有9个字符的书籍
        List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.title size 9)]");
        System.out.println(books);// [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
    }
}

扩展

上面的语法已经够我们日常使用了,下面是JsonPath的一些扩展使用。

阅读文档

我们上面使用的都是JsonPath.read()方法读取内容,如果只读取一次那么是可行的,如果想要读取多次,那么这不是一个好的方法,因为该方法每次读取都需要解析整个json文档,耗费性能。

所以如果我们需要读取多个路径,那么我们可以选择先解析整个文档,再解析路径读取内容。

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 通过解析多次文档读取多个路径
        long start1 = System.currentTimeMillis();// 可以查看它们花费的时间
        String author0 = JsonPath.read(json, "$.store.book[0].author");
        String author1 = JsonPath.read(json, "$.store.book[1].author");
        long end1 = System.currentTimeMillis();
        System.out.println(author0 + ", " + author1 + ", " + (end1 - start1));// Nigel Rees, Evelyn Waugh, 1218

        // 通过解析一次文档读取多个路径【推荐】
        long start2 = System.currentTimeMillis();
        Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);
        String author2 = JsonPath.read(document, "$.store.book[0].author");
        String author3 = JsonPath.read(document, "$.store.book[1].author");
        long end2 = System.currentTimeMillis();
        System.out.println(author2 + ", " + author3 + ", " + (end2 - start2));// Nigel Rees, Evelyn Waugh, 1
    }
}

核心代码就是:

String json="...";
Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);
String author2 = JsonPath.read(document, "$.store.book[0].author");

还可以使用下面这种API,链式配置很灵活:

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 通过阅读上下文来解析路径
        ReadContext context = JsonPath.parse(json);
        List<String> authors = context.read("$.store.book..author");
        System.out.println(authors);// ["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]

        // 或者链式调用
        List list = JsonPath
                .using(Configuration.defaultConfiguration())
                .parse(json)
                .read("$.store.book..author", List.class);
        System.out.println(list);// ["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
    }
}

何时返回

返回结果类型

在使用JsonPath时,要明确知道路径的结果是什么类型,JsonPath会尝试转换为所期待的类型,如:

// 抛出 java.lang.ClassCastException 异常
List<String> list = JsonPath.parse(json).read("$.store.book[0].author")// 实际是返回的是一个字符串,所以用List来接收不合适,无法强制类型转换

// 正常
String author = JsonPath.parse(json).read("$.store.book[0].author")

在评估路径时,您需要了解路径何时确定的概念。如果路径包含以下内容,则该路径是不确定的:

  • ..:深层扫描操作
  • ?(<expression>):表达式
  • [<number>, <number> (, <number>)]:多个数组索引

不确定的路径总数返回一个列表,可由List接收。

返回结果映射
  • 简单对象映射
    默认情况下,MappingProvider SPI提供了一个简单的对象映射器。 这允许您指定所需的返回类型,MappingProvider将尝试执行映射。如将Long类型的时间戳转换成Date类型的日期:
public class JsonPathTest {

    @Test
    public void test01() {
        String json = "{\"timestamp\":1641519588137}";
        Date date = JsonPath.parse(json).read("$.timestamp", Date.class);
        System.out.println(date);// Fri Jan 07 09:39:48 CST 2022
    }
}
  • POJO对象映射
    如果是简单的实体类映射,如Book,如下即可:
public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class);
        System.out.println(book);// Book{category='reference', author='Nigel Rees', title='Sayings of the Century', isbin='null', price=8.95}
    }
}

class Book {
    private String category;
    private String author;
    private String title;
    private String isbn;
    private float price;

    public Book() {
    }

    public Book(String category, String author, String title, String isbn, float price) {
        this.category = category;
        this.author = author;
        this.title = title;
        this.isbn = isbn;
        this.price = price;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "category='" + category + '\'' +
                ", author='" + author + '\'' +
                ", title='" + title + '\'' +
                ", isbin='" + isbn + '\'' +
                ", price=" + price +
                '}';
    }
}

但如果是更复杂的情况,如List<Book>,则可以考虑将JsonPath配置为使用JacksonMappingProvider或GsonMappingProvider,这样就能让JsonPath输出直接映射到POJO中。

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 1.进行默认配置
        Configuration.setDefaults(new Configuration.Defaults() {
            // 注意,这里创建的是GsonJsonProvider和GsonMappingProvider,但需要导入Gson包
            // 还可以使用JacksonJsonProvider和JacksonMappingProvider,也需要导入jackson包
            private final JsonProvider jsonProvider = new GsonJsonProvider();
            private final MappingProvider mappingProvider = new GsonMappingProvider();

            @Override
            public JsonProvider jsonProvider() {
                return jsonProvider;
            }

            @Override
            public Set<Option> options() {
                return EnumSet.noneOf(Option.class);
            }

            @Override
            public MappingProvider mappingProvider() {
                return mappingProvider;
            }
        });
        // 2.设置泛型,List<Book>泛型是无法直接在read()方法中传入的,所以需要在TypeRef<>中设置泛型
        TypeRef<List<Book>> typeRef = new TypeRef<List<Book>>() {};
        // 3.调用read()方法传入路径表达式和泛型并读取数据
        List<Book> books = JsonPath.parse(json).read("$.store.book.*", typeRef);
        // 4.打印结果
        for (Book book : books) {
            System.out.println(book);
        }
        // Book{category='reference', author='Nigel Rees', title='Sayings of the Century', isbin='null', price=8.95}
        // Book{category='fiction', author='Evelyn Waugh', title='Sword of Honour', isbin='null', price=12.99}
        // Book{category='fiction', author='Herman Melville', title='Moby Dick', isbin='0-553-21311-3', price=8.99}
        // Book{category='fiction', author='J. R. R. Tolkien', title='The Lord of the Rings', isbin='0-395-19395-8', price=22.99}
    }

}

class Book {
    private String category;
    private String author;
    private String title;
    private String isbn;
    private float price;

    public Book() {
    }

    public Book(String category, String author, String title, String isbn, float price) {
        this.category = category;
        this.author = author;
        this.title = title;
        this.isbn = isbn;
        this.price = price;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "category='" + category + '\'' +
                ", author='" + author + '\'' +
                ", title='" + title + '\'' +
                ", isbin='" + isbn + '\'' +
                ", price=" + price +
                '}';
    }
}

谓词

在JsonPath中创建过滤器谓词有三种不同的方法。

内联谓词

内联谓词就是在路径中直接定义的谓词。如:

List<Map<String, Object>> books =  JsonPath.parse(json).read("$.store.book[?(@.price < 10)]");

其中[?(@.price < 10)]就是内联谓词,要求过滤书籍价格小于10的书。

还可以使用$$||结合使用多个谓词,即多个过滤条件呈现“与”和“或”的关系,如[?(@.price < 10 && @.category == 'fiction')]表示过滤价格小于10并且分类为fiction的书籍(多个条件同时满足)、[?(@.category == 'reference' || @.price > 10)]表示过滤分类为reference或者价格大于10的书籍(只要满足任一条件即可)。

还有一个!表示“非”,如[?(!(@.price < 10 && @.category == 'fiction'))]表示价格既不小于10分类也不是fiction的书籍。

过滤谓词

谓词可以使用Filter API构建,如下所示:

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 声明谓词过滤器
        Filter bookFilter = Filter.filter(
                // 类似于SQL语句中的条件,即category=='fiction' && price<=10
                Criteria.where("category").is("fiction").and("price").lte(10)
        );
        // 调用read方法传入路径表达式和谓词过滤器
        List<Map<String, Object>> books = JsonPath.parse(json).read("$.store.book[?]", bookFilter);
        System.out.println(books);// [{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
    }

}

注意:

  • 路径表达式$.store.book[?]中使用了占位符?来表示路径中的过滤器。
  • 当提供多个过滤器时,它们按照占位符数量与提供的过滤器数量相匹配的顺序应用。
  • 可以在一个过滤器操作[?,?]中指定多个谓词占位符,这两个谓词都必须匹配。
  • 过滤器可以使用.or().and().exists()来表示“或”、“与”、“非”。
自定义谓词

还可以实现自己的谓词,进行更复杂的过滤。

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 自定义谓词
        Predicate booksPredicate = new Predicate() {
            @Override
            public boolean apply(PredicateContext context) {
                // 要求过滤书籍作者的名字刚好是10个字符
                return context.item(Map.class).get("author").toString().length() == 10;
            }
        };

        // 调用read方法传入路径表达式和自定义谓词
        List<Map<String, Object>> books = JsonPath.parse(json).read("$.store.book[?]", List.class, booksPredicate);
        System.out.println(books);// [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}]
    }
}
路径 vs 值

默认情况下,JsonPath是返回值的,但也可以要求它返回匹配元素的完整路径。如下:

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 进行配置,要求返回路径
        Configuration conf = Configuration.builder().options(Option.AS_PATH_LIST).build();

        // 传入配置读取路径
        List<String> pathList = JsonPath.using(conf).parse(json).read("$..author");
        System.out.println(pathList);// ["$['store']['book'][0]['author']","$['store']['book'][1]['author']","$['store']['book'][2]['author']","$['store']['book'][3]['author']"]
    }
}

调整配置

选项创建配置时,有几个可以改变默认行为的选项标志。

DEFAULT_PATH_LEAF_TO_NULL

此选项使JsonPath对于缺少的叶子返回null,如下的json:

[
   {
      "name" : "john",
      "gender" : "male"
   },
   {
      "name" : "ben"
   }
]

获取第二个对象中的gender,如果按照默认配置项则会抛出异常,我们希望它不存在时能返回null,那么可以设置DEFAULT_PATH_LEAF_TO_NULL

public class JsonPathTest {
    @Test
    public void test01() {
        String json = "[{\"name\":\"john\",\"gender\":\"male\"},{\"name\":\"ben\"}]";

        Configuration conf = Configuration.defaultConfiguration();

        // 正常
        String gender0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
        // 异常 PathNotFoundException thrown
        String gender1 = JsonPath.using(conf).parse(json).read("$[1]['gender']");// com.jayway.jsonpath.PathNotFoundException: No results for path: $[1]['gender']
        System.out.println(gender0 + ", " + gender1);
    }

    @Test
    public void test02() {
        String json = "[{\"name\":\"john\",\"gender\":\"male\"},{\"name\":\"ben\"}]";

        Configuration conf2 = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);

        // 正常
        String gender0 = JsonPath.using(conf2).parse(json).read("$[0]['gender']");
        // 正常 (返回 null)
        String gender1 = JsonPath.using(conf2).parse(json).read("$[1]['gender']");
        System.out.println(gender0 + ", " + gender1);// male, null
    }
}

ALWAYS_RETURN_LIST

此选项将JsonPath配置为返回列表,即使路径是确定的。

$[0]['gender']的结果是一个字符串,我们可以使用String来接收它,但如果我们想要使用List<String>来接收这单个字符串,那么就需要配置ALWAYS_RETURN_LIST选项。

public class JsonPathTest {

    @Test
    public void test01() {
        String json = "[{\"name\":\"john\",\"gender\":\"male\"},{\"name\":\"ben\"}]";

        Configuration conf = Configuration.defaultConfiguration();

        List<String> genders0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");// java.lang.ClassCastException: java.lang.String cannot be cast to java.util.List
        System.out.println(genders0);
    }

    @Test
    public void test02() {
        String json = "[{\"name\":\"john\",\"gender\":\"male\"},{\"name\":\"ben\"}]";

        Configuration conf = Configuration.defaultConfiguration().addOptions(Option.ALWAYS_RETURN_LIST);// 设置总是返回列表

        List<String> genders0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
        System.out.println(genders0);// ["male"]
    }
}

SUPPRESS_EXCEPTIONS

该选项确保不会从路径评估传播异常。 遵循这些简单的规则:

  • 如果选项ALWAYS_RETURN_LIST存在,将返回一个空列表
  • 如果选项ALWAYS_RETURN_LIST不存在返回null

JsonProvider SPI

JsonPath附带了五个不同的JsonProvider:

  • JsonSmartJsonProvider(默认)
  • JacksonJsonProvider
  • JacksonJsonNodeJsonProvider
  • GsonJsonProvider
  • JsonOrgJsonProvider
  • JakartaJsonProvider

只有在初始化应用程序时,才能更改所演示的配置默认值。强烈反对在运行时进行更改,尤其是在多线程应用程序中。

Configuration.setDefaults(new Configuration.Defaults() {

    private final JsonProvider jsonProvider = new JacksonJsonProvider();
    private final MappingProvider mappingProvider = new JacksonMappingProvider();
      
    @Override
    public JsonProvider jsonProvider() {
        return jsonProvider;
    }

    @Override
    public MappingProvider mappingProvider() {
        return mappingProvider;
    }
    
    @Override
    public Set<Option> options() {
        return EnumSet.noneOf(Option.class);
    }
});

注意:JacksonJsonProvider需要com.fasterxml.jackson.core:jackson-databind:2.4.5,GsonJsonProvider需要在您的类路径上使用com.google.code.gson:gson:2.3.1,即需要导入相关的jar包才能使用。

使用JacksonJsonProvider或GsonJsonProvider可以直接将JsonPath的输出映射到实体类上,如下例:

public class JsonPathTest {
    private String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void test01() {
        // 1.进行默认配置
        Configuration.setDefaults(new Configuration.Defaults() {
            // 注意,这里创建的是GsonJsonProvider和GsonMappingProvider,但需要导入Gson包
            // 还可以使用JacksonJsonProvider和JacksonMappingProvider,也需要导入jackson包
            private final JsonProvider jsonProvider = new GsonJsonProvider();
            private final MappingProvider mappingProvider = new GsonMappingProvider();

            @Override
            public JsonProvider jsonProvider() {
                return jsonProvider;
            }

            @Override
            public Set<Option> options() {
                return EnumSet.noneOf(Option.class);
            }

            @Override
            public MappingProvider mappingProvider() {
                return mappingProvider;
            }
        });
        // 2.设置泛型,List<Book>泛型是无法直接在read()方法中传入的,所以需要在TypeRef<>中设置泛型
        TypeRef<List<Book>> typeRef = new TypeRef<List<Book>>() {};
        // 3.调用read()方法传入路径表达式和泛型并读取数据
        List<Book> books = JsonPath.parse(json).read("$.store.book.*", typeRef);
        // 4.打印结果
        for (Book book : books) {
            System.out.println(book);
        }
        // Book{category='reference', author='Nigel Rees', title='Sayings of the Century', isbin='null', price=8.95}
        // Book{category='fiction', author='Evelyn Waugh', title='Sword of Honour', isbin='null', price=12.99}
        // Book{category='fiction', author='Herman Melville', title='Moby Dick', isbin='0-553-21311-3', price=8.99}
        // Book{category='fiction', author='J. R. R. Tolkien', title='The Lord of the Rings', isbin='0-395-19395-8', price=22.99}
    }

}

class Book {
    private String category;
    private String author;
    private String title;
    private String isbn;
    private float price;

    public Book() {
    }

    public Book(String category, String author, String title, String isbn, float price) {
        this.category = category;
        this.author = author;
        this.title = title;
        this.isbn = isbn;
        this.price = price;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "category='" + category + '\'' +
                ", author='" + author + '\'' +
                ", title='" + title + '\'' +
                ", isbin='" + isbn + '\'' +
                ", price=" + price +
                '}';
    }
}

其他

封装工具类

针对使用JsonPath来提取json字符串中的内容然后封装到实体类中,可以做一些简单的封装:

package json;

import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;

import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author lcl100
 * @desc JsonPath工具类
 * @date 2022-01-10
 */
public class JsonPathUtil {

    /**
     * JsonPath配置
     */
    private final static Configuration configuration = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);

    /**
     * 根据路径表达式读取json中的内容
     *
     * @param json 待读取的json字符串
     * @param path JsonPath路径表达式
     * @return 读取到的结果
     */
    private static Object readPath(String json, String path) {
        Object result = null;
        try {
            result = JsonPath.using(configuration).parse(json).read(path);
        } catch (Exception ignored) {

        }
        return result;
    }

    /**
     * 根据json模板字符串提取json中对应内容然后填充到类中对应映射字段,提取实体类
     *
     * @param json     待提取的json字符串
     * @param jsonPath json模板字符串,存储着实体类属性名与json中对应内容的路径关系
     * @param clazz    待映射的实体类
     * @param <T>      实体类泛型
     * @return 从json字符串中提取了内容后并赋值的实体类对象
     * @throws InstantiationException 创建实例对象异常
     * @throws IllegalAccessException 字段访问权限异常
     */
    public static <T> T getJsonObject(String json, String jsonPath, Class<T> clazz) throws InstantiationException, IllegalAccessException {
        // 提取jsonPath这个json字符串中的所有键值对
        Map<String, Map<String, String>> arrayMap = new HashMap<>();
        Map<String, String> objectMap = new HashMap<>();
        // 判断模板字符串是json对象还是json数组
        jsonPath = jsonPath.trim();
        char firstChar = jsonPath.charAt(0);
        boolean isArray = false;
        if (firstChar == '[') {
            isArray = true;
            // 读取json数组中的所有json对象,每个json对象都是一个Map集合,然后放到List中
            List<Map<String, String>> mapList = (List<Map<String, String>>) readPath(jsonPath, "$");
            for (Map<String, String> map : mapList) {
                arrayMap.put(map.getOrDefault("NAME", null), map);
            }
        } else if (firstChar == '{') {
            isArray = false;
            // 读取所有键值对成一个Map集合
            objectMap = (Map<String, String>) readPath(jsonPath, "$");
        }

        // 获取类的所有字段,依次赋值
        T t = clazz.newInstance();
        Class<?> tClass = t.getClass();
        Field[] fields = tClass.getDeclaredFields();
        for (Field field : fields) {
            // 根据字段名提取对应的路径表达式,然后根据路径表达式在json中提取值
            String name = field.getName();
            // 由于模板字符串的不同,提取路径的方式有所不同
            String path = isArray ? (arrayMap.getOrDefault(name, null) != null ? arrayMap.get(name).get("PATH") : null) : objectMap.getOrDefault(name, null);
            // 根据路径读取值
            Object value = readPath(json, path);
            // 如果使用的是带有正则表达式的模板字符串则需要额外处理
            if (isArray) {
                // 进行正则提取
                Matcher matcher = Pattern.compile(arrayMap.getOrDefault(name, null).getOrDefault("REGEXP", null)).matcher(String.valueOf(value));
                if (matcher.find()) {
                    value = matcher.group(1);
                }
            }
            // 为字段赋值
            setField(field, t, value);
        }

        return t;
    }

    /**
     * 根据json模板字符串提取json中对应内容然后填充到类中对应映射字段,提取实体类集合
     *
     * @param json     待提取的json字符串
     * @param jsonPath json模板字符串,存储着实体类属性名与json中对应内容的路径关系
     * @param clazz    待映射的实体类
     * @param <T>      实体类泛型
     * @return 赋值后的实体类集合
     * @throws InstantiationException 创建实例对象异常
     * @throws IllegalAccessException 字段访问权限异常
     */
    public static <T> List<T> getJsonList(String json, String jsonPath, Class<T> clazz) throws InstantiationException, IllegalAccessException {
        List<T> list = new ArrayList<>();
        Map<String, Map<String, String>> arrayMap = new HashMap<>();
        Map<String, String> objectMap = new HashMap<>();
        // 根据是否有"DATA"来判断模板字符串是什么模式
        boolean isArray = jsonPath.contains("DATA");
        if (isArray) {// 该模式使用了正则表达式
            // 直接是一个数组,包含多个json对象,即包含多个Map集合
            List<Map<String, String>> mapList = (List<Map<String, String>>) readPath(jsonPath, "$.DATA");
            for (int i = 0; i < mapList.size(); i++) {
                Map<String, String> map = mapList.get(i);
                String name = map.get("NAME");
                String path = map.get("PATH");
                if (name != null && !"".equals(name.trim()) && path != null && !"".equals(path.trim())) {
                    arrayMap.put(name, map);
                }
            }
        } else {// 该模式未使用正则表达式
            // 直接是一个Map集合,包括所有的键值对
            objectMap = (Map<String, String>) readPath(jsonPath, "$");
        }

        // 计算ROOT_LIST所表示路径在json字符串中代表的数组的长度
        String rootPath = (String) readPath(jsonPath, "$.ROOT_LIST");
        int length = (int) readPath(json, rootPath + ".length()");
        for (int j = 0; j < length; j++) {
            // 创建实体类对象
            T t = clazz.newInstance();
            Class<?> tClass = t.getClass();
            // 获取所有声明的字段
            Field[] fields = tClass.getDeclaredFields();
            for (Field field : fields) {
                // 字段名
                String fieldName = field.getName();
                // 待赋给字段的值
                Object value;
                if (isArray) {
                    // 过滤掉不需要提取的字段
                    if (arrayMap.getOrDefault(fieldName, null) == null) {
                        continue;
                    }
                    // 从json中读取指定路径的值
                    value = readPath(json, rootPath + "[" + j + "]" + arrayMap.getOrDefault(fieldName, null).get("PATH"));
                    // 需要进行正则处理
                    Matcher matcher = Pattern.compile(arrayMap.getOrDefault(fieldName, null).getOrDefault("REGEXP", null)).matcher(String.valueOf(value));
                    if (matcher.find()) {
                        value = matcher.group(1);
                    }
                } else {
                    // 过滤掉不需要提取的字段
                    if (objectMap.getOrDefault(fieldName, null) == null) {
                        continue;
                    }
                    // 从json中读取指定路径的值
                    value = readPath(json, rootPath + "[" + j + "]" + objectMap.getOrDefault(fieldName, null));
                }
                // 为字段赋值
                setField(field, t, value);
            }
            list.add(t);
        }
        return list;
    }

    /**
     * 根据字段类型为实例对象上的字段进行赋值
     *
     * @param field 待赋值的字段
     * @param t     实例对象
     * @param value 待赋予的值
     * @throws IllegalAccessException 字段访问权限异常
     */
    private static void setField(Field field, Object t, Object value) throws IllegalAccessException {
        String typeName = field.getType().getTypeName();
        field.setAccessible(true);
        if (typeName.endsWith("String")) {
            field.set(t, String.valueOf(value));
        } else if (typeName.endsWith("byte")) {
            byte result = 0;
            try {
                result = Byte.parseByte(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("Byte")) {
            Byte result = null;
            try {
                result = Byte.valueOf(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("short")) {
            short result = 0;
            try {
                result = Short.parseShort(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("Short")) {
            Short result = null;
            try {
                result = Short.valueOf(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("int")) {
            int result = 0;
            try {
                result = Integer.parseInt(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("Integer")) {
            Integer result = null;
            try {
                result = Integer.valueOf(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("long")) {
            long result = 0L;
            try {
                result = Long.parseLong(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("Long")) {
            Long result = null;
            try {
                result = Long.valueOf(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("float")) {
            float result = 0F;
            try {
                result = Float.parseFloat(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("Float")) {
            Float result = null;
            try {
                result = Float.valueOf(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("double")) {
            double result = 0;
            try {
                result = Double.parseDouble(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("Double")) {
            Double result = null;
            try {
                result = Double.valueOf(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("boolean")) {
            boolean result = false;
            try {
                result = Boolean.parseBoolean(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("Boolean")) {
            Boolean result = null;
            try {
                result = Boolean.valueOf(value.toString());
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("char")) {
            char result = '\u0000';
            try {
                result = value.toString().charAt(0);
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("Character")) {
            Character result = null;
            try {
                result = value.toString().charAt(0);
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else if (typeName.endsWith("Date")) {
            Date result = null;
            // 暂时只能处理时间戳的日期
            try {
                Matcher timestampMatcher = Pattern.compile("1[\\d]{9}").matcher(value.toString());
                if (timestampMatcher.find()) {
                    result = new Date(Long.parseLong(value.toString()));
                }
            } catch (Exception ignored) {

            }
            field.set(t, result);
        } else {
            System.out.println("无法转换的字段类型: " + typeName);
        }
    }
}

测试类如下:

public class JsonPathUtilTest {
    private final String json = "{\"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},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}";

    @Test
    public void testGetJsonObject() throws InstantiationException, IllegalAccessException {
        // 注意,jsonPath模板表达式中,键名为实体类中的属性名;键值为在json字符串中的完整路径

        // 第一种形式的模板字符串
        String jsonPath1 = "{\n" +
                "  \"category\": \"$.store.book[0].category\",\n" +
                "  \"author\": \"$.store.book[0].author\",\n" +
                "  \"title\": \"$.store.book[0].title\",\n" +
                "  \"isbn\": \"$.store.book[0].isbn\",\n" +
                "  \"price\": \"$.store.book[0].price\"\n" +
                "}";
        Book book1 = JsonPathUtil.getJsonObject(json, jsonPath1, Book.class);
        System.out.println(book1);

        // 第二种形式的模板字符串
        String jsonPath2 = "[\n" +
                "  {\n" +
                "    \"NAME\": \"category\",\n" +
                "    \"PATH\": \"$.store.book[0].category\",\n" +
                "    \"REGEXP\": \"(.*)\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"NAME\": \"author\",\n" +
                "    \"PATH\": \"$.store.book[0].author\",\n" +
                "    \"REGEXP\": \"(.*)\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"NAME\": \"title\",\n" +
                "    \"PATH\": \"$.store.book[0].title\",\n" +
                "    \"REGEXP\": \"(.*)\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"NAME\": \"isbn\",\n" +
                "    \"PATH\": \"$.store.book[0].isbn\",\n" +
                "    \"REGEXP\": \"(.*)\"\n" +
                "  },\n" +
                "  {\n" +
                "    \"NAME\": \"price\",\n" +
                "    \"PATH\": \"$.store.book[0].price\",\n" +
                "    \"REGEXP\": \"(\\\\d+\\\\.\\\\d+)\"\n" +
                "  }\n" +
                "]";
        Book book2 = JsonPathUtil.getJsonObject(json, jsonPath1, Book.class);
        System.out.println(book2);
    }

    @Test
    public void testGetJsonList() throws InstantiationException, IllegalAccessException {
        // 注意,ROOT_LIST为必选字段,表示列表的根路径
        // 注意,jsonPath模板表达式中,键名为实体类中的属性名;键值为在json字符串中的相对路径(相对列表的根路径而言)

        // 第一种形式的模板字符串
        String jsonPath1 = "{\n" +
                "  \"ROOT_LIST\": \"$.store.book\",\n" +
                "  \"category\": \".category\",\n" +
                "  \"author\": \".author\",\n" +
                "  \"title\": \".title\",\n" +
                "  \"isbn\": \".isbn\",\n" +
                "  \"price\": \".price\"\n" +
                "}";
        List<Book> bookList1 = JsonPathUtil.getJsonList(json, jsonPath1, Book.class);
        for (Book book : bookList1) {
            System.out.println(book);
        }

        // 第二种形式的模板字符串
        String jsonPath2 = "{\n" +
                "  \"ROOT_LIST\": \"$.store.book\",\n" +
                "  \"DATA\": [\n" +
                "    {\n" +
                "      \"NAME\": \"category\",\n" +
                "      \"PATH\": \".category\",\n" +
                "      \"REGEXP\": \"(.*)\"\n" +
                "    },\n" +
                "    {\n" +
                "      \"NAME\": \"author\",\n" +
                "      \"PATH\": \".author\",\n" +
                "      \"REGEXP\": \"(.*)\"\n" +
                "    },\n" +
                "    {\n" +
                "      \"NAME\": \"title\",\n" +
                "      \"PATH\": \".title\",\n" +
                "      \"REGEXP\": \"(.*)\"\n" +
                "    },\n" +
                "    {\n" +
                "      \"NAME\": \"isbn\",\n" +
                "      \"PATH\": \".isbn\",\n" +
                "      \"REGEXP\": \"(.*)\"\n" +
                "    },\n" +
                "    {\n" +
                "      \"NAME\": \"price\",\n" +
                "      \"PATH\": \".price\",\n" +
                "      \"REGEXP\": \"(\\\\d+\\\\.\\\\d+)\"\n" +
                "    }\n" +
                "  ]\n" +
                "}";
        List<Book> bookList2 = JsonPathUtil.getJsonList(json, jsonPath2, Book.class);
        for (Book book : bookList2) {
            System.out.println(book);
        }
    }
}

具体说明请参考:GitHub 文档

在线JsonPath测试

网站地址:在线 JSONPath 测试

可以帮助我们快速判断jsonpath写得是否正确,结果是否是我们期望的结果,而不需要写代码去验证。

在这里插入图片描述

参考资料

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值