JsonPath中文文档

JsonPath教程

操做

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

功能方法

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

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<E>
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左侧(数组或字符串)的大小应与右侧一致
emptyleft(数组或字符串)应为空

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
}
JsonPathResult
$.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
  • ?(<expression>) - 表达式
  • [<number>, <number> (, <number>)] - 多个数组索引

无限路径总是返回一个列表(由当前 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:

只有在应用程序初始化时,才能按演示更改配置默认值。强烈不建议在运行期间更改配置,尤其是在多线程应用程序中。

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
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值