JsonPath中文文档

JsonPath教程

文章目录

  • JsonPath教程
    • 操做
    • 功能方法
    • JsonPath示例
    • 读取文档
    • 什么时候返回
    • 谓词
      • 内联谓词
      • 过滤谓词
      • 自己动手
    • Path vs Value
    • 设置值
    • 调整配置
      • 选项
        • default_path_leaf_too_null
        • always_return_list
        • suppress_exceptions
        • require_properties
      • JsonProvider SPI
      • Cache SPI
    • 进阶功能
      • 进阶语法
      • 合并修改结构体成员

操做

操做符号 描述
$ 要查询的根元素。它启动所有路径表达式。
@ 过滤谓词正在处理的当前节点。
* 通配符。可用于任何需要名称或数字的地方。
.. 深度扫描。可在任何需要名称的地方使用。
. 子类
['' (, '')] 括号中注明的一个或多个子女括号中注明的一个或多个子女
[ (, )] 数组索引
[start:end] 数组切分运算符
[?()] 过滤表达式。表达式的值必须是布尔值。

功能方法

函数可以在路径的尾端调用–函数的输入就是路径表达式的输出。函数的输出由函数本身决定。

Functions can be invoked at the tail end of a path - the input to a function is the output of the path expression. The function output is dictated by the function itself.

方法 描述 输出类型
min() 提供数字数组的最小值 Double
max() 提供数字数组的最大值 Double
avg() 提供数字数组的平均值 Double
stddev() 提供数字数组的标准偏差值 Double
length() 提供数组的长度 Integer
sum() 提供数字数组的和值 Double
keys() 提供属性键(终端子键 ~ 的替代选择) Set
concat(X) 提供路径输出的合并版本,并添加一个新项目 like input
append(X) 为 json 路径输出数组添加项目 like input
first() 提供数组的第一个项目 Depends on the array
last() 提供数组的最后一项 Depends on the array
index(X) 提供索引数组的项: X,如果 X 为负数,则向后取值 Depends on the array
Filter Operators

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

运算符号 描述
== 左等于右(注意 "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 left(数组或字符串)应为空

JsonPath示例

{
    "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 Result
$.store.book[*].author 所有书籍的作者
$…author 所有作者
$.store.* 万物,包括书籍和自行车
$.store…price 所有书籍的价格
$…book[2] 第三本书
$…book[-2] 倒数第二本书
$…book[0,1] 前两本书
$…book[:2] 从索引 0(含)到索引 2(不含)的所有图书
$…book[1:2] 从索引 1(含)到索引 2(不含)的所有书籍
$…book[-2:] 最后两本书
$…book[2:] 从索引 2(含)到最后的所有书籍
$…book[?(@.isbn)] 所有有 ISBN 编号的书籍
$.store.book[?(@.price < 10)] 店内所有书籍价格均低于 10
Misplaced @ $..book[?(@.price <= $[‘expensive’])] 获取json中book数组中price<=$[‘expensive’]结果的所有值
$…book[?(@.author =~ /.*REES/i)] 与 regex 匹配的所有图书(忽略大小写)
$…* 给我一切
$…book.length() 书籍数量

读取文档

使用 JsonPath 最简单直接的方法是通过静态读取 API。

String json = "...";

List<String> authors = JsonPath.read(json, "$.store.book[*].author");

如果你只想读取一次,这没有问题。如果您还需要读取其他路径,这就不是办法了,因为每次调用 JsonPath.read(…) 都会对文档进行解析。为了避免这个问题,可以先解析 json。

String json = "...";
Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);

String author0 = JsonPath.read(document, "$.store.book[0].author");
String author1 = JsonPath.read(document, "$.store.book[1].author");

JsonPath 还提供了流畅的应用程序接口。这也是最灵活的一种。

String json = "...";

ReadContext ctx = JsonPath.parse(json);

List<String> authorsOfBooksWithISBN = ctx.read("$.store.book[?(@.isbn)].author");


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

什么时候返回

在 Java 中使用 JsonPath 时,了解结果的预期类型非常重要。JsonPath 会自动尝试将结果转换为调用者期望的类型。

//Will throw an java.lang.ClassCastException    
List<String> list = JsonPath.parse(json).read("$.store.book[0].author")

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

在评估一条路径时,你需要了解一条路径何时是确定的这一概念。如果一条路径包含以下内容,那么它就是不确定的:

  • .. - a deep scan operator
  • ?() - 表达式
  • [, (, )] - 多个数组索引

无限路径总是返回一个列表(由当前 JsonProvider 表示)。

默认情况下,MappingProvider SPI 会提供一个简单对象映射器。这允许你指定想要的返回类型,而 MappingProvider 会尝试执行映射。下面的示例演示了 Long 和 Date 之间的映射。

String json = "{\"date_as_long\" : 1411455611975}";

Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);

如果将 JsonPath 配置为使用 JacksonMappingProvider、GsonMappingProvider 或 JakartaJsonProvider,甚至可以将 JsonPath 输出直接映射到 POJO 中。

Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class);

要获取完整的类属类型信息,请使用 TypeRef。

TypeRef<List<String>> typeRef = new TypeRef<List<String>>() {};

List<String> titles = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[*].title", typeRef);

谓词

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

内联谓词

内联谓词是在路径中定义的谓词。

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

您可以使用 && 和 || 组合多个谓词 [?(@.price < 10 && @.category == ‘fiction’)] , [?(@.category == ‘reference’ || @.price > 10)]。

您可以使用 ! 来否定一个谓词 [?(!(@.price < 10 && @.category == ‘fiction’))] 。

过滤谓词

如下所示,可以使用过滤器 API 构建谓词:

import static com.jayway.jsonpath.JsonPath.parse;
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
...
...

Filter cheapFictionFilter = filter(
   where("category").is("fiction").and("price").lte(10D)
);

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

注意路径中过滤器的占位符 ? 当提供多个过滤器时,它们将按顺序应用,其中占位符的数量必须与提供的过滤器数量相匹配。您可以在一个过滤器操作中指定多个谓词占位符[?, ?],但两个谓词必须匹配。

过滤器还可以与 "OR "和 "AND "组合使用。

Filter fooOrBar = filter(
   where("foo").exists(true)).or(where("bar").exists(true)
);
   
Filter fooAndBar = filter(
   where("foo").exists(true)).and(where("bar").exists(true)
);

自己动手

第三种方法是实现自己的谓词

Predicate booksWithISBN = new Predicate() {
    @Override
    public boolean apply(PredicateContext ctx) {
        return ctx.item(Map.class).containsKey("isbn");
    }
};

List<Map<String, Object>> books = 
   reader.read("$.store.book[?].isbn", List.class, booksWithISBN);

Path vs Value

在 Goessner 的实现中,JsonPath 可以返回 Path 或 Value。Value 是默认值,也是上面所有示例的返回值。如果你更希望得到我们的查询所命中的元素的路径,这可以通过一个选项来实现。

Configuration conf = Configuration.builder()
   .options(Option.AS_PATH_LIST).build();

List<String> pathList = using(conf).parse(json).read("$..author");

assertThat(pathList).containsExactly(
    "$['store']['book'][0]['author']",
    "$['store']['book'][1]['author']",
    "$['store']['book'][2]['author']",
    "$['store']['book'][3]['author']");

设置值

标准库提供了设置值的可能性。

String newJson = JsonPath.parse(json).set("$['store']['book'][0]['author']", "Paul").jsonString();

调整配置

选项

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

default_path_leaf_too_null

该选项使 JsonPath 在缺少叶时返回空值。请看下面的 json

[
   {
      "name" : "john",
      "gender" : "male"
   },
   {
      "name" : "ben"
   }
]
Configuration conf = Configuration.defaultConfiguration();

//Works fine
String gender0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
//PathNotFoundException thrown
String gender1 = JsonPath.using(conf).parse(json).read("$[1]['gender']");

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

//Works fine
String gender0 = JsonPath.using(conf2).parse(json).read("$[0]['gender']");
//Works fine (null is returned)
String gender1 = JsonPath.using(conf2).parse(json).read("$[1]['gender']");

always_return_list

该选项用于配置 JsonPath,即使路径是确定的,也会返回一个列表。

Configuration conf = Configuration.defaultConfiguration();

//ClassCastException thrown
List<String> genders0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");

Configuration conf2 = conf.addOptions(Option.ALWAYS_RETURN_LIST);

//Works fine
List<String> genders0 = JsonPath.using(conf2).parse(json).read("$[0]['gender']");

suppress_exceptions

该选项确保路径评估不会传播异常。它遵循以下简单规则

  • 如果存在 ALWAYS_RETURN_LIST 选项,则返回空列表
  • 如果不存在 ALWAYS_RETURN_LIST 选项,则返回空列表

require_properties

该选项用于配置 JsonPath,使其在评估不确定路径时要求使用路径中定义的属性。

Configuration conf = Configuration.defaultConfiguration();

//Works fine
List<String> genders = JsonPath.using(conf).parse(json).read("$[*]['gender']");

Configuration conf2 = conf.addOptions(Option.REQUIRE_PROPERTIES);

//PathNotFoundException thrown
List<String> genders = JsonPath.using(conf2).parse(json).read("$[*]['gender']");

JsonProvider SPI

JsonPath 提供五种不同的 JsonProviders:

  • JsonSmartJsonProvider (default)
  • 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 的类路径。

Jakarta EE 9 JSON-P (JSR-342) 和 JSON-B (JSR-367) 提供程序至少需要 Java 8,并要求应用程序运行时类路径上有兼容的 JSON API 实现(如 Eclipse Glassfish 和 Eclipse Yasson);Java EE 应用程序容器也可能提供此类实现。还请注意,Apache Johnzon 尚不兼容 Jakarta EE 9 规范的类路径,如果选择 JSON-B 映射提供程序,则还必须配置和使用 JSON-P 提供程序。

Jakarta EE 9 关于 JSON 处理和数据库绑定(映射)的规范有一个特点,即 Json 数组和对象在完全解析或写入后具有不变性。为了遵守 API 规范,同时允许 JsonPath 通过添加、设置/输入、替换和删除操作来修改 Json 文档,JakartaJsonProvider 必须使用可选的 true 参数进行 initiliazed:

  • JsonProvider jsonProvider = new JakartaJsonProvider(true)(启用可变 Json 数组和对象)
  • JsonProvider jsonProvider = new JakartaJsonProvider()(默认,严格遵循 JSON-P API)

无论采用哪种启动模式,都支持使用 JsonPath 进行的所有查找和读取操作。默认模式所需的内存更少,性能更高。

Cache SPI

JsonPath 2.1.0 引入了新的缓存 SPI。这允许 API 用户根据自己的需要配置路径缓存。缓存必须在首次访问前配置好,否则会产生 JsonPathException 异常。JsonPath 有两种缓存实现

  • com.jayway.jsonpath.spi.cache.LRUCache(默认,线程安全)
  • com.jayway.jsonpath.spi.cache.NOOPCache(无缓存)

如果您想实现自己的缓存,API 也很简单。

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

进阶功能

进阶语法

  • * 通配操作符,表示获取当前作用对象的 所有 子成员。
  • *~ 内置函数,表示获取当前可迭代对象所有子对象的名称。
  • min() 内置函数,表示获取当前可迭代对象子对象的最小值。
  • max() 内置函数,表示获取当前可迭代对象子对象的最大值。
  • sum() 内置函数,表示获取当前可迭代对象子对象之和。
  • concat() 内置函数,表示连接多个对象并生成字符串。

合并修改结构体成员

在某些场景下,需要在数据处理中对 JSON 结构体的多个对象进行合并整合处理,以便投递到下游进行下一步操作。考虑如下格式:

{
  "data": {
    "Response": {
      "SubnetSet": [
        {
          "VpcId": "vpc-xxxxxxxx",
          "SubnetId": "subnet-xxxxxxxx",
          "SubnetName": "ckafka_cloud_subnet-1",
          "CidrBlock": "10.0.0.0/19",
          "Ipv6CidrBlock": "",
          "IsDefault": false,
          "IsRemoteVpcSnat": false,
          "EnableBroadcast": false,
          "Zone": "ap-changsha-ec-1",
          "RouteTableId": "rtb-xxxxxxxx",
          "NetworkAclId": "",
          "TotalIpAddressCount": 8189,
          "AvailableIpAddressCount": 8033,
          "CreatedTime": "2021-01-25 17:31:00",
          "TagSet": [],
          "CdcId": "",
          "IsCdcSubnet": 0,
          "LocalZone": false,
          "IsShare": false
        }
      ],
      "TotalCount": 1,
      "RequestId": "705c4955-0cd9-48b2-9132-79eadae2e3e6"
    }
  },
  "code": 0
}

当下游不具有计算功能,需要在数据处理中聚合 Vpc 以及子网属性时,可以使用 JSONPath 中的 concat() 函数进行多个字段的聚合,并且在此基础上对字符串进行修改。

例如可以使用 $.concat($.data.Response.SubnetSet[0].VpcId,"#",$.data.Response.SubnetSet[0].SubnetId,"#",$.data.Response.SubnetSet[0].CidrBlock)) 语法拼接 Vpc 和子网的属性,并且通过 # 字符加以分割。

运行结果如下所示,可以看出在测试结果中已经成功获取整合了 Vip 相关的资源信息:

vpc-xxxxxxxx#subnet-xxxxxxxx#10.0.0.0/19

你可能感兴趣的:(java,mysql,数据库)