This Go library allows you to extract parts of a JSON object using the JSONPath query syntax.
The core JSONPath syntax supported by this library is based on:
- Stefan GΓΆssner's JSONPath - XPath for JSON
- Christoph Burgmer's json-path-comparison
- JSONPath Internet Draft Development
For details on syntax compatibility with other libraries, see π my comparison results.
go get github.com/AsaiYusuke/jsonpath/v2package main
import (
"encoding/json"
"fmt"
"github.com/AsaiYusuke/jsonpath/v2"
)
func main() {
jsonPath, srcJSON := `$.key`, `{"key":"value"}`
var src any
json.Unmarshal([]byte(srcJSON), &src)
output, _ := jsonpath.Retrieve(jsonPath, src)
outputJSON, _ := json.Marshal(output)
fmt.Println(string(outputJSON))
// Output:
// ["value"]
}- The JSONPath syntax parser is implemented using PEG, which helps keep the source code simple and maintainable.
- Robust unit tests are provided to prevent bugs and ensure consistent results.
- The library provides comprehensive error types, making it easy for users to handle errors appropriately.
- The library incorporates consensus behaviors from Christoph Burgmer's json-path-comparison, ensuring high compatibility with other implementations.
The Retrieve function extracts values from a JSON object using a JSONPath expression:
output, err := jsonpath.Retrieve(jsonPath, src)The Parse function returns a parser function that checks the JSONPath syntax in advance. You can use this parser function to repeatedly extract values with the same JSONPath:
parsed, err := jsonpath.Parse(jsonPath)
output1, err1 := parsed(src1)
output2, err2 := parsed(src2)parser function
- The parser function accepts an optional second argument: a pointer to a
[]anybuffer. - When omitted or
nil, a new slice is allocated on each call and returned. - When provided (non-nil), the buffer will be reset to length 0 and filled directly, enabling reuse without extra allocations.
Example of buffer reuse:
parsed, _ := jsonpath.Parse("$[*]")
buf := make([]any, 0, 4)
args := []*[]any{&buf}
out1, _ := parsed([]any{1}, args...) // writes into buf -> [1]
out2, _ := parsed([]any{2}, args...) // reuses the same buf -> now [2]
fmt.Println(out1)
fmt.Println(out2)
fmt.Println(buf)
// Output:
// [2]
// [2]
// [2]Note: Do not share the same buffer across goroutines concurrently.
If an error occurs during API execution, a specific error type is returned. The following error types help you identify the cause:
| Error type | Message format | Symptom | Ex |
|---|---|---|---|
ErrorInvalidSyntax |
invalid syntax (position=%d, reason=%s, near=%s) |
The JSONPath contains invalid syntax. The reason in the message provides more details. | π |
ErrorInvalidArgument |
invalid argument (argument=%s, error=%s) |
An argument in the JSONPath is invalid according to Go syntax. | π |
ErrorFunctionNotFound |
function not found (path=%s) |
The specified function in the JSONPath was not found. | π |
ErrorNotSupported |
not supported (path=%s, feature=%s) |
The JSONPath uses unsupported syntax. | π |
| Error type | Message format | Symptom | Ex |
|---|---|---|---|
ErrorMemberNotExist |
member did not exist (path=%s) |
The specified object or array member does not exist in the JSON object. | π |
ErrorTypeUnmatched |
type unmatched (path=%s, expected=%s, found=%s) |
The type of the node in the JSON object does not match what is expected by the JSONPath. | π |
ErrorFunctionFailed |
function failed (path=%s, error=%s) |
The function specified in the JSONPath failed to execute. | π |
Type checking makes it easy to determine which error occurred.
import jsonpath "github.com/AsaiYusuke/jsonpath/v2"
import errors "github.com/AsaiYusuke/jsonpath/v2/errors"
_, err := jsonpath.Retrieve(jsonPath, srcJSON)
switch err.(type) {
case errors.ErrorMemberNotExist:
fmt.Printf("retry with other srcJSON: %v", err)
// handle or continue
case errors.ErrorInvalidArgument:
return nil, fmt.Errorf("specified invalid argument: %v", err)
}You can use user-defined functions to format results. The function syntax is appended after the JSONPath expression.
There are two types of functions:
A filter function applies a user-defined function to each value in the result, transforming them individually.
An aggregate function combines all values in the result into a single value.
Instead of retrieving values directly, you can obtain accessors (Getters / Setters) for the input JSON. These accessors allow you to update the original JSON object.
Enable this feature by calling Config.SetAccessorMode().
Setters are not available for some results, such as when using function syntax in the JSONPath.
Accessor operations follow Go's map/slice semantics. If you modify the structure of the JSON, be aware that accessors may not behave as expected. To avoid issues, obtain a new accessor each time you change the structure.
Some behaviors in this library differ from the consensus of other implementations. For a full comparison, see π this result.
These behaviors may change in the future if more appropriate approaches are found.
The following character types are allowed for identifiers in dot-child notation:
| Character type | Available | Escape required |
|---|---|---|
* Numbers and alphabets (0-9 A-Z a-z) |
Yes | No |
* Hyphen and underscore (- _) |
Yes | No |
* Non-ASCII Unicode characters (0x80 - 0x10FFFF) |
Yes | No |
* Other printable symbols (Space ! " # $ % & ' ( ) * + , . / : ; < = > ? @ [ \ ] ^ ` { | } ~) |
Yes | Yes |
* 0x00 - 0x1F, 0x7F) |
No | - |
Printable symbols (except hyphen and underscore) can be used by escaping them.
JSONPath : $.abc\.def
srcJSON : {"abc.def":1}
Output : [1]
Wildcards in qualifiers can be specified as a union with subscripts.
JSONPath : $[0,1:3,*]
srcJSON : [0,1,2,3,4,5]
Output : [0,1,2,0,1,2,3,4,5]
Regular expression syntax follows Go's regular expression rules. In particular, you can use "(?i)" to make the regular expression case-insensitive.
JSONPath : $[?(@=~/(?i)CASE/)]
srcJSON : ["Case","Hello"]
Output : ["Case"]
JSONPaths that return a value group cannot be used with a comparator or regular expression. However, you can use these syntaxes for existence checks.
| JSONPaths that return a value group | example |
|---|---|
| Recursive descent | @..a |
| Multiple identifier | @['a','b'] |
| Wildcard identifier | @.* |
| Slice qualifier | @[0:1] |
| Wildcard qualifier | @[*] |
| Union in the qualifier | @[0,1] |
| Filter qualifier | @.a[?(@.b)] |
- comparator example (error)
JSONPath : $[?(@..x == "hello world")]
srcJSON : [{"a":1},{"b":{"x":"hello world"}}]
Error : ErrorInvalidSyntax
- regular expression example (error)
JSONPath : $[?(@..x=~/hello/)]
srcJSON : [{"a":1},{"b":{"x":"hello world"}}]
Error : ErrorInvalidSyntax
- existence check example
JSONPath : $[?(@..x)]
srcJSON : [{"a":1},{"b":{"x":"hello world"}}]
Output : [{"b":{"x":"hello world"}}]
If a JSONPath filter begins with the Root, it performs a whole-match operation if any match is found.
JSONPath : $[?($..x)]
srcJSON : [{"a":1},{"b":{"x":"hello world"}}]
Output : [{"a":1},{"b":{"x":"hello world"}}]
Benchmark results for various Go JSONPath libraries (measured by myself) are available in the following repository:
- Syntax
- Identifier
- identifier in dot notation
- identifier in bracket notation
- wildcard
- multiple identifiers in brackets
- recursive retrieval
- Qualifier
- index
- slice
- wildcard
- Filter
- logical operations
- comparators
- JSONPath retrieval in filter
- script
- Function
- filter
- aggregate
- Refer to the consensus behaviors
- Identifier
- Architecture
- PEG syntax analysis
- Error handling
- Function support
- JSON accessors
- Go language manner
- retrieve with an object unmarshaled to interface
- retrieve with the json.Number type
- Source code
- Release version
- Unit tests
- syntax tests
- benchmarks
- coverage >80%
- Examples
- CI automation
- Documentation
- README
- API documentation
- comparison results (local)
- Development status
- requirements and functional design
- Decided to follow a standard or reference implementation for JSONPath syntax
- design-based coding
- testing
- documentation
- requirements and functional design
- Future ToDo
- Go language affinity
- retrieve with an object unmarshaled to struct
- retrieve with struct tags
- retrieve with user-defined objects
- Go language affinity