Fastjson source code analysis - JSONPath analysis

2021SC@SDUSC

JSONPath

Definition of JSONPath

Just as XPath parses XML, the definition of JSONPath is simply a kind of parsing of JSON documents. Through JSONPath, you can easily obtain the data of the specified "path" for the JSON document, and accurately find the required part in the complex and hard to read JSON string.

Syntax of JSONPath

  1. $represents the root element of the document
  2. @Represents the current element of the document
  3. .node_name means matching child nodes
  4. index is an element in an array
  5. Start: end: step supports array slicing syntax
  6. *As a wildcard, matches all child elements of a member
  7. () use expressions

There are two points to note:

The index of JSONPath starts counting from 0
Strings in JSONPath are represented by single quotes

Use of JSONPath in Fastjson

Using the jsonpath parser in Fastjson, you first need to convert the json string into an object of JSONObject type. Then we call the JSONPath.eval() method to get the specified string.

package com.xiaobu.note.json.fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;

public class FastJsonDemo1 {

    public static void main(String[] args) {
        String jsonStr = "{ \"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,\"isbn\": \"0-553-21311-3\"" +
                "}],\"bicycle\": {\"color\": \"red\",\"price\": 19.95}}}";
        // First parse the JSON data into JSONObject, and then you can directly use JSONPath.
        JSONObject jsonObject = JSON.parseObject(jsonStr);
        System.out.println("book number:"+ JSONPath.eval(jsonObject, "$.store.book.size()") );
        System.out.println("Of the first book title:"+JSONPath.eval(jsonObject,"$.store.book[0].title"));
        System.out.println("Of the first book category and author:"+JSONPath.eval(jsonObject,"$.store.book[0]['category','author']"));
        System.out.println("price>10 My book:"+JSONPath.eval(jsonObject,"$.store.book[price>10]"));
        System.out.println("price>8 Title of the book:"+JSONPath.eval(jsonObject,"$.store.book[price>8]"));
        System.out.println("price>7 My book:"+JSONPath.eval(jsonObject,"$.store.book[price>7]"));
        System.out.println("price>7 Title of the book:"+JSONPath.eval(jsonObject,"$.store.book[price>7].title"));
       //An exception in thread "main" java.lang.unsupported operationexception occurs without single quotes
        System.out.println("The title of the book is Sayings of the Century:"+JSONPath.eval(jsonObject,"$.store.book[title='Sayings of the Century']"));
        System.out.println("bicycle All properties of:"+JSONPath.eval(jsonObject,"$.store.bicycle.*"));
        System.out.println("bicycle:"+JSONPath.eval(jsonObject,"$.store.bicycle"));

    }
}


result:

book number:2
 Of the first book title:Sayings of the Century
 Of the first book category and author:[reference, Nigel Rees]
price>10 My book:[{"author":"Evelyn Waugh","price":12.99,"isbn":"0-553-21311-3","category":"fiction","title":"Sword of Honour"}]
price>8 Title of the book:[{"author":"Evelyn Waugh","price":12.99,"isbn":"0-553-21311-3","category":"fiction","title":"Sword of Honour"}]
price>7 My book:[{"author":"Nigel Rees","price":8.95,"category":"reference","title":"Sayings of the Century"}, {"author":"Evelyn Waugh","price":12.99,"isbn":"0-553-21311-3","category":"fiction","title":"Sword of Honour"}]
price>7 Title of the book:[Sayings of the Century, Sword of Honour]
The title of the book is Sayings of the Century:[{"author":"Nigel Rees","price":8.95,"category":"reference","title":"Sayings of the Century"}]
bicycle All properties of:[red, 19.95]
bicycle:{"color":"red","price":19.95}


You can see that in this difficult json string, we can easily get the key information such as the number of books and the title of the first book.
This is just the use of eval method. The following describes the functions of several other key methods of this class:

Evaluation, static method
public static Object eval(Object rootObject, String path);

Calculate the Size, the number of Map non empty elements, the number of object non empty elements, the Size of the Collection, and the length of the array. The other cannot be evaluated and returns - 1
public static int size(Object rootObject, String path);

Whether it contains, and whether there are objects in the path
public static boolean contains(Object rootObject, String path) { }

Whether to include, whether the specified value exists in the path, and if it is a collection or array, check whether the value exists in the collection
public static boolean containsValue(Object rootObject, String path, Object value) { }

Modify the value of the specified path. If the modification is successful, return true; otherwise, return false
public static boolean set(Object rootObject, String path, Object value) {}

Add elements to an array or collection
public static boolean array_add(Object rootObject, String path, Object... values);

JSONPath code parsing

Analyze the mechanism of eval method in JSONPath.

    public static Object eval(Object rootObject, String path) {
        JSONPath jsonpath = compile(path);
        return jsonpath.eval(rootObject);
    }

This is the eval method we call. You can see that it calls the eval method with the same name:

public Object eval(Object rootObject) {
        if (rootObject == null) {
            return null;
        } else {
            this.init();
            Object currentObject = rootObject;

            for(int i = 0; i < this.segments.length; ++i) {
                currentObject = this.segments[i].eval(this, rootObject, currentObject);
            }

            return currentObject;
        }
    }

At this point, you can see that the eval method is called again. We follow up again:

public Object eval(JSONPath path, Object rootObject, Object currentObject) {
            int size = JSONPath.SizeSegement.instance.eval(path, rootObject, currentObject);//Gets the size of the rootObject, that is, the number of JSONObject objects
            int start = this.start >= 0 ? this.start : this.start + size;
            int end = this.end >= 0 ? this.end : this.end + size;//Get start and end positions
            int array_size = (end - start) / this.step + 1;//Get array size
            if (array_size == -1) {
                return null;
            } else {
                List<Object> items = new ArrayList(array_size);

                for(int i = start; i <= end && i < size; i += this.step) {
                    Object item = path.getArrayItem(currentObject, i);
                    items.add(item);//Traverse the array, add the qualified Object object to the item and return it as the result
                }

                return items;
            }
        }

Obviously, Fastjson's job is to traverse the JSONObject object layer by layer. Through the JSONPath path given by us, according to our parsing rules, put the traversal result into a List object and return it to the caller. Since the start position and end position are determined in advance, the access to the data that will not appear in the result set will be reduced many times in the traversal process, which greatly speeds up the parsing speed. This is also the reason why Fastjson is fast -- reducing access to data by adding conditions.

summary

Using the static method provided by the JSONPath class, the json string can be parsed very quickly, and the json string at the specified position can be returned as an object, which is convenient for the caller of the system. JSONPath has almost the same path parsing rules as XPath. Skillfully using this parsing method and using the static method provided by Fastjson can greatly improve the parsing speed in projects that need to use JSONPath.

Tags: Java Back-end fastjson

Posted on Tue, 30 Nov 2021 13:14:29 -0500 by anf.etienne