Hyperbee.Json
1.0.0
See the version list below for details.
dotnet add package Hyperbee.Json --version 1.0.0
NuGet\Install-Package Hyperbee.Json -Version 1.0.0
<PackageReference Include="Hyperbee.Json" Version="1.0.0" />
paket add Hyperbee.Json --version 1.0.0
#r "nuget: Hyperbee.Json, 1.0.0"
// Install Hyperbee.Json as a Cake Addin #addin nuget:?package=Hyperbee.Json&version=1.0.0 // Install Hyperbee.Json as a Cake Tool #tool nuget:?package=Hyperbee.Json&version=1.0.0
JSONPath
A C# implementation of JSONPath for .NET System.Text.Json
with a plugable expression selector.
Why
.NET System.Text.Json
lacks support for JSONPath. The primary goal of this project is to create a JSONPath library for .NET that will
- Directly leverage
System.Text.Json
- Align with the draft JSONPath Specification
- Function according to the emerging consensus of use based on the majority of existing implementations; except through concious exception or deference to the RFC.
- Provide a plugable model for expression script handling.
JSONPath Expressions
JSONPath expressions always refers to a JSON structure in the same way as XPath
expression are used in combination with an XML document. Since a JSON structure is
usually anonymous and doesn't necessarily have a root member object JSONPath
assumes the abstract name $
assigned to the outer level object.
JSONPath expressions can use the dot-notation:
$.store.book[0].title
or the bracket-notation:
$['store']['book'][0]['title']
for input paths. Internal or output paths will always be converted to the more general bracket-notation.
JSONPath allows the wildcard symbol *
for member names and array indices. It
borrows the descendant operator ..
from [E4X][e4x] and the array slice
syntax proposal [start:end:step]
from ECMASCRIPT 4.
Expressions of the underlying scripting language (<expr>
) can be used as an
alternative to explicit names or indices, as in:
$.store.book[(@.length-1)].title
using the symbol @
for the current object. Filter expressions are supported via
the syntax ?(<boolean expr>)
, as in:
$.store.book[?(@.price < 10)].title
Below is a complete overview and a side-by-side comparison of the JSONPath syntax elements with its XPath counterparts:
XPath | JSONPath | Description |
---|---|---|
/ |
$ |
The root object/element |
. |
@ |
The current object/element |
/ |
. or [] |
Child operator |
.. |
n/a | Parent operator |
// |
.. |
Recursive descent. JSONPath borrows this syntax from E4X. |
* |
* |
Wildcard. All objects/elements regardless their names. |
@ |
n/a | Attribute access. JSON structures don't have attributes. |
[] |
[] |
Subscript operator. XPath uses it to iterate over element collections and for [predicates][xpath-predicates]. In Javascript and JSON it is the native array operator. |
\| |
[,] |
Union operator in XPath results in a combination of node sets. JSONPath allows alternate names or array indices as a set. |
n/a | [start:end:step] |
Array slice operator borrowed from ES4. |
[] |
?() |
Applies a filter (script) expression. |
n/a | () |
Script expression, using the underlying script engine. |
() |
n/a | Grouping in XPath |
Examples
Given a simple JSON structure that represents a bookstore:
{ "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
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
XPath | JSONPath | Result | Notes |
---|---|---|---|
/store/book/author |
$.store.book[*].author |
The authors of all books in the store | |
//author |
$..author |
All authors | |
/store/* |
$.store.* |
All things in store, which are some books and a red bicycle | |
/store//price |
$.store..price |
The price of everything in the store | |
//book[3] |
$..book[2] |
The third book | |
//book[last()] |
$..book[(@.length-1)]<br>$..book[-1:] |
The last book in order | |
//book[position()<3] |
$..book[0,1] <br>$..book[:2] |
The first two books | |
//book/*[self::category|self::author] or //book/(category,author) in XPath 2.0 |
$..book[category,author] |
The categories and authors of all books | |
//book[isbn] |
$..book[?(@.isbn)] |
Filter all books with isbn number |
|
//book[price<10] |
$..book[?(@.price<10)] |
Filter all books cheapier than 10 | |
//*[price>19]/.. |
$..[?(@.price>19)] |
Categories with things more expensive than 19 | Parent (caret) not present in original spec |
//* |
$..* |
All elements in XML document; all members of JSON structure | |
/store/book/[position()!=1] |
$.store.book[?(@path !== "$[\'store\'][\'book\'][0]")] |
All books besides that at the path pointing to the first | @path not present in original spec |
Script Evaluators
Hyperbee.Json
provides out-of-the-box expression evaluators for handling JsonPath filter selectors.
Name | Description |
---|---|
JsonPathCSharpEvaluator | A Roslyn based expression evaluator that supports [(@...)] and [?(@...)] expresison syntax |
JsonPathFuncEvaluator | A simple Func<> evaluator suitable for simple, custom expression handling |
JsonPathNullEvaluator | An evaluator that does nothing |
You can create your own evaluator by deriving from IJsonPathScriptEvaluator
.
public class JsonPathFuncEvaluator : IJsonPathScriptEvaluator
{
private readonly JsonPathEvaluator _evaluator;
public JsonPathFuncEvaluator( JsonPathEvaluator evaluator )
{
_evaluator = evaluator;
}
public object Evaluator( string script, JsonElement current, string context )
{
return _evaluator?.Invoke( script, current, context );
}
}
You can set a global default for the evaluator.
JsonPath.DefaultEvaluator = new JsonPathCSharpEvaluator();
Or you can wire it up through dependency injection.
public static IServiceCollection AddJsonPath( this IServiceCollection services, IConfiguration config )
{
services.AddTransient<IJsonPathScriptEvaluator,JsonPathCSharpEvaluator>();
services.AddTransient<JsonPath>();
return services;
}
Code examples
A couple of trivial code examples. Review the tests for detailed examples.
Example 1 Select the last element of an array.
const string json = @"
[
""first"",
""second"",
""third""
]";
var document = JsonDocument.Parse( json );
var match = document.SelectPath( "$[-1:]" ).Single();
Assert.IsTrue( match.Value.GetString() == "third" );
Example 2 Select all elemets that have a key
property with a value less than 42.
This example leverages bracket expressions using the Roslyn
jsonpath script evaluator.
const string json = @"
[
{ ""key"": 0},
{ ""key"": 42},
{ ""key"": -1},
{ ""key"": 41},
{ ""key"": 43},
{ ""key"": 42.0001},
{ ""key"": 41.9999},
{ ""key"": 100},
{ ""some"": ""value""}
]";
var document = JsonDocument.Parse( json );
var matches = document.SelectPath( "$[?(@.key<42)]", JsonPathCSharpEvaluator.Evaluator );
// outputs 0 -1 41 41.9999
foreach( var element in matches )
{
Console.WriteLine( document.RootElement.GetDouble() );
};
Dynamic Object Serialization
Basic support is provided for serializing to and from dynamic objects through the use of a custom JsonConverter
.
The DynamicJsonConverter
converter class is useful for simple scenareos. It is intended as a simple helper for basic use cases only.
DynamicJsonConverter
var serializerOptions = new JsonSerializerOptions
{
Converters = {new DynamicJsonConverter()}
};
// jsonInput is a string containing the bookstore json from the previous examples
var jobject = JsonSerializer.Deserialize<dynamic>( jsonInput, serializerOptions);
Assert.IsTrue( jobject.store.bicycle.color == "red" );
var jsonOutput = JsonSerializer.Serialize<dynamic>( jobject, serializerOptions ) as string;
Assert.IsTrue( jsonInput == jsonOutput );
Enum handling
When deserializing, the converter will treat enumerations as strings. You can override this behavior by setting
the TryReadValueHandler
on the converter. This handler will allow you to intercept and convert string and
numeric values during the deserialization process.
Acknowlegements
This project builds on the work of:
Stefan Gössner - Original JSONPath specification dated 2007-02-21
Atif Aziz - .NET JSONPath
Christoph Burgmer - Parser Consensus tests
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. |
-
net8.0
- Microsoft.CodeAnalysis.CSharp.Scripting (>= 4.9.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
3.0.2 | 36 | 2/14/2025 |
3.0.1 | 109 | 12/4/2024 |
3.0.1-develop.250214205653 | 32 | 2/14/2025 |
3.0.0 | 91 | 11/20/2024 |
3.0.0-develop.241120190203 | 48 | 11/20/2024 |
2.0.1 | 128 | 8/2/2024 |
2.0.0 | 102 | 8/1/2024 |
2.0.0-develop.240801174802 | 53 | 8/1/2024 |
1.4.0 | 141 | 7/22/2024 |
1.4.0-develop.240722203604 | 69 | 7/22/2024 |
1.3.0 | 116 | 7/12/2024 |
1.3.0-develop.240712213246 | 56 | 7/12/2024 |
1.2.1 | 127 | 7/2/2024 |
1.2.1-develop.240702141343 | 59 | 7/2/2024 |
1.2.1-develop.240625212527 | 62 | 6/25/2024 |
1.2.1-develop.240621185513 | 59 | 6/21/2024 |
1.2.1-develop.240620151810 | 67 | 6/20/2024 |
1.2.0-develop.240619213520 | 70 | 6/19/2024 |
1.1.0 | 134 | 6/14/2024 |
1.1.0-develop.240614155707 | 57 | 6/14/2024 |
1.0.0 | 134 | 4/8/2024 |