100% RFC 9535 compliant JSONPath implementation for .NET. High-performance JSONPath query engine with full support for all selectors, filters, functions, and recursive descent. Includes JsonPathHelper utility for feature detection and complexity analysis. Extended test coverage certified through 536 comprehensive tests.
Blazing.Json.JSONPath is a high-performance, 100% RFC 9535 compliant JSONPath implementation for .NET. This library enables powerful querying of JSON documents using standardized JSONPath syntax, providing a robust and reliable solution for navigating complex JSON structures.
Unlike other JSONPath implementations that may use custom or non-standard syntax, Blazing.Json.JSONPath strictly adheres to the RFC 9535 specification, ensuring predictable behavior and maximum interoperability with other RFC-compliant tools.
Whether you're querying API responses, processing configuration files, or analyzing complex data structures, Blazing.Json.JSONPath provides the precision and reliability you need with the familiar JSONPath syntax you already know.
.. operatorBlazing.Json.JSONPath is 100% RFC 9535 compliant, validated through comprehensive testing:
Test Summary:
Total: 324 tests
Passed: 324 tests ?
Failed: 0 tests ?
Skipped: 0 tests
Success: 100% ?
This compliance ensures:
The RFC 9535 specification defines the standard JSONPath query language. Blazing.Json.JSONPath implements every aspect of this specification:
| RFC Component | Compliance | Tests |
|---|---|---|
| Lexer (2.1) | ? 100% | 100+ |
| Parser (2.2-2.3) | ? 100% | 50+ |
| Evaluator (2.3-2.4) | ? 100% | 120+ |
| Filter Expressions (2.3.5) | ? 100% | 80+ |
| Functions (2.4) | ? 100% | 35+ |
| TOTAL | ? 100% | 324 |
For detailed compliance verification, see:
? Use Blazing.Json.JSONPath When:
Blazing.Json.JSONPath is certified 100% compliant with RFC 9535 - JSONPath: Query Expressions for JSON.
Certification Date: January 11, 2026
Standard Version: RFC 9535
Test Coverage: 324 passing tests (0 failures)
Validation: All RFC features, semantics, and edge cases verified
All RFC 9535 features are fully implemented and tested:
Perfect implementation of all RFC semantics:
For complete compliance details, see RFC9535_COMPLIANCE_VERIFICATION.md.
Install via NuGet Package Manager:
<PackageReference Include="Blazing.Json.JSONPath" Version="1.0.0" />Or via the .NET CLI:
dotnet add package Blazing.Json.JSONPathOr via the Package Manager Console:
Install-Package Blazing.Json.JSONPathusing System.Text.Json;
using Blazing.Json.JSONPath.Parser;
using Blazing.Json.JSONPath.Evaluator;
// Sample JSON data
var json = JsonDocument.Parse("""
{
"store": {
"book": [
{"category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95},
{"category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "price": 8.99},
{"category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "price": 22.99}
],
"bicycle": {"color": "red", "price": 19.95}
}
}
""");
// Parse JSONPath query
var query = JsonPathParser.Parse("$.store.book[?@.price < 10]");
// Evaluate query
var evaluator = new JsonPathEvaluator();
var results = evaluator.Evaluate(query, json.RootElement);
// Process results
foreach (var result in results)
{
Console.WriteLine($"{result.NormalizedPath}: {result.Value}");
}
// Output:
// $['store']['book'][0]: {"category":"reference","author":"Nigel Rees",...}
// $['store']['book'][1]: {"category":"fiction","author":"Herman Melville",...}Navigate JSON structures with standard JSONPath syntax:
var bookstore = JsonDocument.Parse("""
{
"store": {
"book": [
{"title": "Book 1", "price": 8.95},
{"title": "Book 2", "price": 12.99},
{"title": "Book 3", "price": 8.99}
]
}
}
""").RootElement;
// Root identifier
var query1 = JsonPathParser.Parse("$");
var all = evaluator.Evaluate(query1, bookstore);
// Name selector (dot notation)
var query2 = JsonPathParser.Parse("$.store.book");
var books = evaluator.Evaluate(query2, bookstore);
// Name selector (bracket notation)
var query3 = JsonPathParser.Parse("$['store']['book']");
var booksAlt = evaluator.Evaluate(query3, bookstore);
// Array index
var query4 = JsonPathParser.Parse("$.store.book[0]");
var firstBook = evaluator.Evaluate(query4, bookstore);
// Negative index (from end)
var query5 = JsonPathParser.Parse("$.store.book[-1]");
var lastBook = evaluator.Evaluate(query5, bookstore);
// Wildcard selector
var query6 = JsonPathParser.Parse("$.store.book[*].title");
var allTitles = evaluator.Evaluate(query6, bookstore);Powerful filtering with comparison and logical operators:
var products = JsonDocument.Parse("""
{
"products": [
{"name": "Laptop", "price": 1200, "category": "electronics", "inStock": true},
{"name": "Mouse", "price": 25, "category": "electronics", "inStock": true},
{"name": "Desk", "price": 350, "category": "furniture", "inStock": true},
{"name": "Chair", "price": 200, "category": "furniture", "inStock": false}
]
}
""").RootElement;
// Comparison operators
var cheap = JsonPathParser.Parse("$.products[?@.price < 100]");
var expensive = JsonPathParser.Parse("$.products[?@.price >= 200]");
var exactPrice = JsonPathParser.Parse("$.products[?@.price == 350]");
// Logical AND
var cheapElectronics = JsonPathParser.Parse(
"$.products[?@.price < 100 && @.category == 'electronics']");
// Logical OR
var furnitureOrCheap = JsonPathParser.Parse(
"$.products[?@.category == 'furniture' || @.price < 50]");
// Logical NOT
var outOfStock = JsonPathParser.Parse("$.products[?!@.inStock]");
// Existence tests
var withOptionalField = JsonPathParser.Parse("$.products[?@.optional]");
// Complex nested conditions
var complex = JsonPathParser.Parse(
"$.products[?(@.price > 100 && @.inStock) || @.category == 'electronics']");All RFC 9535 functions are supported:
var data = JsonDocument.Parse("""
{
"users": [
{"name": "Alice", "email": "alice@example.com", "tags": ["admin", "user"]},
{"name": "Bob", "email": "bob@test.org", "tags": ["user"]},
{"name": "Charlie", "email": "charlie@example.com", "tags": ["user", "moderator"]}
]
}
""").RootElement;
// length() - String, array, or object length
var longNames = JsonPathParser.Parse("$.users[?length(@.name) > 5]");
var manyTags = JsonPathParser.Parse("$.users[?length(@.tags) > 1]");
// count() - Count nodelist elements
var userCount = JsonPathParser.Parse("$[?count($.users[*]) > 2]");
// match() - Full regex match (I-Regexp RFC 9485)
var dotComEmails = JsonPathParser.Parse("$.users[?match(@.email, '.*\\.com$')]");
// search() - Substring regex search
var hasExample = JsonPathParser.Parse("$.users[?search(@.email, 'example')]");
// value() - Convert singular nodelist to value
var singleUser = JsonPathParser.Parse("$.users[?@.name == 'Alice']");
// Use value() in filter: "$.users[?value(@.tags[0]) == 'admin']"RFC-compliant array slicing with start, end, and step:
var numbers = JsonDocument.Parse("""
{
"items": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
}
""").RootElement;
// Basic slicing [start:end]
var firstThree = JsonPathParser.Parse("$.items[0:3]"); // [0, 1, 2]
var middleItems = JsonPathParser.Parse("$.items[3:7]"); // [3, 4, 5, 6]
// Default start (from beginning)
var upToFive = JsonPathParser.Parse("$.items[:5]"); // [0, 1, 2, 3, 4]
// Default end (to end of array)
var fromFive = JsonPathParser.Parse("$.items[5:]"); // [5, 6, 7, 8, 9]
// Negative indices (from end)
var lastTwo = JsonPathParser.Parse("$.items[-2:]"); // [8, 9]
var exceptLast = JsonPathParser.Parse("$.items[:-1]"); // [0, 1, 2, ..., 8]
// Step (every nth element)
var everyOther = JsonPathParser.Parse("$.items[::2]"); // [0, 2, 4, 6, 8]
var everyThird = JsonPathParser.Parse("$.items[1::3]"); // [1, 4, 7]
// Negative step (reverse)
var reversed = JsonPathParser.Parse("$.items[::-1]"); // [9, 8, 7, ..., 0]
var reverseEvery2 = JsonPathParser.Parse("$.items[::-2]"); // [9, 7, 5, 3, 1]Navigate deeply nested structures with the descendant operator:
var company = JsonDocument.Parse("""
{
"company": {
"departments": [
{
"name": "Engineering",
"employees": [
{"name": "Alice", "role": "Senior Dev"},
{"name": "Bob", "role": "Developer"}
]
},
{
"name": "Sales",
"teams": [
{
"region": "North",
"members": [
{"name": "Charlie", "role": "Sales Manager"}
]
}
]
}
]
}
}
""").RootElement;
// Find all 'name' fields at any depth
var allNames = JsonPathParser.Parse("$..name");
// Find all employees arrays
var allEmployees = JsonPathParser.Parse("$..employees");
// Combine recursive descent with filters
var allManagers = JsonPathParser.Parse("$..[?@.role == 'Sales Manager']");
// Recursive descent with wildcards
var allArrayElements = JsonPathParser.Parse("$..[*]");| Selector | Syntax | Description | Example |
|---|---|---|---|
| Root | $ | Root node | $ |
| Name | .name or ['name'] | Object member | $.store.book |
| Wildcard | * or [*] | All elements | $.store.* |
| Index | [index] | Array element | $.book[0] |
| Slice | [start:end:step] | Array slice | $.book[0:3] |
| Filter | ?[expr] | Filtered elements | $.book[?@.price < 10] |
| Segment | Syntax | Description | Example |
|---|---|---|---|
| Child | . or [] | Direct children | $.store.book |
| Descendant | .. | All descendants | $..author |
Filter expressions use @ for the current node and support:
| Operator | Description | Example |
|---|---|---|
== | Equal | @.price == 8.95 |
!= | Not equal | @.category != 'fiction' |
< | Less than | @.price < 10 |
<= | Less than or equal | @.price <= 10 |
> | Greater than | @.price > 10 |
>= | Greater than or equal | @.price >= 10 |
| Operator | Description | Example |
|---|---|---|
&& | Logical AND | @.price < 10 && @.inStock |
|| | Logical OR | @.category == 'fiction' || @.price < 5 |
! | Logical NOT | !@.inStock |
Test if a field exists (non-empty nodelist):
// Has optional field
$.items[?@.optional]
// Has nested property
$.users[?@.profile.avatar]| Function | Signature | Description | Example |
|---|---|---|---|
length() | length(value) | String/array/object length | length(@.name) > 5 |
count() | count(nodelist) | Count nodes in nodelist | count(@.tags) > 2 |
match() | match(string, pattern) | Full regex match | match(@.email, '.*\\.com$') |
search() | search(string, pattern) | Substring regex search | search(@.name, 'Smith') |
value() | value(nodelist) | Singular nodelist to value | value(@.items[0]) |
Blazing.Json.JSONPath uses a three-stage processing pipeline:
Lexical Analysis (Lexer): Tokenizes JSONPath query strings
Syntax Analysis (Parser): Builds Abstract Syntax Tree (AST)
Evaluation (Evaluator): Executes query against JSON document
// Query: Find books under $10
var query = "$.store.book[?@.price < 10]";
// 1. Lexer: Tokenize
// $ . store . book [ ? @ . price < 10 ]
// ROOT DOT NAME DOT NAME LBRACKET FILTER ...
// 2. Parser: Build AST
// Query
// ?? RootSegment ($)
// ?? ChildSegment (.store)
// ?? ChildSegment (.book)
// ?? FilterSelector (?@.price < 10)
// 3. Evaluator: Execute
// - Start at root
// - Navigate to store
// - Navigate to book array
// - For each book:
// * Evaluate @.price < 10
// * If true, include in results
// - Return matching nodes with normalized pathsArrayPool<T> for buffer managementyield return| Operation | Time | Allocations |
|---|---|---|
| Parse simple query | ~1-5 ?s | Minimal |
| Parse complex filter | ~10-20 ?s | Low |
| Evaluate small JSON | ~5-10 ?s | Pooled buffers |
| Evaluate 1MB JSON | ~100-500 ?s | Constant memory |
Note: Performance varies based on query complexity and JSON structure. Run benchmarks in your specific scenarios.
The library includes comprehensive sample projects demonstrating different usage patterns:
All samples are located in the samples/Blazing.Json.JSONPath.Samples directory:
# Clone the repository
git clone https://github.com/gragra33/Blazing.Json.JSONPath.git
cd Blazing.Json.JSONPath
# Run the samples project
dotnet run --project samples/Blazing.Json.JSONPath.SamplesThe samples include:
If you like or are using this project to learn or start your solution, please give it a star. Thanks!
Also, if you find this library useful and you're feeling really generous, please consider buying me a coffee ?.
Initial Release - 100% RFC 9535 Certified
Core Features:
Selector Support:
Segment Types:
Filter Expressions:
Built-in Functions:
length() - String/array/object lengthcount() - Nodelist element countmatch() - Full regex match (I-Regexp)search() - Substring regex searchvalue() - Singular nodelist conversionType System:
Performance Optimizations:
Unicode Support:
Quality Assurance:
Documentation:
License: MIT License - see LICENSE file for details
Copyright � 2026 Graeme Grant. All rights reserved.