A performant and feature-rich JSONPath implementation for Go.
Backed by bytedance/sonic for JSON parsing, it provides a fluent Builder API and generic result handling, designed for a concise and efficient JSON query experience.
- Full JSONPath Support:
- Path Selectors:
.key,['key'] - Array Operations: Index
[i](supports negative indexing), Slice[start:end:step] - Wildcard:
* - Recursive Descent:
.. - Filters:
[?(expr)], supporting logical operators (&&,||,!) and comparison operators (==,!=,>,>=,<,<=,=~)
- Path Selectors:
- High Performance: Powered by sonic for underlying JSON marshaling and unmarshaling.
- Chainable Builder: Offers a fluent API for constructing queries, allowing a mix of standard JSONPath strings and programmatic building.
- Generics Support: Directly map results to Go types (
ResultsT[T],IntoT[T]). - Optional Support: Integrated with
bytedance/gg/goptionfor Optional-style return value handling.
go get github.com/Wenrh2004/jsonpathThe most common way is to compile the path and then execute the query:
package main
import (
"fmt"
"github.com/Wenrh2004/jsonpath"
)
func main() {
jsonStr := `{"store": {"book": [{"title": "Sayings of the Century", "price": 8.95}, {"title": "Sword of Honour", "price": 12.99}]}}`
// 1. Compile the path
q, err := jsonpath.Compile("$.store.book[?(@.price < 10)].title")
if err != nil {
panic(err)
}
// 2. Execute query (EvalBytes or Eval)
nodes, err := q.EvalBytes([]byte(jsonStr))
if err != nil {
panic(err)
}
// 3. Get results
// nodes is []core.Node, containing Path and Value
fmt.Println("Result:", nodes[0].Value) // Output: Sayings of the Century
// Or use generic helper functions to convert results
titles, _ := jsonpath.Results[string](nodes)
fmt.Println("Titles:", titles)
}The Builder pattern allows you to build queries programmatically or continue querying on specific nodes, offering great flexibility.
package main
import (
"fmt"
"github.com/Wenrh2004/jsonpath"
)
func main() {
data := []byte(`...`) // Your JSON data
// Create Builder from raw bytes
builder, _ := jsonpath.FromBytes(data)
// Chain calls: Store -> Book -> Price >= 20 -> First -> Title
title, err := jsonpath.IntoT[string](
builder.Clone().
Select("$.store.book[?(@.price >= 20)]"). // Filter using JSONPath string
Index(0). // Get the 0th element (supports chaining on result sets)
Child("title"), // Get title field
)
if err != nil {
panic(err)
}
fmt.Println("Expensive book:", title)
}If you prefer Optional-style error handling (like Rust/Java Optional), you can use methods like EvalBytesO:
// Returns goption.O[[]core.Node]
optNodes := jsonpath.EvalBytesO(q, data, false)
if optNodes.IsPresent() {
nodes := optNodes.Unwrap()
// ...
}| Syntax | Description | Example |
|---|---|---|
$ |
Root node | $ |
.key |
Child node (dot) | $.store.book |
['key'] |
Child node (bracket) | $['store']['book'] |
* |
Wildcard (all children) | $.store.book[*].author |
.. |
Recursive descent | $..author |
[i] |
Array index (supports negative) | $.book[0], $.book[-1] |
[s:e:step] |
Array slice | $.book[0:2], $.book[::-1] |
[?(expr)] |
Filter expression | $.book[?(@.price < 10)] |
@ |
Current node (in filter) | @.price |
length() |
Length function | [?(length(@.title) > 10)] |
- Comparison:
==,!=,>,>=,<,<= - Regex:
=~(e.g.,[?(@.name =~ 'Harry.*')]) - Logical:
&&,||,!(e.g.,[?(@.price < 10 && @.category == 'fiction')])
- jsonpath: Root package, providing core API (
Compile,Eval,FromBytes) and Builder entry points. - jsonpath/core: Core engine, containing Lexer, Parser, AST execution (
Segment), and evaluation logic (Eval). - jsonpath/builder: Builder implementation, providing fluent operation methods.
- jsonpath/optional.go: Provides Optional-style helper APIs.
This project is licensed under the MIT License.