基于java对jsonpath的初步学习

1. 介绍

类似于XPath在xml文档中的定位,JsonPath表达式通常是用来路径检索或设置Json的。

2. 语法(操作符)

操作员 描述
` 操作员
------------------------- ------------------------------------------------------------
                   | 要查询的根元素。用于表示一个json数据,可以是数组或对象       |

| @ | 过滤器断言(filter predicate)处理的当前节点对象,类似于java中的this字段 |
| * | 通配符,可以表示一个名字或数字 |
| .. | 可以理解为递归搜索,深层扫描。可在任何需要名称的地方使用。 |
| .<name> | 表示一个子节点 |
| ['<name>' (, '<name>')] | 表示一个或多个子节点 |
| [<number> (, <number>)] | 表示一个或多个数组下标 |
| [start:end] | 数组片段,区间为[start,end),不包含end |
| [?(<expression>)] | 过滤表达式。表达式必须计算为布尔值。 |

2. 语法(函数)

可以在JsonPath表达式执行后进行调用,其输入值为表达式的结果。

功能 描述 输出量
min() 提供数值类型数组的最小值 Double
max() 提供数值类型数组的最大值 Double
avg() 提供数值类型数组的平均值 Double
stddev() 提供数值类型数组的标准偏差值 Double
length() 提供数值类型数组的长度 Integer
sum() 提供数值类型数组的总和 Double

2. 语法(过滤器)

过滤器是用于过滤数组的逻辑表达式,一个通常的表达式形如:[?(@.age > 18)],可以通过逻辑表达式&&或||组合多个过滤器表达式,例如[?(@.price < 10 && @.category == ‘fiction’)],字符串必须用单引号或双引号包围,例如[?(@.color == ‘blue’)] or [?(@.color == “blue”)]

操作符 描述
== 等于符号,但数字1不等于字符1(note that 1 is not equal to ‘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 判空符号

3. 官方示例参考

{
    "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表达式 说明
$.store.book[*].author 所有书籍的作者
$..author 所有作者
$.store.* 所有东西,包括书籍和自行车
$.store..price 所有东西的价格
$..book[2] 第三本书
$..book[-2] 倒数第二本书
$..book[0,1] 前两本书
$..book[:2] All 从索引0(含)到索引2(不含)的图书
$..book[1:2] 从索引1(含)到索引2(不含)的图书
$..book[-2:] 最后两本书
$..book[2:] 从尾数第二本书
$..book[?(@.isbn)] 所有带有ISBN号的图书
$.store.book[?(@.price < 10)] 商店中所有价格低于10书籍
$..book[?(@.price <= $['expensive'])] 商店中所有非“昂贵”的图书
$..book][?(@.author =~ /.*REES/i)] 所有与正则表达式匹配的书籍(忽略大小写)
$..* 所有数据

4. javaDemo 测试

新建maven项目最终的pom.xml文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.demo</groupId>
    <artifactId>studyJsonPath</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--   jsonpath jar包     -->
        <dependency>
            <groupId>com.jayway.jsonpath</groupId>
            <artifactId>json-path</artifactId>
            <version>2.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.2</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>jsonpathDemo</finalName>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

测试用的json文件如上

使用JsonPath的最简单,最直接的方法是通过静态readAPI。

import com.jayway.jsonpath.JsonPath;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * @author john
 * @date 2020/4/8 - 17:31
 */
public class JsonPathStudyDemo {
    public static void main(String[] args) throws IOException {
        File file = new File(JsonPathStudyDemo.class.getClassLoader().getResource("test.json").getPath());
        String json = FileUtils.readFileToString(file);

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

如果仅是单次使用是OK的,如果是多次使用的话,为了避免每次解析json都需要调用JsonPath.read(...),你可以先解析json

import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;

/**
 * @author john
 * @date 2020/4/8 - 17:31
 */
public class JsonPathStudyDemo {
    public static void main(String[] args) throws IOException {
        File file = new File(JsonPathStudyDemo.class.getClassLoader().getResource("test.json").getPath());
        String json = FileUtils.readFileToString(file);

        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");
        System.out.println(author0);
        System.out.println(author1);
    }
}

JsonPath还提供了流式的API。这也是最灵活的一种。

import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.ReadContext;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * @author john
 * @date 2020/4/8 - 17:31
 */
public class JsonPathStudyDemo {
    public static void main(String[] args) throws IOException {
        File file = new File(JsonPathStudyDemo.class.getClassLoader().getResource("test.json").getPath());
        String json = FileUtils.readFileToString(file);

        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);
        System.out.println(authorsOfBooksWithISBN);
        System.out.println(expensiveBooks);
    }
}

默认情况下,MappingProvider SPI提供了一个简单的对象映射器。这使您可以指定所需的返回类型,并且MappingProvider将尝试执行映射。在下面的示例中,演示了Long和Date之间的映射。

import com.jayway.jsonpath.JsonPath;
import java.io.IOException;
import java.util.Date;

/**
 * @author john
 * @date 2020/4/8 - 17:31
 */
public class JsonPathStudyDemo {
    public static void main(String[] args) throws IOException {
        String json = "{\"date_as_long\" : 1411455611975}";
        Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);
        System.out.println(date);
    }
}

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

先修改pom.xml,添加如下依赖

  <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.16</version>
        </dependency>

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

新建bean文件

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

/**
 * @author john
 * @date 2020/4/8 - 18:13
 */
@Slf4j
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Book {
    private String category;
    private String author;
    private String title;
    private String isbn;
    private double price;
}

进行测试

import cn.demo.bean.Book;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.EnumSet;
import java.util.Set;

/**
 * @author john
 * @date 2020/4/8 - 17:31
 */
public class JsonPathStudyDemo {
    public static void main(String[] args) throws IOException {
        File file = new File(JsonPathStudyDemo.class.getClassLoader().getResource("test.json").getPath());
        String json = FileUtils.readFileToString(file);
        Configuration.setDefaults(new Configuration.Defaults() {

            private final JsonProvider jsonProvider = new JacksonJsonNodeJsonProvider();
            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);
            }
        });

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

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

import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.TypeRef;
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

/**
 * @author john
 * @date 2020/4/8 - 17:31
 */
public class JsonPathStudyDemo {
    public static void main(String[] args) throws IOException {
        File file = new File(JsonPathStudyDemo.class.getClassLoader().getResource("test.json").getPath());
        String json = FileUtils.readFileToString(file);
        Configuration.setDefaults(new Configuration.Defaults() {

            private final JsonProvider jsonProvider = new JacksonJsonNodeJsonProvider();
            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> titles = JsonPath.parse(json).read("$.store.book[*].title", typeRef);
        System.out.println(titles);
    }
}

向json中指定位置添加内容

import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;

/**
 * @author john
 * @date 2020/4/8 - 17:31
 */
public class JsonPathStudyDemo {
    public static void main(String[] args) throws IOException {
        File file = new File(JsonPathStudyDemo.class.getClassLoader().getResource("test.json").getPath());
        String json = FileUtils.readFileToString(file);
        DocumentContext document = JsonPath.parse(json).put("$.store.book[*]", "kind", "paper");

        System.out.println(document.jsonString());
    }
}

5. 参考

JsonPath教程

JSONPath-简单入门

JsonPath的用法

jsonpath github

posted @ 2020-04-08 18:54  if年少有为  阅读(1434)  评论(0编辑  收藏  举报