From eeeb9e1ad12c228ed50a2c948d299fdd6a3bf80b Mon Sep 17 00:00:00 2001 From: Brenton Farmer Date: Tue, 11 Jun 2024 17:21:55 -0700 Subject: [PATCH 1/5] Slight optimization to JsonPathToken Singular --- src/Hyperbee.Json/Tokenizer/JsonPathToken.cs | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs b/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs index 1b65c31e..5aca498b 100644 --- a/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs +++ b/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs @@ -1,6 +1,5 @@  using System.Diagnostics; -using System.Runtime.CompilerServices; namespace Hyperbee.Json.Tokenizer; @@ -19,22 +18,20 @@ internal record JsonPathToken public string FirstSelector => Selectors[0].Value; - public bool Singular + public bool Singular { get; } + + private bool IsSingular() { - [MethodImpl( MethodImplOptions.AggressiveInlining )] - get - { - if ( Selectors.Length != 1 ) - return false; + if ( Selectors.Length != 1 ) + return false; - var selectorKind = Selectors[0].SelectorKind; + var selectorKind = Selectors[0].SelectorKind; - return selectorKind == SelectorKind.UnspecifiedSingular || // prioritize runtime value - selectorKind == SelectorKind.Dot || - selectorKind == SelectorKind.Index || - selectorKind == SelectorKind.Name || - selectorKind == SelectorKind.Root; - } + return selectorKind == SelectorKind.UnspecifiedSingular || // prioritize runtime value + selectorKind == SelectorKind.Dot || + selectorKind == SelectorKind.Index || + selectorKind == SelectorKind.Name || + selectorKind == SelectorKind.Root; } public JsonPathToken( string selector, SelectorKind kind ) @@ -43,11 +40,14 @@ public JsonPathToken( string selector, SelectorKind kind ) [ new SelectorDescriptor { SelectorKind = kind, Value = selector } ]; + + Singular = IsSingular(); } public JsonPathToken( SelectorDescriptor[] selectors ) { Selectors = selectors; + Singular = IsSingular(); } public void Deconstruct( out bool singular, out SelectorDescriptor[] selectors ) From 21692d6ef9b56565dba33aa796d60e7812a9f527 Mon Sep 17 00:00:00 2001 From: Brenton Farmer Date: Tue, 11 Jun 2024 17:22:12 -0700 Subject: [PATCH 2/5] Updated readme --- README.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index fc81a9ee..7bd683ba 100644 --- a/README.md +++ b/README.md @@ -4,36 +4,34 @@ A C# implementation of JSONPath for .NET `System.Text.Json` and `System.Text.Jso ## Why -.NET `System.Text.Json` lacks support for JSONPath. The primary goal of this project is to provide a JSONPath library for .NET that will +.NET `System.Text.Json` lacks support for JSONPath. The primary goal of this project is to provide a JSONPath library for .NET that -* Directly leverage `System.Text.Json` and `System.Text.Json.Nodes` -* Align with the draft JSONPath Specification RFC 9535 +* Works natively with both `JsonDocument` (`JsonElement`) and `JsonNode` +* Can be extended to support other JSON models +* Aligns with the draft JSONPath Specification RFC 9535 * [Working Draft](https://github.com/ietf-wg-jsonpath/draft-ietf-jsonpath-base). * [Editor Copy](https://ietf-wg-jsonpath.github.io/draft-ietf-jsonpath-base/draft-ietf-jsonpath-base.html) -* Function according to the emerging consensus of use based on the majority of existing implementations; except through concious exception or deference to the RFC. +* Functions according to the emerging consensus of use based on the majority of existing implementations; except through concious exception or deference to the RFC. * [Parser Comparison Results](https://cburgmer.github.io/json-path-comparison) * [Parser Comparison GitHub](https://github.com/cburgmer/json-path-comparison/tree/master) ## 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 +expressions 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: +JSONPath expressions can use dot-notation: $.store.book[0].title -or the bracket-notation: +or 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 +borrows the descendant operator `..` from [E4X][e4x], and 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: @@ -166,6 +164,9 @@ foreach( var element in matches ) ``` ## Helper Classes +In addition to JSONPath processing, a few additional helper classes are provided to support dynamic property access, +property diving, and element comparisons. + ### Dynamic Object Serialization Basic support is provided for serializing to and from dynamic objects through the use of a custom `JsonConverter`. @@ -201,19 +202,19 @@ numeric values during the deserialization process. |:-----------------------------------|:----------- | `JsonElement.DeepEquals` | Performs a deep equals comparison | `JsonElementEqualityDeepComparer` | A deep equals equality comparer -| `JsonElementPositionComparer` | A position comparer that compares position in the backing stream +| `JsonElementPositionComparer` | A position comparer that compares the element's position in the backing stream ### Property Diving | Method | Description |:-----------------------------------|:----------- -| `JsonElement.GetPropertyFromKey` | Dives for properties using absolute keys like `$['store']['book'][2]['author']` +| `JsonElement.GetPropertyFromKey` | Dives for properties using absolute bracket notation keys like `$['store']['book'][2]['author']` ### JsonElement Helpers | Method | Description |:-----------------------------------|:----------- -| `JsonPathBuilder` | Returns the absolute JsonPath string for a given element +| `JsonPathBuilder` | Returns the JsonPath location string for a given element ## Acknowlegements From e6f3dda94ff4425c4013d7e83e81f14b172230ec Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 12 Jun 2024 00:22:55 +0000 Subject: [PATCH 3/5] Updated code formatting to match rules in .editorconfig --- src/Hyperbee.Json/Tokenizer/JsonPathToken.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs b/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs index 5aca498b..0634dba3 100644 --- a/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs +++ b/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs @@ -19,7 +19,7 @@ internal record JsonPathToken public string FirstSelector => Selectors[0].Value; public bool Singular { get; } - + private bool IsSingular() { if ( Selectors.Length != 1 ) From aadfe5722fa3f454dc49a0f6d2619b4972862323 Mon Sep 17 00:00:00 2001 From: Brenton Farmer Date: Tue, 11 Jun 2024 17:23:48 -0700 Subject: [PATCH 4/5] Moved position of IsSingular in class --- src/Hyperbee.Json/Tokenizer/JsonPathToken.cs | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs b/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs index 5aca498b..7e23dd8d 100644 --- a/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs +++ b/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs @@ -20,20 +20,6 @@ internal record JsonPathToken public bool Singular { get; } - private bool IsSingular() - { - if ( Selectors.Length != 1 ) - return false; - - var selectorKind = Selectors[0].SelectorKind; - - return selectorKind == SelectorKind.UnspecifiedSingular || // prioritize runtime value - selectorKind == SelectorKind.Dot || - selectorKind == SelectorKind.Index || - selectorKind == SelectorKind.Name || - selectorKind == SelectorKind.Root; - } - public JsonPathToken( string selector, SelectorKind kind ) { Selectors = @@ -50,6 +36,20 @@ public JsonPathToken( SelectorDescriptor[] selectors ) Singular = IsSingular(); } + private bool IsSingular() + { + if ( Selectors.Length != 1 ) + return false; + + var selectorKind = Selectors[0].SelectorKind; + + return selectorKind == SelectorKind.UnspecifiedSingular || // prioritize runtime value + selectorKind == SelectorKind.Dot || + selectorKind == SelectorKind.Index || + selectorKind == SelectorKind.Name || + selectorKind == SelectorKind.Root; + } + public void Deconstruct( out bool singular, out SelectorDescriptor[] selectors ) { singular = Singular; From 78188335537e93a2f76745f24a47a016df1fec2f Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 12 Jun 2024 00:25:25 +0000 Subject: [PATCH 5/5] Updated code formatting to match rules in .editorconfig --- src/Hyperbee.Json/Tokenizer/JsonPathToken.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs b/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs index aefa3ba4..38d73e85 100644 --- a/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs +++ b/src/Hyperbee.Json/Tokenizer/JsonPathToken.cs @@ -1,4 +1,4 @@ - + using System.Diagnostics; namespace Hyperbee.Json.Tokenizer; @@ -19,7 +19,7 @@ internal record JsonPathToken public string FirstSelector => Selectors[0].Value; public bool Singular { get; } - + public JsonPathToken( string selector, SelectorKind kind ) { Selectors =