-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FEATURE]: Add JsonPatch and JsonDiff (#59)
* Added JsonPatch and JsonDiff * Added JsonPointer and JsonPathPointerConverter * Improved ValueStringBuilder performance improvement * Improve IRegex rewrite code * Improved Test coverage * Added support for initial documentation in GitHub Pages * Updated Namespaces for Path, Pointer, Patch --------- Co-authored-by: Matt Edwards <[email protected]> Co-authored-by: Brenton Farmer <[email protected]>
- Loading branch information
1 parent
97b5ba1
commit 8e5529d
Showing
134 changed files
with
5,658 additions
and
1,311 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,280 +1,62 @@ | ||
# Welcome to Hyperbee.Json | ||
|
||
# Hyperbee.Json | ||
Hyperbee.Json is a high-performance JSON library for .NET, providing robust support for JSONPath, JsonPointer, JsonPatch, and JsonDiff. | ||
This library is optimized for speed and low memory allocations, and it adheres to relevant RFCs to ensure reliable and predictable behavior. | ||
|
||
`Hyperbee.Json` is a high-performance JSONPath parser for .NET, that supports both `JsonElement` and `JsonNode`. | ||
The library is designed to be quick and extensible, allowing support for other JSON document types and functions. | ||
Unlike other libraries that support only `JsonElement` or `JsonNode`, Hyperbee.Json supports **both** types, and can be easily extended to | ||
support additional document types and functions. | ||
|
||
## Features | ||
|
||
- **High Performance:** Optimized for performance and efficiency. | ||
- **Supports:** `JsonElement` and `JsonNode`. | ||
- **Extensible:** Easily extended to support additional JSON document types and functions. | ||
- **`IEnumerable` Results:** Deferred execution queries with `IEnumerable`. | ||
- **Conformant:** Adheres to the JSONPath Specification [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html). | ||
- **Low Memory Allocations:** Designed to minimize memory usage. | ||
- **Comprehensive JSON Support:** Supports JSONPath, JsonPointer, JsonPatch, and JsonDiff. | ||
- **Conformance:** Adheres to the JSONPath Specification [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html), JSONPointer [RFC 6901](https://www.rfc-editor.org/rfc/rfc6901.html), and JsonPatch [RFC 6902](https://www.rfc-editor.org/rfc/rfc6902.html). | ||
- **Supports both `JsonElement` and `JsonNode`:** Works seamlessly with both JSON document types. | ||
|
||
## JSONPath RFC | ||
## JSONPath | ||
|
||
Hyperbee.Json conforms to the RFC, and aims to support the [JSONPath consensus](https://cburgmer.github.io/json-path-comparison) | ||
when the RFC is unopinionated. When the RFC is unopinionated, and where the consensus is ambiguous or not aligned with our | ||
performance and usability goals, we may deviate. Our goal is always to provide a robust and performant library while | ||
strengthening our alignment with the RFC and the community. | ||
JSONPath is a query language for JSON, allowing you to navigate and extract data from JSON documents using a set of path expressions. | ||
Hyperbee.Json's JSONPath implementation is designed for optimal performance, ensuring low memory allocations and fast query execution. | ||
It fully conforms to [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html). | ||
|
||
## Installation | ||
[Read more about JsonPath](jsonpath/index.md) | ||
[Read more about JsonPath syntax](jsonpath/jsonpath-syntax.md) | ||
|
||
Install via NuGet: | ||
|
||
```bash | ||
dotnet add package Hyperbee.Json | ||
``` | ||
|
||
## Usage | ||
## JSONPointer | ||
|
||
### Basic Examples | ||
|
||
#### Selecting Elements | ||
|
||
```csharp | ||
|
||
var json = """ | ||
{ | ||
"store": { | ||
"book": [ | ||
{ "category": "fiction" }, | ||
{ "category": "science" } | ||
] | ||
} | ||
} | ||
"""; | ||
JSONPointer is a syntax for identifying a specific value within a JSON document. It is simple and easy to use, making it an excellent | ||
choice for pinpointing exact values. Hyperbee.Json's JsonPointer implementation adheres to [RFC 6901](https://www.rfc-editor.org/rfc/rfc6901.html). | ||
|
||
var root = JsonDocument.Parse(json); | ||
var result = JsonPath.Select(root, "$.store.book[*].category"); | ||
|
||
foreach (var item in result) | ||
{ | ||
Console.WriteLine(item); // Output: "fiction" and "science" | ||
} | ||
``` | ||
|
||
#### Filtering | ||
|
||
```csharp | ||
|
||
var json = """ | ||
{ | ||
"store": { | ||
"book": [ | ||
{ | ||
"category": "fiction", | ||
"price": 10 | ||
}, | ||
{ | ||
"category": "science", | ||
"price": 15 | ||
} | ||
] | ||
} | ||
} | ||
"""; | ||
[Documentation for JsonPointer coming soon] | ||
|
||
var root = JsonDocument.Parse(json); | ||
var result = JsonPath.Select(root, "$.store.book[?(@.price > 10)]"); | ||
|
||
foreach (var item in result) | ||
{ | ||
Console.WriteLine(item); // Output: { "category": "science", "price": 15 } | ||
} | ||
``` | ||
|
||
#### Working with (JsonElement, Path) pairs | ||
```csharp | ||
|
||
var json = """ | ||
{ | ||
"store": { | ||
"book": [ | ||
{ "category": "fiction" }, | ||
{ "category": "science" } | ||
] | ||
} | ||
} | ||
"""; | ||
|
||
var root = JsonDocument.Parse(json); | ||
var result = JsonPath.SelectPath(root, "$.store.book[0].category"); | ||
|
||
var (node, path) = result.First(); | ||
|
||
Console.WriteLine(node); // Output: "fiction" | ||
Console.WriteLine(path); // Output: "$.store.book[0].category | ||
``` | ||
|
||
#### Working with JsonNode | ||
|
||
```csharp | ||
|
||
var json = """ | ||
{ | ||
"store": { | ||
"book": [ | ||
{ "category": "fiction" }, | ||
{ "category": "science" } | ||
] | ||
} | ||
} | ||
"""; | ||
|
||
var root = JsonNode.Parse(json); | ||
var result = JsonPath.Select(root, "$.store.book[0].category"); | ||
|
||
Console.WriteLine(result.First()); // Output: "fiction" | ||
``` | ||
## JSONPatch | ||
|
||
## JSONPath Syntax Overview | ||
JSONPatch is a format for describing changes to a JSON document. It allows you to apply partial modifications to JSON data efficiently. | ||
Hyperbee.Json supports JsonPatch as defined in [RFC 6902](https://www.rfc-editor.org/rfc/rfc6902.html), ensuring compatibility and reliability. | ||
|
||
Here's a quick overview of JSONPath syntax: | ||
[Read more about JsonPatch](jsonpatch.md) | ||
|
||
| JSONPath | Description | ||
|:---------------------------------------------|:----------------------------------------------------------- | ||
| `$` | Root node | ||
| `@` | Current node | ||
| `.<name>`, `.'<name>'`, or `."<name>"` | Object member dot operator | ||
| `[<name>]`, or `['<name>']`, or `["<name>"]` | Object member subscript operator | ||
| `[<index]` | Array access operator | ||
| `[,]` | Union operator | ||
| `[start:end:step]` | Array slice operator | ||
| `*`, or `[*]` | Wildcard | ||
| `..` | Recursive descent | ||
| `?<expr>` | Filter selector | ||
## JSONDiff | ||
|
||
JSONPath expressions refer to a JSON structure, and JSONPath assumes the name `$` is assigned | ||
to the root JSON object. | ||
JSONDiff allows you to compute the difference between two JSON documents, which is useful for versioning and synchronization. | ||
Hyperbee.Json's implementation is optimized for performance and low memory usage, adhering to the standards set in [RFC 6902](https://www.rfc-editor.org/rfc/rfc6902.html). | ||
|
||
JSONPath expressions can use dot-notation: | ||
[Read more about JsonDiff](jsonpatch.md) | ||
|
||
$.store.book[0].title | ||
## Getting Started | ||
|
||
or bracket-notation: | ||
To get started with Hyperbee.Json, refer to the documentation for detailed instructions and examples. Install the library via NuGet: | ||
|
||
$['store']['book'][0]['title'] | ||
|
||
- JSONPath allows the wildcard symbol `*` for member names and array indices. | ||
- It borrows the descendant operator `..` from [E4X][e4x] | ||
- It uses the `@` symbol to refer to the current object. | ||
- It uses `?` syntax for filtering. | ||
- It uses the array slice syntax proposal `[start:end:step]` from ECMASCRIPT 4. | ||
|
||
Expressions can be used as an alternative to explicit names or indices, as in: | ||
|
||
$.store.book[(length(@)-1)].title | ||
|
||
Filter expressions are supported via the syntax `?(<boolean expr>)`, as in: | ||
|
||
$.store.book[?(@.price < 10)].title | ||
|
||
### JSONPath Functions | ||
|
||
JsonPath expressions support basic method calls. | ||
|
||
| Method | Description | Example | ||
|------------|--------------------------------------------------------|------------------------------------------------ | ||
| `length()` | Returns the length of an array or string. | `$.store.book[?(length(@.title) > 5)]` | ||
| `count()` | Returns the count of matching elements. | `$.store.book[?(count(@.authors) > 1)]` | ||
| `match()` | Returns true if a string matches a regular expression. | `$.store.book[?(match(@.title,'.*Century.*'))]` | ||
| `search()` | Searches for a string within another string. | `$.store.book[?(search(@.title,'Sword'))]` | ||
| `value()` | Accesses the value of a key in the current object. | `$.store.book[?(value(@.price) < 10)]` | ||
|
||
### JSONPath Extended Syntax | ||
|
||
The library extends the JSONPath expression syntax to support additional features. | ||
|
||
| Operators | Description | Example | ||
|---------------------|-----------------------------------------------|------------------------------------------------ | ||
| `+` `-` `*` `\` `%` | Basic math operators. | `$[?(@.a + @.b == 3)]` | ||
| `in` | Tests is a value is in a set. | `$[[email protected] in ['a', 'b', 'c'] ]` | ||
|
||
|
||
### JSONPath Custom Functions | ||
|
||
You can extend the supported function set by registering your own functions. | ||
|
||
**Example:** Implement a `JsonNode` Path Function: | ||
|
||
**Example:** Implement a `JsonNode` Path Function: | ||
|
||
**Step 1:** Create a custom function that returns the path of a `JsonNode`. | ||
|
||
```csharp | ||
public class PathNodeFunction() : ExtensionFunction( PathMethod, CompareConstraint.MustCompare ) | ||
{ | ||
public const string Name = "path"; | ||
private static readonly MethodInfo PathMethod = GetMethod<PathNodeFunction>( nameof( Path ) ); | ||
|
||
private static ScalarValue<string> Path( IValueType argument ) | ||
{ | ||
return argument.TryGetNode<JsonNode>( out var node ) ? node?.GetPath() : null; | ||
} | ||
} | ||
``` | ||
|
||
**Step 2:** Register your custom function. | ||
|
||
```csharp | ||
JsonTypeDescriptorRegistry.GetDescriptor<JsonNode>().Functions | ||
.Register( PathNodeFunction.Name, () => new PathNodeFunction() ); | ||
``` | ||
|
||
**Step 3:** Use your custom function in a JSONPath query. | ||
Install via NuGet: | ||
|
||
```csharp | ||
var results = source.Select( "$..[?path(@) == '$.store.book[2].title']" ); | ||
```bash | ||
dotnet add package Hyperbee.Json | ||
``` | ||
|
||
## Why Choose [Hyperbee.Json](https://github.com/Stillpoint-Software/Hyperbee.Json) ? | ||
## Documentation | ||
|
||
- High Performance. | ||
- Supports both `JsonElement`, and `JsonNode`. | ||
- Deferred execution queries with `IEnumerable`. | ||
- Enhanced JsonPath syntax. | ||
- Extendable to support additional JSON document types. | ||
- RFC conforming JSONPath implementation. | ||
|
||
## Comparison with Other Libraries | ||
|
||
There are excellent libraries available for RFC-9535 .NET JsonPath. | ||
|
||
### [JsonPath.Net](https://docs.json-everything.net/path/basics/) Json-Everything | ||
|
||
- **Pros:** | ||
- Comprehensive feature set. | ||
- Deferred execution queries with `IEnumerable`. | ||
- Enhanced JsonPath syntax. | ||
- Strong community support. | ||
|
||
- **Cons:** | ||
- No support for `JsonElement`. | ||
- More memory intensive. | ||
- Not quite as fast as other `System.Text.Json` implementations. | ||
|
||
### [JsonCons.NET](https://danielaparker.github.io/JsonCons.Net/articles/JsonPath/JsonConsJsonPath.html) | ||
|
||
- **Pros:** | ||
- High performance. | ||
- Enhanced JsonPath syntax. | ||
|
||
- **Cons:** | ||
- No support for `JsonNode`. | ||
- Does not return an `IEnumerable` result (no defered query execution). | ||
|
||
### [Json.NET](https://www.newtonsoft.com/json) Newtonsoft | ||
|
||
- **Pros:** | ||
- Comprehensive feature set. | ||
- Deferred execution queries with `IEnumerable`. | ||
- Documentation and examples. | ||
- Strong community support. | ||
|
||
- **Cons:** | ||
- No support for `JsonElement`, or `JsonNode`. | ||
Documentation can be found in the project's `/docs` folder. | ||
|
||
## Benchmarks | ||
|
||
|
@@ -356,10 +138,6 @@ Here is a performance comparison of various queries on the standard book store d | |
| JsonEverything_JsonNode | 4.612 μs | 2.0037 μs | 0.1098 μs | 5.96 KB | ||
| Newtonsoft_JObject | 9.627 μs | 1.1498 μs | 0.0630 μs | 14.56 KB | ||
|
||
## Additional Documentation | ||
|
||
Additional documentation can be found in the project's `/docs` folder. | ||
|
||
## Credits | ||
|
||
Hyperbee.Json is built upon the great work of several open-source projects. Special thanks to: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
--- | ||
layout: default | ||
title: Additional Classes | ||
nav_order: 3 | ||
nav_order: 99 | ||
--- | ||
|
||
## Additional Classes | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' < '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> | ||
<HasSharedItems>true</HasSharedItems> | ||
<SharedGUID>fc3b0a95-4da0-43a0-a19d-624152bdf2f6</SharedGUID> | ||
</PropertyGroup> | ||
<PropertyGroup Label="Configuration"> | ||
<Import_RootNamespace>docs</Import_RootNamespace> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<None Include="$(MSBuildThisFileDirectory)additional-classes.md" /> | ||
<None Include="$(MSBuildThisFileDirectory)index.md" /> | ||
<None Include="$(MSBuildThisFileDirectory)jsonpatch.md" /> | ||
<None Include="$(MSBuildThisFileDirectory)jsonpath\functions.md" /> | ||
<None Include="$(MSBuildThisFileDirectory)jsonpath\index.md" /> | ||
<None Include="$(MSBuildThisFileDirectory)jsonpath\syntax.md" /> | ||
<None Include="$(MSBuildThisFileDirectory)jsonpointer.md" /> | ||
</ItemGroup> | ||
</Project> |
Oops, something went wrong.