Java中json处理工具JsonPath的使用教程

 更新时间:2023年08月30日 11:38:32   作者:movee  
JsonPath类似于XPath,是一种json数据结构节点定位和导航表达式语言,这篇文章主要为大家介绍了JsonPath的基本使用,需要的小伙伴可以参考下

最近在思考构建一个服务编排(Service Orchestration)系统,考虑这个系统至少需要具备以下特征:

使用统一的方法定义服务功能单元

使用一种通用的方式将一个或多个服务的输出映射到下游服务的输入,映射时支持基础的数据转换与处理

支持以搭积木的方式将低层服务功能单元组织成更高层抽象的服务功能,直至一个完整的服务

用户编排服务时,具备较大的灵活性定制业务

1 JsonPath是什么

json本质上是一个树形数据结构,同样作为典型树形数据结构的xml文档有XPathXML Path Language)这种广泛使用的定位和选择节点的表达式语言,java对象也有OGNLObject-Graph Navigation Language)这种对象属性定位和导航表达式语言,JsonPath类似于XPath,是一种json数据结构节点定位和导航表达式语言。

2 JsonPath的基本使用

2.1 JsonPath依赖

要使用JsonPath,需要在pom.xml文件中添加相应的依赖:

2.2 json测试数据

为了便于说明,本文用到的json测试数据,如果没有特别说明,使用以下的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
}

2.3 读取json内容

需要获取指定json指定节点的数据,提供json字符串和JsonPath表达式即可:

    // 读取json字符串的expensive叶子节点的值
    Object expensive = JsonPath.read(jsonStr, "$.expensive");
    log.info("expensive value: {}, class: {}", expensive, expensive.getClass().getCanonicalName());
    // 读取json字符串store节点下bicycle节点的值
    Object bicycle = JsonPath.read(jsonStr, "$.store.bicycle");
    log.info("bicycle value: {}, class: {}", bicycle, bicycle.getClass().getCanonicalName());
    // 读取json字符串store下第一个book节点的值
    Object book = JsonPath.read(jsonStr, "$.store.book[0]");
    log.info("book value: {}, class: {}", book, book.getClass().getCanonicalName());
    // 读取json字符串所有的author后代节点的值
    Object authors = JsonPath.read(jsonStr, "$..author");
    log.info("authors value: {}, class: {}", authors, authors.getClass().getCanonicalName());
    // 读取json字符串中所有price值小于10的值
    Object books = JsonPath.read(jsonStr, "$.store.book[?(@.price < 10)]");
    log.info("books value: {}, class: {}", books, books.getClass().getCanonicalName());

输出如下日志:

expensive value: 10, class: java.lang.Integer
bicycle value: {color=red, price=19.95}, class: java.util.LinkedHashMap
book value: {category=reference, author=Nigel Rees, title=Sayings of the Century, price=8.95}, class: java.util.LinkedHashMap
authors value: ["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"], class: net.minidev.json.JSONArray
books value: [{"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}], class: net.minidev.json.JSONArray

2.3.1 基本语法

在JsonPath表达式语法中,$表示json顶层节点,.表示直接孩子,所以$.expensive表示json字符串的直接孩子节点expensive,节点也可以用[]表示。所以$['expensive']$.expensive等价。如果[]中是数字,则表示数组的某个元素,所以$.store.book[0]表示第1本书。数组支持切片,$.store.book[0:3]表示第1本到第3本书。..则表示任意的后代。*则表示所有的子节点。综合起来如下表所示:

OperatorDescription
$The root element to query. This starts all path expressions.
@The current node being processed by a filter predicate.
*Wildcard. Available anywhere a name or numeric are required.
..Deep scan. Available anywhere a name is required.
.<name>Dot-notated child
['<name>' (, '<name>')]Bracket-notated child or children
[<number> (, <number>)]Array index or indexes
[start:end]Array slice operator
[?(<expression>)]Filter expression. Expression must evaluate to a boolean value.

JsonPath还支持根据条件过滤,表达式[?(expression)]中括号内为条件表达式。表达式$.store.book[?(@.price < 10)]@表示遍历到的当前book,整个表达式就表示store中价格小于10的所有的book。

JsonPath的条件表达式支持以下操作符:

OperatorDescription
==left is equal to right (note that 1 is not equal to '1')
!=left is not equal to right
<left is less than right
<=left is less or equal to right
left is greater than right
>=left is greater than or equal to right
=~left matches regular expression [?(@.name =~ /foo.*?/i)]
inleft exists in right [?(@.size in ['S', 'M'])]
ninleft does not exists in right
subsetofleft is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])]
anyofleft has an intersection with right [?(@.sizes anyof ['M', 'L'])]
noneofleft has no intersection with right [?(@.sizes noneof ['M', 'L'])]
sizesize of left (array or string) should match right
emptyleft (array or string) should be empty

当然一些常用的filter函数也是要支持的:

FunctionDescriptionOutput type
min()Provides the min value of an array of numbersDouble
max()Provides the max value of an array of numbersDouble
avg()Provides the average value of an array of numbersDouble
stddev()Provides the standard deviation value of an array of numbersDouble
length()Provides the length of an arrayInteger
sum()Provides the sum value of an array of numbersDouble
keys()Provides the property keys (An alternative for terminal tilde ~)Set<E>
concat(X)Provides a concatinated version of the path output with a new itemlike input
append(X)add an item to the json path output arraylike input
first()Provides the first item of an arrayDepends on the array
last()Provides the last item of an arrayDepends on the array
index(X)Provides the item of an array of index: X, if the X is negative, take from backwardsDepends on the array
Filter Operators

详细的JsonPath表达式语法和支持的过滤条件请参考github官方文档,文档本身介绍的已经很详细了。

2.3.2 返回值

从上面的日志结果来看,JsonPath的返回结果的数据类型依情况而定,大概情况是(具体实际情况与底层使用的Json处理组件相关):

  • 如果结果是json叶子节点,则是叶子节点对应的基本数据类型
  • 如果是数组,则是JSONArray
  • 如果是对象,这是LinkedHashMap

2.3.3 指定返回值类型

JsonPath也支持指定返回值类型,类似于json反序列化

Long expensive = JsonPath.parse(jsonStr).read("$.expensive", Long.class);
Bicycle bicycle = JsonPath.parse(jsonStr).read("$.store.bicycle", Bicycle.class);

如果json底层处理组件是jackson或gson,还可以支持泛型,假如使用jackson,先添加jackson-databind依赖:

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.15.2</version>
    </dependency>

然后配置使用jackson进行json处理和映射即可,如下所示:

Configuration.setDefaults(new Configuration.Defaults() {
    // 底层使用jackson
    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);
    }
});
TypeRef<List<String>> typeRef = new TypeRef<List<String>>() {};
List<String> authors = JsonPath.parse(jsonStr).read("$..author", typeRef);

2.4 修改json的值

2.4.1 为节点设置新值

Object book = JsonPath.read(jsonStr, "$.store.book[0]");
String target = "{\"x\": 128}";
String modified = JsonPath.parse(target).set("$.x", book).jsonString();

输出:

modified: {"x":{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}}

可见目标json字符串的节点x的值替换为了book的值,新的值与原先的值可以是完全不同的类型

2.4.2 为对象节点添加新的子节点

下面的例子为target json字符串添加了一个名字为k的子节点。注意,这个方法只能作用于json对象,不能是叶子节点或数组

Object book = JsonPath.read(jsonStr, "$.store.book[0]");
String target = "{\"x\": 128}";
String modified = JsonPath.parse(target).put("$", "k", book).jsonString();

2.4.3 对数组添加新的元素

String target = "{\"x\": [128]}";
String modified = JsonPath.parse(target).put("$.x", 300).jsonString();

2.4.4 重命名节点名字

String target = "{\"x\": [128]}";
String modified = JsonPath.parse(target).renameKey("$", "x", "y").jsonString();

2.4.5 删除

String target = "{\"x\": [128]}";
String modified = JsonPath.parse(target).delete("$.x").jsonString();

3 JsonPath的高级特性

3.1 进一步配置你的JsonPath

JsonPath提供了一些配置项来调整它的功能,这些配置项配置在Option枚举类中,如下所示:

public enum Option {
    DEFAULT_PATH_LEAF_TO_NULL,
    ALWAYS_RETURN_LIST,
    AS_PATH_LIST,
    SUPPRESS_EXCEPTIONS,
    REQUIRE_PROPERTIES
}

功能说明:

  • DEFAULT_PATH_LEAF_TO_NULL:当路径表达的节点不存在时,不抛出异常,而是返回null值
  • ALWAYS_RETURN_LIST:返回值总是一个list,即使路径找到的是单个值
  • AS_PATH_LIST:返回找到节点的路径,而不是节点的值
  • SUPPRESS_EXCEPTIONS:异常时不抛出异常

使用配置:

Configuration conf = Configuration.builder()
   .options(Option.AS_PATH_LIST).build();
List<String> pathList = JsonPath.using(conf).parse(jsonStr).read("$..author");
// 返回的是author节点的路径列表
assertThat(pathList).containsExactly(
    "$['store']['book'][0]['author']",
    "$['store']['book'][1]['author']",
    "$['store']['book'][2]['author']",
    "$['store']['book'][3]['author']");

3.2 修改json底层处理组件

JsonPath支持多个json组件,默认使用的是JsonSmartJsonProvider,其他的还包括:

JacksonJsonProvider
JacksonJsonNodeJsonProvider
GsonJsonProvider
JsonOrgJsonProvider
JakartaJsonProvider

使用下面的方式可以修改默认的json provider:

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);
    }
});

3.3 性能优化

如下的方式每次都需要解析json字符串:

Object book = JsonPath.read(jsonStr, "$.store.book[0]");

可以先解析json字符串,然后在查找,这样只需要解析一次:

DocumentContext context = JsonPath.parse(jsonStr)
Long expensive = context.read("$.expensive", Long.class);
Bicycle bicycle = context.read("$.store.bicycle", Bicycle.class);

还可以使用cache,JsonPath已经提供了两个cache:

com.jayway.jsonpath.spi.cache.LRUCache (default, thread safe)
com.jayway.jsonpath.spi.cache.NOOPCache (no cache)

我们可以这样配置使用cache:

CacheProvider.setCache(new LRUCache(100));

或者配置一个我们自己实现的cache:

CacheProvider.setCache(new Cache() {
    //Not thread safe simple cache
    private Map<String, JsonPath> map = new HashMap<String, JsonPath>();
    @Override
    public JsonPath get(String key) {
        return map.get(key);
    }
    @Override
    public void put(String key, JsonPath jsonPath) {
        map.put(key, jsonPath);
    }
});

到此这篇关于Java中json处理工具JsonPath的使用教程的文章就介绍到这了,更多相关Java JsonPath内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis基于TypeHandler实现敏感数据加密

    Mybatis基于TypeHandler实现敏感数据加密

    业务场景中经常会遇到诸如用户手机号,身份证号,银行卡号,邮箱,地址,密码等等信息,属于敏感信息,本文就来介绍一下Mybatis基于TypeHandler实现敏感数据加密,感兴趣的可以了解一下
    2023-10-10
  • Java基本数据类型与封装类型详解(int和Integer区别)

    Java基本数据类型与封装类型详解(int和Integer区别)

    这篇文章主要介绍了Java基本数据类型与封装类型详解(int和Integer区别) ,需要的朋友可以参考下
    2017-02-02
  • Java实现可视化走迷宫小游戏的示例代码

    Java实现可视化走迷宫小游戏的示例代码

    这篇文章主要介绍了Java如何实现可视化走迷宫小游戏。本程序适用于java程序员巩固类与对象、文件读取、事件响应、awt包中各种工具的相关概念以及对逻辑能力的锻炼,需要的可以参考一下
    2022-11-11
  • Idea 自动生成测试的实现步骤

    Idea 自动生成测试的实现步骤

    当我们在写完一些接口方法后需要测试时,一个一个新建测试类比较麻烦 idea给我们提供了快捷办法,本文主要介绍了Idea 自动生成测试的实现步骤,具有一定的参考价值,感兴趣的可以了解一下
    2024-05-05
  • MybatisPlus的LambdaQueryWrapper用法详解

    MybatisPlus的LambdaQueryWrapper用法详解

    LambdaQueryWrapper<Tag> 是 MyBatis-Plus 框架中的一个功能强大的查询构造器,它用于构建 SQL 查询条件,具有一定的参考价值,感兴趣的可以了解一下
    2024-10-10
  • Java实现五子棋游戏

    Java实现五子棋游戏

    这篇文章主要为大家详细介绍了Java实现五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • Java将RTF转换为PDF格式的实现

    Java将RTF转换为PDF格式的实现

    本文主要介绍了Java将RTF转换为PDF格式的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • 利用java实现单词倒序排列

    利用java实现单词倒序排列

    这篇文章就是利用java实现单词倒序排列,感觉像是在变魔术,感兴趣的小伙伴来见证一下
    2015-07-07
  • MyBatis-plus使用lambda条件构造器报错问题及解决

    MyBatis-plus使用lambda条件构造器报错问题及解决

    这篇文章主要介绍了MyBatis-plus使用lambda条件构造器报错问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • 解决Callable的对象中,用@Autowired注入别的对象失败问题

    解决Callable的对象中,用@Autowired注入别的对象失败问题

    这篇文章主要介绍了解决Callable的对象中,用@Autowired注入别的对象失败问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07

最新评论