JsonPath操作JSON
引言
假设想要解析JSON对象且JSON的格式不固定,最开始的做法是转换Json对象为Map对象,然后一层层遍历Map(例如使用Gson),那么有没有类似XML中的xpath解析表达式的工具?最终发现了json-path工具。
参考链接:github json-path
Maven依赖:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
该工具提供了类似XPATH的层级似的JSON解析功能,同时提供读取、写入的功能。
JsonPath语法
Operators
Operator | Description |
---|---|
$ | 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. |
Functions
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.
Function | Description | Output |
---|---|---|
min() | Provides the min value of an array of numbers | Double |
max() | Provides the max value of an array of numbers | Double |
avg() | Provides the average value of an array of numbers | Double |
stddev() | Provides the standard deviation value of an array of numbers | Double |
length() | Provides the length of an array | Integer |
sum() | Provides the sum value of an array of numbers | Double |
Filter Operators
Filters are logical expressions used to filter arrays. A typical filter would be [?(@.age > 18)] where @ represents the current item being processed. More complex filters can be created with logical operators && and ||. String literals must be enclosed by single or double quotes ([?(@.color == ‘blue’)] or [?(@.color == “blue”)]).
Operator | Description |
---|---|
== | 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)] |
in | left exists in right [?(@.size in ['S', 'M'])] |
nin | left does not exists in right |
subsetof | left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])] |
anyof | left has an intersection with right [?(@.sizes anyof ['M', 'L'])] |
noneof | left has no intersection with right [?(@.sizes noneof ['M', 'L'])] |
size | size of left (array or string) should match right |
empty | left (array or string) should be empty |
JsonPath语法示例
Given the 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 | Result |
---|---|
$.store.book[*].author] | The authors of all books |
$..author | All authors |
$.store.* | All things, both books and bicycles |
$.store..price | The price of everything |
$..book[2] | The third book |
$..book[-2] | The second to last book |
$..book[0,1] | The first two books |
$..book[:2] | All books from index 0 (inclusive) until index 2 (exclusive) |
$..book[1:2] | All books from index 1 (inclusive) until index 2 (exclusive) |
$..book[-2:] | Last two books |
$..book[2:] | Book number two from tail |
$..book[?(@.isbn)] | All books with an ISBN number |
$.store.book[?(@.price < 10)] | All books in store cheaper than 10 |
$..book[?(@.price <= $['expensive'])] | All books in store that are not "expensive" |
$..book[?(@.author =~ /.*REES/i)] | All books matching regex (ignore case) |
$..* | Give me every thing |
$..book.length()/td> | The number of books |
JsonPath代码示例
解析并修改如下JSON(示例为Istio中的某个VirtualService定义):
{
"kind": "VirtualService",
"apiVersion": "networking.istio.io/v1alpha3",
"metadata": {
"name": "my-svc",
"namespace": "tsp"
},
"spec": {
"hosts": [
"s106.tsp.svc.cluster.local",
"my-svc.tsp.svc.cluster.local"
],
"gateways": [
"mesh",
"my-svc"
],
"http": [
{
"match": [
{
"headers": {
"user-id": {
"regex": "11|22|33"
}
},
"uri": {
"prefix": "/256"
}
}
],
"rewrite": {
"uri": "/my_svc"
},
"route": [
{
"destination": {
"host": "s106.tsp.svc.cluster.local",
"subset": "v257"
}
}
]
},
{
"match": [
{
"uri": {
"prefix": "/256"
}
}
],
"rewrite": {
"uri": "/my_svc"
},
"route": [
{
"destination": {
"host": "s106.tsp.svc.cluster.local",
"subset": "v256"
}
}
]
},
{
"route": [
{
"destination": {
"host": "s106.tsp.svc.cluster.local",
"subset": "v256"
}
}
]
}
],
"tcp": null,
"tls": null
}
}
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.JsonPath;
import com.svc.mgt.utils.JsonUtils;
import net.minidev.json.JSONArray;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.Map;
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
/**
* K8s查询结果Json转换 - 测试
*
* @Ahthor luohq
* @Date 2020-07-20
*/
@RunWith(PowerMockRunner.class) //使用PowerMockRunner运行时
@PowerMockIgnore({"javax.management.*"}) //忽略一些mock异常
public class K8sJsonResultTest {
private static final Logger logger = LogManager.getLogger(K8sJsonResultTest.class);
@Test
public void testVsJson() {
String vsResult = "...";
/** JsonPath解析 */
final DocumentContext ctx = JsonPath.parse(vsResult);
/** 基础路由过滤器 - 包含http[].match 且 不包含有http[].match[0].headers*/
Filter baseRouteFilter = filter(
where("$.match").exists(true).and("$.match[0].headers").exists(false)
);
/** 读取属性(指定过滤器)*/
JSONArray httpRoutes = ctx.read("$.spec.http[?]*", baseRouteFilter);
for (Object httpRoute : httpRoutes) {
String subset = JsonPath.read(httpRoute, "$.route[0].destination.subset");
logger.info("baseRoute -> subset={}, httpRoute={}", subset, JsonPath.parse(httpRoute).jsonString());
}
/** 读取属性 */
Map httpRouteObj = ctx.read("$.spec.http[0]");
//替换user-id | product-id
String headersJson = String.format("{\"user-id\": {\"regex\": \"%s\"},\"product-id\": {\"exact\":\"%s\"}}", "111|222", "12342");
/** 设置属性 - 重置baseRoute中的match.headers(add添加数组元素、set设置属性、put新增或修改属性) */
JsonPath.parse(httpRouteObj).put("$.match[0]", "headers", JsonUtils.fromJson(headersJson, Map.class));
logger.info("svcRouteRule: {}", JsonPath.parse(httpRouteObj).jsonString());
logger.info("vs.ctx={}", ctx.jsonString());
/** 删除对象 - 清空 $.spec.http数组 */
ctx.delete("$.spec.http.*");
logger.info("vs.ctx={}", ctx.jsonString());
}
}
运行结果:
2020-07-21 14:49:39.389 INFO [main][K8sJsonResultTest.java:146] - baseRoute -> subset=v256, httpRoute={"match":[{"uri":{"prefix":"/256"}}],"rewrite":{"uri":"/mx_tour_app"},"route":[{"destination":{"host":"s106.tsp.svc.cluster.local","subset":"v256"}}]}
2020-07-21 14:49:39.488 INFO [main][K8sJsonResultTest.java:154] - svcRouteRule: {"match":[{"headers":{"user-id":{"regex":"111|222"},"product-id":{"exact":"12342"}},"uri":{"prefix":"/256"}}],"rewrite":{"uri":"/mx_tour_app"},"route":[{"destination":{"host":"s106.tsp.svc.cluster.local","subset":"v257"}}]}
2020-07-21 14:49:39.488 INFO [main][K8sJsonResultTest.java:155] - vs.ctx={"kind":"VirtualService","apiVersion":"networking.istio.io/v1alpha3","metadata":{"name":"my-app","namespace":"tsp","selfLink":"/apis/networking.istio.io/v1alpha3/namespaces/tsp/virtualservices/my-app","uid":"368f02de-ac7b-11ea-b7d7-fa163e432d7e","resourceVersion":"194978865","generation":11,"creationTimestamp":"2020-06-12T07:06:18Z"},"spec":{"hosts":["s106.tsp.svc.cluster.local","my-app.tsp.svc.cluster.local","my-app-pre.xxxx.com"],"gateways":["mesh","my-app"],"http":[{"match":[{"headers":{"user-id":{"regex":"111|222"},"product-id":{"exact":"12342"}},"uri":{"prefix":"/256"}}],"rewrite":{"uri":"/mx_tour_app"},"route":[{"destination":{"host":"s106.tsp.svc.cluster.local","subset":"v257"}}]},{"match":[{"uri":{"prefix":"/256"}}],"rewrite":{"uri":"/mx_tour_app"},"route":[{"destination":{"host":"s106.tsp.svc.cluster.local","subset":"v256"}}]},{"route":[{"destination":{"host":"s106.tsp.svc.cluster.local","subset":"v256"}}]}],"tcp":null,"tls":null}}
2020-07-21 14:49:39.489 INFO [main][K8sJsonResultTest.java:160] - vs.ctx={"kind":"VirtualService","apiVersion":"networking.istio.io/v1alpha3","metadata":{"name":"my-app","namespace":"tsp","selfLink":"/apis/networking.istio.io/v1alpha3/namespaces/tsp/virtualservices/my-app","uid":"368f02de-ac7b-11ea-b7d7-fa163e432d7e","resourceVersion":"194978865","generation":11,"creationTimestamp":"2020-06-12T07:06:18Z"},"spec":{"hosts":["s106.tsp.svc.cluster.local","my-app.tsp.svc.cluster.local","my-app-pre.xxxx.com"],"gateways":["mesh","my-app"],"http":[],"tcp":null,"tls":null}}
本文地址:https://blog.csdn.net/luo15242208310/article/details/107487627
上一篇: 软件License许可设计思路
下一篇: vue 组件
推荐阅读