Skip to content

This is a full implementation of RFC 9535 & JSON Path Plus

License

Notifications You must be signed in to change notification settings

pb33f/jsonpath

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

89 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pb33f jsonpath

Go Doc

A full implementation of RFC 9535 JSONPath with JSONPath Plus extensions for enhanced querying capabilities.

This library was forked from speakeasy-api/jsonpath.

What is JSONPath Plus?

JSONPath Plus extends the standard JSONPath specification with powerful context-aware operators, type selectors, and navigation features. These extensions are inspired by and compatible with JSONPath-Plus/JSONPath (the JavaScript reference implementation).

Key benefits:

  • 100% backward compatible with RFC 9535 - all standard queries work unchanged
  • Context variables (@property, @path, @parent, etc.) for advanced filtering
  • Type selectors (isString(), isNumber(), etc.) for type-based filtering
  • Parent navigation (^) for traversing up the document tree

Installation

go get github.com/pb33f/jsonpath

Quick Start

package main

import (
    "fmt"
    "github.com/pb33f/jsonpath/pkg/jsonpath"
    "go.yaml.in/yaml/v4"
)

func main() {
    data := `
store:
  book:
    - title: "Book 1"
      price: 10
    - title: "Book 2"
      price: 20
`
    var node yaml.Node
    yaml.Unmarshal([]byte(data), &node)

    // Standard RFC 9535 query
    path, _ := jsonpath.NewPath(`$.store.book[?(@.price > 15)]`)
    results := path.Query(&node)

    // JSONPath Plus query with @property
    path2, _ := jsonpath.NewPath(`$.store.*[?(@property == 'book')]`)
    results2 := path2.Query(&node)
}

JSONPath Plus Extensions

Context Variables

Context variables provide information about the current evaluation context within filter expressions. They are prefixed with @ and can be used in comparisons. By default, context tracking is eager to preserve historical behavior. If you want to reduce overhead for queries that do not use context variables, enable config.WithLazyContextTracking() to turn on tracking only when a query uses @property, @path, @parentProperty, or @index.

@property

Returns the property name (for objects) or index as string (for arrays) used to reach the current node.

# Data
paths:
  /users:
    get: { summary: "Get users" }
    post: { summary: "Create user" }
  /orders:
    get: { summary: "Get orders" }
# Query: Find all GET operations
$.paths.*[?(@property == 'get')]

# Returns: The get objects under /users and /orders

@path

Returns the normalized JSONPath string to the current node being evaluated.

# Data
store:
  book:
    - title: "Book 1"
    - title: "Book 2"
# Query: Find the first book by its path
$.store.book[?(@path == "$['store']['book'][0]")]

# Returns: The first book object

@parent

Returns the parent node of the current node being evaluated. Requires parent tracking to be enabled (automatic when used).

# Data
items:
  - name: "Item 1"
    category: "A"
  - name: "Item 2"
    category: "B"
# Query: Find items where parent is an array
$.items[?(@parent)]

# Returns: All items (parent is the items array)

@parentProperty

Returns the property name or index used to reach the parent of the current node.

# Data
store:
  book:
    details: { price: 10 }
  bicycle:
    details: { price: 20 }
# Query: Find details where parent was reached via 'book'
$.store.*[?(@parentProperty == 'book')]

# Returns: The details object under book

@root

Provides access to the document root from within filter expressions.

# Data
config:
  defaultPrice: 10
items:
  - name: "Item 1"
    price: 10
  - name: "Item 2"
    price: 20
# Query: Find items matching the default price
$.items[?(@.price == @root.config.defaultPrice)]

# Returns: Item 1

@index

Returns the current array index (-1 if not in an array context).

# Data
items:
  - name: "First"
  - name: "Second"
  - name: "Third"
# Query: Find items at even indices
$.items[?(@index == 0 || @index == 2)]

# Returns: First and Third items

Type Selector Functions

Type selectors filter nodes based on their data type. They can be used within filter expressions.

Function Matches
isNull(@) Null values
isBoolean(@) Boolean values (true/false)
isNumber(@) Numeric values (integers and floats)
isInteger(@) Integer values only
isString(@) String values
isArray(@) Array/sequence nodes
isObject(@) Object/mapping nodes

Examples

# Data
mixed:
  - 42
  - "hello"
  - true
  - null
  - [1, 2, 3]
  - { key: "value" }
# Query: Find all string values
$.mixed[?isString(@)]
# Returns: "hello"

# Query: Find all numeric values
$.mixed[?isNumber(@)]
# Returns: 42

# Query: Find all arrays
$.mixed[?isArray(@)]
# Returns: [1, 2, 3]

# Query: Find all objects
$.mixed[?isObject(@)]
# Returns: { key: "value" }

Parent Selector (^)

The caret operator (^) returns the parent of the matched node. This allows you to navigate up the document tree.

# Data
store:
  book:
    - title: "Expensive Book"
      price: 100
    - title: "Cheap Book"
      price: 5
# Query: Find parents of expensive items (price > 50)
$.store.book[?(@.price > 50)]^

# Returns: The book array (parent of the matching book)

Note: Using ^ on the root node returns an empty result.


Property Name Selector (~)

The tilde operator (~) returns the property name (key) instead of the value.

# Data
person:
  name: "John"
  age: 30
  city: "NYC"
# Query: Get all property names
$.person.*~

# Returns: ["name", "age", "city"]

Standard RFC 9535 Features

This library fully implements RFC 9535, including:

Selectors

Selector Example Description
Root $ The root node
Current @ Current node (in filters)
Child .property or ['property'] Direct child access
Recursive ..property Descendant search
Wildcard .* or [*] All children
Array Index [0], [-1] Specific index (negative from end)
Array Slice [0:5], [::2] Range with optional step
Filter [?(@.price < 10)] Conditional selection
Union [0,1,2] or ['a','b'] Multiple selections

Filter Operators

Operator Description
== Equal
!= Not equal
< Less than
<= Less than or equal
> Greater than
>= Greater than or equal
&& Logical AND
|| Logical OR
! Logical NOT

Built-in Functions

Function Description
length(@) Length of string, array, or object
count(@) Number of nodes in a nodelist
match(@.name, 'pattern') Regex full match
search(@.name, 'pattern') Regex partial match
value(@) Extract value from single-node result

Examples

Filtering by Property Name

# Find all HTTP methods in an OpenAPI spec
$.paths.*[?(@property == 'get' || @property == 'post')]

Complex Path Matching

# Find nodes at a specific path pattern
$.store.*.items[*][?(@path == "$['store']['electronics']['items'][0]")]

Type-Safe Queries

# Find all string properties in a config
$..config.*[?isString(@)]

Parent Navigation

# Get containers of items over $100
$..[?(@.price > 100)]^

Combining Features

# Find GET operations where parent path contains 'users'
$.paths[?(@property == '/users')].get

ABNF Grammar

The complete ABNF grammar for RFC 9535 JSONPath is available in the RFC 9535 specification.


Contributing

We welcome contributions! Please open a GitHub issue or Pull Request for bug fixes or features.

This library is compliant with the JSONPath Compliance Test Suite.


License

See LICENSE for details.

About

This is a full implementation of RFC 9535 & JSON Path Plus

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Go 73.5%
  • TypeScript 20.3%
  • JavaScript 4.9%
  • CSS 0.7%
  • Shell 0.4%
  • HTML 0.2%