From d383924124e1c5519ad0a1744280a864f1fcb3b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 15:15:14 -0700 Subject: [PATCH 01/18] [FEATURE]: Improve parser error handling (#32) * Improve query parser error handling of quoted and unquoted select names * Correctly throw when there are unexpected characters at the end of a query * Refactored JsonHelper and extensions --------- Co-authored-by: Brenton Farmer --- README.md | 87 +++----- docs/ADDITIONAL-CLASSES.md | 21 +- .../Node/Functions/ValueNodeFunction.cs | 2 +- .../Dynamic/DynamicJsonElement.cs | 33 ++- .../Extensions/JsonElementExtensions.cs | 75 +------ src/Hyperbee.Json/Extensions/JsonHelper.cs | 87 ++++++++ .../Extensions/JsonNodeExtensions.cs | 24 +-- .../Extensions/JsonPathHelper.cs | 53 ----- .../Extensions/JsonPathPointerExtensions.cs | 5 +- src/Hyperbee.Json/JsonPathQueryParser.cs | 204 ++++++++++++------ .../Dynamic/JsonDynamicTests.cs | 2 +- .../Extensions/JsonExtensionTests.cs | 2 +- .../Parsers/JsonPathQueryParserTests.cs | 17 +- 13 files changed, 320 insertions(+), 292 deletions(-) create mode 100644 src/Hyperbee.Json/Extensions/JsonHelper.cs delete mode 100644 src/Hyperbee.Json/Extensions/JsonPathHelper.cs diff --git a/README.md b/README.md index 789f2a24..18b7600e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + # Hyperbee.Json `Hyperbee.Json` is a high-performance JSONPath parser for .NET, that supports both `JsonElement` and `JsonNode`. @@ -14,8 +14,8 @@ The library is designed to be quick and extensible, allowing support for other J ## JSONPath Consensus -Hyperbee.Json aims to follow the RFC and 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 +Hyperbee.Json aims to follow the RFC, and 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. @@ -31,34 +31,9 @@ dotnet add package Hyperbee.Json ### Basic Examples -#### Selecting a Single Element - -```csharp -using Hyperbee.JsonPath; -using System.Text.Json; - -var json = """ -{ - "store": { - "book": [ - { "category": "fiction" }, - { "category": "science" } - ] - } -} -"""; - -var root = JsonDocument.Parse(json); -var result = JsonPath.Select(root, "$.store.book[0].category"); - -Console.WriteLine(result.First()); // Output: "fiction" -``` - -#### Selecting Multiple Elements +#### Selecting Elements ```csharp -using Hyperbee.JsonPath; -using System.Text.Json; var json = """ { @@ -83,8 +58,6 @@ foreach (var item in result) #### Filtering ```csharp -using Hyperbee.JsonPath; -using System.Text.Json; var json = """ { @@ -114,8 +87,6 @@ foreach (var item in result) #### Working with (JsonElement, Path) pairs ```csharp -using Hyperbee.JsonPath; -using System.Text.Json; var json = """ { @@ -140,8 +111,6 @@ Console.WriteLine(path); // Output: "$.store.book[0].category #### Working with JsonNode ```csharp -using Hyperbee.JsonPath; -using System.Text.Json.Nodes; var json = """ { @@ -160,9 +129,9 @@ var result = JsonPath.Select(root, "$.store.book[0].category"); Console.WriteLine(result.First()); // Output: "fiction" ``` -## JSONPath Syntax Reference +## JSONPath Syntax Overview -Here's a quick reference for JSONPath syntax: +Here's a quick overview of JSONPath syntax: | JSONPath | Description |:---------------------------------------------|:----------------------------------------------------------- @@ -177,9 +146,8 @@ Here's a quick reference for JSONPath syntax: | `..` | Recursive descent | `?` | Filter selector -JSONPath expressions refer to a JSON structure in the same way as XPath expressions -are used in combination with an XML document. JSONPath assumes the name `$` is assigned -to the root level object. +JSONPath expressions refer to a JSON structure, and JSONPath assumes the name `$` is assigned +to the root JSON object. JSONPath expressions can use dot-notation: @@ -189,35 +157,38 @@ or bracket-notation: $['store']['book'][0]['title'] -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. +- 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 the `?()` 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 + $.store.book[(length(@)-1)].title -using the symbol `@` for the current object. Filter expressions are supported via -the syntax `?()`, as in: +Filter expressions are supported via the syntax `?()`, as in: $.store.book[?(@.price < 10)].title -### JSONPath Methods +### JSONPath Functions JsonPath expressions support basic methods 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'))]` +| `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)]` -You can extend the supported function set by registering your own functions. +### JSONPath Custom Functions -#### Example: `JsonNode` Path Function +You can also extend the supported function set by registering your own functions. + +**Example:** Implement a `JsonNode` Path Function: **Step 1:** Create a custom function that returns the path of a `JsonNode`. @@ -264,7 +235,8 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. - Comprehensive feature set. - Deferred execution queries with `IEnumerable`. - Strong community support. - + - .NET Foundation Project. + - **Cons:** - No support for `JsonElement`. - Not quite as fast as other `System.Text.Json` implementations. @@ -285,7 +257,7 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. - Comprehensive feature set. - Documentation and examples. - Strong community support. - - Level 2 .NET Foundation Project. + - .NET Foundation Project. - **Cons:** - No support for `JsonElement`, or `JsonNode`. @@ -296,9 +268,9 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. - Supports both `JsonElement`, and `JsonNode`. - Deferred execution queries with `IEnumerable`. - Extendable to support additional JSON document types and functions. -- Consensus focused JSONPath implementation. +- RFC and Consensus focused JSONPath implementation. -- ## Benchmarks +## Benchmarks Here is a performance comparison of various queries on the standard book store document. @@ -374,8 +346,7 @@ Here is a performance comparison of various queries on the standard book store d | JsonEverything_JsonNode | $.store.book[0] | 4.779 us | 2.9031 us | 0.1591 us | 5.96 KB | Newtonsoft_JObject | $.store.book[0] | 8.714 us | 2.5518 us | 0.1399 us | 14.56 KB ``` -``` -``` + ## Additional Documentation diff --git a/docs/ADDITIONAL-CLASSES.md b/docs/ADDITIONAL-CLASSES.md index 73d99814..77f3e1d9 100644 --- a/docs/ADDITIONAL-CLASSES.md +++ b/docs/ADDITIONAL-CLASSES.md @@ -5,28 +5,18 @@ property diving, element comparisons, and dynamic property access. ### Property Diving -Property diving acts similarly to JSON Pointer; it expects a path that returns a single element. -Unlike JSON Pointer, property diving notation expects a singular JSON Path. +Property diving acts **similarly** to JSON Pointer; it expects an absolute path that returns a single element. +Unlike JSON Pointer, property diving notation expects normalized JSON Path notation. | Method | Description |:-----------------------------------|:----------- | `JsonElement.FromJsonPathPointer` | Dives for properties using absolute locations like `$.store.book[2].author` -The syntax supports singular paths; dotted notation, quoted names, and simple bracketed array accessors only. +The syntax supports absolute (normalized) paths; dotted notation, quoted names, and simple bracketed array accessors only. The intention is to return a single element by literal path. -Json path style '$', wildcard '*', '..', and '[a,b]' multi-result selector notations and filters are **not** supported. +Json path style wildcard '*', '..', and '[a,b]' multi-result selector notations and filters are **not** supported. -``` -Examples of valid path syntax: - - prop1.prop2 - prop1[0] - 'prop.2' - prop1[0].prop2 - prop1['prop.2'] - prop1.'prop.2'[0].prop3 -``` ### JsonElement Path @@ -48,7 +38,8 @@ for a given `JsonElement`. 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. +basic use cases only. A helper methods `JsonPathHelper.ConvertToDynamic` is provided to simplify the process of +serializing and deserializing dynamic objects. #### DynamicJsonConverter diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs index f31f8fe4..94dde195 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using System.Numerics; using System.Text.Json; using System.Text.Json.Nodes; using Hyperbee.Json.Extensions; @@ -43,5 +44,4 @@ static bool IsNotEmpty( JsonNode node ) }; } } - } diff --git a/src/Hyperbee.Json/Dynamic/DynamicJsonElement.cs b/src/Hyperbee.Json/Dynamic/DynamicJsonElement.cs index 5bfad4e7..7d504c9f 100644 --- a/src/Hyperbee.Json/Dynamic/DynamicJsonElement.cs +++ b/src/Hyperbee.Json/Dynamic/DynamicJsonElement.cs @@ -1,6 +1,5 @@ using System.Dynamic; using System.Text.Json; -using Hyperbee.Json.Extensions; namespace Hyperbee.Json.Dynamic; @@ -11,9 +10,9 @@ public class DynamicJsonElement : DynamicObject public static implicit operator double( DynamicJsonElement proxy ) => proxy.Value.GetDouble(); public static implicit operator decimal( DynamicJsonElement proxy ) => proxy.Value.GetDecimal(); - public static implicit operator short( DynamicJsonElement proxy ) => proxy.Value.GetNumberAsInt16(); - public static implicit operator int( DynamicJsonElement proxy ) => proxy.Value.GetNumberAsInt32(); - public static implicit operator long( DynamicJsonElement proxy ) => proxy.Value.GetNumberAsInt64(); + public static implicit operator short( DynamicJsonElement proxy ) => GetNumberAsInt16( proxy.Value ); + public static implicit operator int( DynamicJsonElement proxy ) => GetNumberAsInt32( proxy.Value ); + public static implicit operator long( DynamicJsonElement proxy ) => GetNumberAsInt64( proxy.Value ); public static implicit operator bool( DynamicJsonElement proxy ) => proxy.Value.GetBoolean(); public static implicit operator byte( DynamicJsonElement proxy ) => proxy.Value.GetByte(); public static implicit operator sbyte( DynamicJsonElement proxy ) => proxy.Value.GetSByte(); @@ -78,4 +77,30 @@ public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, result = null; return false; } + + // Value extensions + + private static short GetNumberAsInt16( JsonElement value ) + { + if ( value.TryGetInt16( out var number ) ) + return number; + + return (short) value.GetDouble(); // for cases where the number contains fractional digits + } + + private static int GetNumberAsInt32( JsonElement value ) + { + if ( value.TryGetInt32( out var number ) ) + return number; + + return (int) value.GetDouble(); // for cases where the number contains fractional digits + } + + private static long GetNumberAsInt64( JsonElement value ) + { + if ( value.TryGetInt64( out var number ) ) + return number; + + return (long) value.GetDouble(); // for cases where the number contains fractional digits + } } diff --git a/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs b/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs index 536fc29d..b5cc3319 100644 --- a/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs +++ b/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs @@ -1,87 +1,14 @@ -using System.Buffers; -using System.Text.Json; -using System.Text.Json.Nodes; -using Hyperbee.Json.Dynamic; +using System.Text.Json; namespace Hyperbee.Json.Extensions; public static class JsonElementExtensions { - // To operations - - public static dynamic ToDynamic( this JsonElement value, string path = null ) => new DynamicJsonElement( ref value, path ); - public static dynamic ToDynamic( this JsonDocument value ) => ToDynamic( value.RootElement, "$" ); - - public static JsonNode ToJsonNode( this JsonDocument document ) - { - return ToJsonNode( document.RootElement ); - } - - public static JsonNode ToJsonNode( this JsonElement element ) - { - return element.ValueKind switch - { - JsonValueKind.Object => JsonObject.Create( element ), - JsonValueKind.Array => JsonArray.Create( element ), - _ => JsonValue.Create( element ) - }; - } - - public static T ToObject( this JsonElement value, JsonSerializerOptions options = null ) - where T : new() - { - var bufferWriter = new ArrayBufferWriter(); - using var writer = new Utf8JsonWriter( bufferWriter ); - - value.WriteTo( writer ); - writer.Flush(); - - var reader = new Utf8JsonReader( bufferWriter.WrittenSpan ); - return JsonSerializer.Deserialize( ref reader, options ); - } - // Deep Equals/Compare extensions - public static bool DeepEquals( this JsonElement elmA, string strB, JsonDocumentOptions options = default ) - { - if ( strB == null ) - return false; - - var comparer = new JsonElementDeepEqualityComparer( options.MaxDepth ); - using var docB = JsonDocument.Parse( strB, options ); - - return comparer.Equals( elmA, docB.RootElement ); - } - public static bool DeepEquals( this JsonElement elmA, JsonElement elmB, JsonDocumentOptions options = default ) { var comparer = new JsonElementDeepEqualityComparer( options.MaxDepth ); return comparer.Equals( elmA, elmB ); } - - // Value extensions - - public static short GetNumberAsInt16( this JsonElement value ) - { - if ( value.TryGetInt16( out var number ) ) - return number; - - return (short) value.GetDouble(); // for cases where the number contains fractional digits - } - - public static int GetNumberAsInt32( this JsonElement value ) - { - if ( value.TryGetInt32( out var number ) ) - return number; - - return (int) value.GetDouble(); // for cases where the number contains fractional digits - } - - public static long GetNumberAsInt64( this JsonElement value ) - { - if ( value.TryGetInt64( out var number ) ) - return number; - - return (long) value.GetDouble(); // for cases where the number contains fractional digits - } } diff --git a/src/Hyperbee.Json/Extensions/JsonHelper.cs b/src/Hyperbee.Json/Extensions/JsonHelper.cs new file mode 100644 index 00000000..a40ffa8a --- /dev/null +++ b/src/Hyperbee.Json/Extensions/JsonHelper.cs @@ -0,0 +1,87 @@ +using System.Buffers; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using Hyperbee.Json.Dynamic; + +namespace Hyperbee.Json.Extensions; + +public static class JsonHelper +{ + // conversion + + public static ReadOnlySpan ConvertToBracketNotation( ReadOnlySpan path ) + { + var segments = JsonPathQueryParser.ParseNoCache( path ); + + var builder = new StringBuilder(); + + foreach ( var token in segments.AsEnumerable() ) + { + builder.Append( '[' ); + + foreach ( var selector in token.Selectors ) + { + switch ( selector.SelectorKind ) + { + case SelectorKind.Root: + builder.Append( "'$'" ); + break; + case SelectorKind.DotName: + case SelectorKind.Name: + builder.Append( $"'{selector.Value}'" ); + break; + case SelectorKind.Wildcard: + builder.Append( '*' ); + break; + case SelectorKind.Descendant: + builder.Append( ".." ); + break; + case SelectorKind.Slice: + case SelectorKind.Filter: + case SelectorKind.Index: + builder.Append( selector.Value ); + break; + + case SelectorKind.Undefined: + default: + throw new NotSupportedException( $"Unsupported {nameof( SelectorKind )}." ); + } + } + + builder.Append( ']' ); + } + + return builder.ToString(); + } + + public static dynamic ConvertToDynamic( JsonNode value ) => new DynamicJsonNode( ref value ); + public static dynamic ConvertToDynamic( JsonElement value, string path = null ) => new DynamicJsonElement( ref value, path ); + public static dynamic ConvertToDynamic( JsonDocument value ) => ConvertToDynamic( value.RootElement, "$" ); + + public static T ConvertToObject( JsonElement value, JsonSerializerOptions options = null ) + where T : new() + { + var bufferWriter = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter( bufferWriter ); + + value.WriteTo( writer ); + writer.Flush(); + + var reader = new Utf8JsonReader( bufferWriter.WrittenSpan ); + return JsonSerializer.Deserialize( ref reader, options ); + } + + public static T ConvertToObject( JsonNode value, JsonSerializerOptions options = null ) + where T : new() + { + var bufferWriter = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter( bufferWriter ); + + value.WriteTo( writer ); + writer.Flush(); + + var reader = new Utf8JsonReader( bufferWriter.WrittenSpan ); + return JsonSerializer.Deserialize( ref reader, options ); + } +} diff --git a/src/Hyperbee.Json/Extensions/JsonNodeExtensions.cs b/src/Hyperbee.Json/Extensions/JsonNodeExtensions.cs index 141f3322..d77bf283 100644 --- a/src/Hyperbee.Json/Extensions/JsonNodeExtensions.cs +++ b/src/Hyperbee.Json/Extensions/JsonNodeExtensions.cs @@ -1,33 +1,13 @@ -using System.Buffers; -using System.Numerics; -using System.Text.Json; +using System.Numerics; using System.Text.Json.Nodes; -using Hyperbee.Json.Dynamic; namespace Hyperbee.Json.Extensions; public static class JsonNodeExtensions { - // To operations - - public static dynamic ToDynamic( this JsonNode value ) => new DynamicJsonNode( ref value ); - - public static T ToObject( this JsonNode value, JsonSerializerOptions options = null ) - where T : new() - { - var bufferWriter = new ArrayBufferWriter(); - using var writer = new Utf8JsonWriter( bufferWriter ); - - value.WriteTo( writer ); - writer.Flush(); - - var reader = new Utf8JsonReader( bufferWriter.WrittenSpan ); - return JsonSerializer.Deserialize( ref reader, options ); - } - // Value extensions - public static T GetNumber( this JsonNode value ) + internal static T GetNumber( this JsonNode value ) where T : struct, IComparable, IFormattable, IConvertible, IComparable, IEquatable, INumber { var source = value.AsValue(); diff --git a/src/Hyperbee.Json/Extensions/JsonPathHelper.cs b/src/Hyperbee.Json/Extensions/JsonPathHelper.cs deleted file mode 100644 index d896634e..00000000 --- a/src/Hyperbee.Json/Extensions/JsonPathHelper.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Text; - -namespace Hyperbee.Json.Extensions; - -public static class JsonPathHelper -{ - // conversion - - public static ReadOnlySpan NormalizePath( ReadOnlySpan path ) - { - var segments = JsonPathQueryParser.ParseNoCache( path ); - - var builder = new StringBuilder(); - - foreach ( var token in segments.AsEnumerable() ) - { - builder.Append( '[' ); - - foreach ( var selector in token.Selectors ) - { - switch ( selector.SelectorKind ) - { - case SelectorKind.Root: - builder.Append( "'$'" ); - break; - case SelectorKind.Dot: - case SelectorKind.Name: - builder.Append( $"'{selector.Value}'" ); - break; - case SelectorKind.Wildcard: - builder.Append( '*' ); - break; - case SelectorKind.Descendant: - builder.Append( ".." ); - break; - case SelectorKind.Slice: - case SelectorKind.Filter: - case SelectorKind.Index: - builder.Append( selector.Value ); - break; - - case SelectorKind.Undefined: - default: - throw new NotSupportedException( $"Unsupported {nameof( SelectorKind )}." ); - } - } - - builder.Append( ']' ); - } - - return builder.ToString(); - } -} diff --git a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs index 7e897173..596bb164 100644 --- a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs +++ b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs @@ -4,9 +4,10 @@ namespace Hyperbee.Json.Extensions; // DISTINCT from JsonPath these extensions are intended to facilitate 'diving' for Json Properties using -// absolute singular paths. similar to JsonPointer but using JsonPath notation. +// normalized paths. a normalized path is an absolute path that references a single element. +// similar to JsonPointer but using JsonPath notation. // -// syntax supports singular paths; dotted notation, quoted names, and simple bracketed array accessors only. +// syntax supports absolute paths; dotted notation, quoted names, and simple bracketed array accessors only. // // Json path style wildcard '*', '..', and '[a,b]' multi-result selector notations are NOT supported. // diff --git a/src/Hyperbee.Json/JsonPathQueryParser.cs b/src/Hyperbee.Json/JsonPathQueryParser.cs index 255cceac..c12fc9da 100644 --- a/src/Hyperbee.Json/JsonPathQueryParser.cs +++ b/src/Hyperbee.Json/JsonPathQueryParser.cs @@ -14,7 +14,7 @@ public enum SelectorKind // dot notation Root = 0x4 | Singular, - Dot = 0x8 | Singular, + DotName = 0x8 | Singular, // union notation Name = 0x10 | Singular, @@ -33,20 +33,22 @@ internal static class JsonPathQueryParser private enum State { + Undefined, Start, DotChild, UnionStart, - UnionElementQuoted, - UnionElementQuotedFinal, + UnionQuotedFinal, UnionElement, - UnionNextElement, + UnionNext, UnionFinal, + QuotedName, + FinalSelector, Final } private static string GetSelector( State state, ReadOnlySpan buffer, int start, int stop ) { - var adjust = state == State.Final ? 0 : 1; // non-final states have already advanced to the next character, so we need to subtract 1 + var adjust = state == State.FinalSelector || state == State.Final ? 0 : 1; // non-final states have already advanced to the next character, so we need to subtract 1 var length = stop - start - adjust; return length <= 0 ? null : buffer.Slice( start, length ).Trim().ToString(); } @@ -56,7 +58,7 @@ private static void InsertToken( ICollection tokens, SelectorDe if ( selector?.Value == null ) return; - InsertToken( tokens, [selector] ); + InsertToken( tokens, new[] { selector } ); } private static void InsertToken( ICollection tokens, SelectorDescriptor[] selectors ) @@ -79,8 +81,6 @@ internal static JsonPathSegment ParseNoCache( ReadOnlySpan query ) private static JsonPathSegment TokenFactory( ReadOnlySpan query ) { - // transform jsonpath patterns like "$.store.book[*]..author" to an array of tokens [ $, store, book, *, .., author ] - var tokens = new List(); query = query.TrimEnd(); // remove trailing whitespace to simplify parsing @@ -96,10 +96,24 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) var selectors = new List(); var state = State.Start; + State returnState = State.Undefined; do { - var c = query[i++]; + // read next character + char c; + + if ( i < n ) + { + c = query[i++]; + } + else // end of input + { + state = State.FinalSelector; + c = '\0'; // Add null terminator to signal end of input + } + + // process character SelectorKind selectorKind; string selectorValue; @@ -128,6 +142,18 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) break; + case State.QuotedName: + if ( c == '\\' ) // handle escaping + { + i++; // advance past the escaped character + } + else if ( c == literalDelimiter ) + { + state = returnState; // transition back to the appropriate state + } + + break; + case State.DotChild: switch ( c ) { @@ -139,15 +165,22 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) { "$" when tokens.Count != 0 => throw new NotSupportedException( $"Invalid use of root `$` at pos {i - 1}." ), "$" => SelectorKind.Root, + "@" when tokens.Count != 0 => throw new NotSupportedException( $"Invalid use of local root `$` at pos {i - 1}." ), + "@" => SelectorKind.Root, "*" => SelectorKind.Wildcard, - _ => SelectorKind.Dot + _ => SelectorKind.DotName }; + if ( selectorKind == SelectorKind.DotName && selectorValue != null ) + { + ThrowIfQuoted( selectorValue ); + ThrowIfNotValidUnquotedName( selectorValue ); + } + InsertToken( tokens, new SelectorDescriptor { SelectorKind = selectorKind, Value = selectorValue } ); break; case '.': - if ( i == n ) throw new NotSupportedException( $"Missing character after `.` at pos {i - 1}." ); @@ -156,24 +189,38 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) { "$" when tokens.Count != 0 => throw new NotSupportedException( $"Invalid use of root `$` at pos {i - 1}." ), "$" => SelectorKind.Root, + "@" when tokens.Count != 0 => throw new NotSupportedException( $"Invalid use of local root `$` at pos {i - 1}." ), + "@" => SelectorKind.Root, "*" => SelectorKind.Wildcard, - _ => SelectorKind.Dot + _ => SelectorKind.DotName }; + if ( selectorKind == SelectorKind.DotName && selectorValue != null ) // can be null after a union + { + ThrowIfQuoted( selectorValue ); + ThrowIfNotValidUnquotedName( selectorValue ); + } + InsertToken( tokens, new SelectorDescriptor { SelectorKind = selectorKind, Value = selectorValue } ); - if ( i <= n && query[i] == '.' ) + if ( i < n && query[i] == '.' ) // peek next character { InsertToken( tokens, new SelectorDescriptor { SelectorKind = SelectorKind.Descendant, Value = ".." } ); - i++; } selectorStart = i; break; + case '\'': + case '"': + throw new NotSupportedException( $"Quoted member names are not allowed in dot notation at pos {i - 1}." ); case ' ': case '\t': throw new NotSupportedException( $"Invalid whitespace in object notation at pos {i - 1}." ); + case '\0': + state = State.FinalSelector; + i--; // step back to process the last character + break; } break; @@ -198,7 +245,8 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) break; case '\'': case '"': - state = State.UnionElementQuoted; + returnState = State.UnionQuotedFinal; + state = State.QuotedName; literalDelimiter = c; selectorStart = i - 1; bracketDepth = 1; @@ -213,19 +261,7 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) break; - case State.UnionElementQuoted: - if ( c == '\\' ) // handle escaping - { - i++; // advance past the escaped character - } - else if ( c == literalDelimiter ) - { - state = State.UnionElementQuotedFinal; - } - - break; - - case State.UnionElementQuotedFinal: + case State.UnionQuotedFinal: switch ( c ) { case ' ': @@ -273,14 +309,12 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) selectorKind = GetSelectorKind( selectorValue ); - if ( selectorKind == SelectorKind.Undefined ) - throw new NotSupportedException( $"Invalid bracket expression syntax. Unrecognized selector format at pos {i - 1}." ); - - if ( selectorKind == SelectorKind.Name ) + selectorValue = selectorKind switch { - selectorValue = selectorValue[1..^1]; // remove surrounding quotes - selectorValue = Regex.Unescape( selectorValue ); // unescape selector - } + SelectorKind.Undefined => throw new NotSupportedException( $"Invalid bracket expression syntax. Unrecognized selector format at pos {i - 1}." ), + SelectorKind.Name => UnquoteAndUnescape( selectorValue ), + _ => selectorValue + }; selectors.Insert( 0, new SelectorDescriptor { SelectorKind = selectorKind, Value = selectorValue } ); @@ -289,11 +323,13 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) switch ( c ) { case ',': - state = State.UnionNextElement; + state = State.UnionNext; break; case ']': + if ( i < n && query[i] != '.' && query[i] != '[' ) + throw new NotSupportedException( $"Invalid character after `]` at pos {i - 1}." ); state = State.DotChild; - InsertToken( tokens, [.. selectors] ); + InsertToken( tokens, selectors.ToArray() ); selectors.Clear(); break; } @@ -303,7 +339,7 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) break; - case State.UnionNextElement: + case State.UnionNext: case State.UnionFinal: switch ( c ) { @@ -311,20 +347,23 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) case '\t': break; case ']': + if ( i < n && query[i] != '.' && query[i] != '[' ) + throw new NotSupportedException( $"Invalid character after `]` at pos {i - 1}." ); state = State.DotChild; selectorStart = i; break; case '\'': case '"': - if ( state != State.UnionNextElement ) + if ( state != State.UnionNext ) throw new NotSupportedException( $"Invalid bracket syntax at pos {i - 1}." ); - state = State.UnionElementQuoted; + returnState = State.UnionQuotedFinal; + state = State.QuotedName; literalDelimiter = c; selectorStart = i - 1; break; default: - if ( state != State.UnionNextElement ) + if ( state != State.UnionNext ) throw new NotSupportedException( $"Invalid bracket syntax at pos {i - 1}." ); state = State.UnionElement; @@ -336,27 +375,33 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) break; + case State.FinalSelector: + selectorValue = GetSelector( state, query, selectorStart, i ); + if ( selectorValue != null ) + { + var finalKind = selectorValue switch + { + "*" => SelectorKind.Wildcard, + ".." => SelectorKind.Descendant, + _ => SelectorKind.DotName + }; + + if ( finalKind == SelectorKind.DotName ) + { + ThrowIfQuoted( selectorValue ); + ThrowIfNotValidUnquotedName( selectorValue ); + } + + InsertToken( tokens, new SelectorDescriptor { SelectorKind = finalKind, Value = selectorValue } ); + } + + state = State.Final; + break; + default: throw new InvalidOperationException(); } - } while ( i < n ); - - // handle the trailing bits - state = State.Final; - - var finalSelector = GetSelector( state, query, selectorStart, i ); - - if ( finalSelector != null ) - { - var finalKind = finalSelector switch - { - "*" => SelectorKind.Wildcard, - ".." => SelectorKind.Descendant, - _ => SelectorKind.Dot - }; - - InsertToken( tokens, new SelectorDescriptor { SelectorKind = finalKind, Value = finalSelector } ); - } + } while ( state != State.Final ); // return tokenized query as a segment list @@ -481,6 +526,45 @@ private static bool IsIndex( ReadOnlySpan input ) private static bool IsQuoted( ReadOnlySpan input ) { - return (input[0] == '"' && input[^1] == '"') || (input[0] == '\'' && input[^1] == '\''); + return (input.Length > 1 && + input[0] == '"' && input[^1] == '"' || + input[0] == '\'' && input[^1] == '\''); + } + + private static void ThrowIfQuoted( string value ) + { + if ( IsQuoted( value ) ) + throw new NotSupportedException( $"Quoted member names are not allowed in dot notation: {value}" ); + } + + private static void ThrowIfNotValidUnquotedName( ReadOnlySpan name ) + { + if ( name.IsEmpty ) + throw new NotSupportedException( "Selector name cannot be null." ); + + // Validate the first character + if ( !char.IsLetter( name[0] ) && name[0] != '_' && name[0] != '$' ) + throw new NotSupportedException( $"Selector name cannot start with `{name[0]}`." ); + + // Validate subsequent characters + for ( int i = 1; i < name.Length; i++ ) + { + if ( !char.IsLetterOrDigit( name[i] ) && name[i] != '_' && name[i] != '-' && name[i] != '$' ) + throw new NotSupportedException( $"Selector name cannot contain `{name[i]}`." ); + } + } + + private static string UnquoteAndUnescape( string value ) + { + if ( value.Length <= 0 ) + return null; + + value = value.Trim(); + + if ( IsQuoted( value ) ) + return Regex.Unescape( value[1..^1] ); // unquote and unescape + + ThrowIfNotValidUnquotedName( value ); + return value; } } diff --git a/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs b/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs index f740a307..3ca4ff68 100644 --- a/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs +++ b/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs @@ -21,7 +21,7 @@ private enum Thing public void DynamicJsonElement_ShouldReturnCorrectResults() { var source = GetDocument(); - var element = source.ToDynamic(); + var element = JsonHelper.ConvertToDynamic( source ); var book = element.store.book[0]; var author = book.author; diff --git a/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs b/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs index 42f1b368..9b5402c4 100644 --- a/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs +++ b/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs @@ -28,7 +28,7 @@ public void Should_SerializeJsonElement_ToObject() var document = JsonDocument.Parse( json ); // act - var result = document.RootElement.ToObject(); + var result = JsonHelper.ConvertToObject( document.RootElement ); // assert Assert.AreEqual( source, result ); diff --git a/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs index a89af0a5..7c6d345e 100644 --- a/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Hyperbee.Json.Tests.Parsers; @@ -56,4 +57,18 @@ static string SegmentToString( JsonPathSegment segment ) } } } + + [TestMethod] + public void ShouldFilterExpressionWithParentAxisOperator() + { + // NOT-SUPPORTED: parent axis operator is not supported + + // act & assert + const string jsonPath = "$[*].bookmarks[ ? (@.page == 45)]^^^"; + + Assert.ThrowsException( () => + { + _ = JsonPathQueryParser.Parse( jsonPath ); + } ); + } } From 4d192af1485458144c83505d0625fff6f261821f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Jul 2024 06:24:35 -0700 Subject: [PATCH 02/18] [FEATURE]: Improve documentation and comments (#34) * Update comments and documentation --------- Co-authored-by: Brenton Farmer --- docs/ADDITIONAL-CLASSES.md | 24 +++++++++--- .../Extensions/JsonPathPointerExtensions.cs | 23 ++++++----- src/Hyperbee.Json/Filters/FilterEvaluator.cs | 2 +- .../Filters/FilterEvaluatorException.cs | 20 ---------- src/Hyperbee.Json/Filters/FilterException.cs | 20 ++++++++++ .../Filters/Parser/FilterParser.cs | 3 +- .../Filters/Parser/FilterTruthyExpression.cs | 39 +++++++++---------- src/Hyperbee.Json/JsonPath.cs | 14 ++++--- .../Parsers/FilterExtensionFunctionTests.cs | 29 +++++++------- 9 files changed, 96 insertions(+), 78 deletions(-) delete mode 100644 src/Hyperbee.Json/Filters/FilterEvaluatorException.cs create mode 100644 src/Hyperbee.Json/Filters/FilterException.cs diff --git a/docs/ADDITIONAL-CLASSES.md b/docs/ADDITIONAL-CLASSES.md index 77f3e1d9..bb90dcf3 100644 --- a/docs/ADDITIONAL-CLASSES.md +++ b/docs/ADDITIONAL-CLASSES.md @@ -25,7 +25,7 @@ for a given `JsonElement`. | Method | Description |:---------------------------|:----------- -| `JsonPathBuilder.GetPath` | Returns the JsonPath location string for a given element +| `JsonPathBuilder.GetPath` | Returns the JsonPath location string for a given element ### Equality Helpers @@ -37,11 +37,25 @@ for a given `JsonElement`. ### 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. A helper methods `JsonPathHelper.ConvertToDynamic` is provided to simplify the process of +The `DynamicJsonConverter` class is useful for simple scenareos. It is intended as a simple helper for +basic use cases only. A helper methods `JsonHelper.ConvertToDynamic` is provided to simplify the process of serializing and deserializing dynamic objects. -#### DynamicJsonConverter +#### Example: ConvertToDynamic + +```csharp +var root = JsonDocument.Parse(jsonInput); // jsonInput contains the bookstore example +var element = JsonHelper.ConvertToDynamic( source ); + +var book = element.store.book[0]; +var author = book.author; +var price = book.price; + +Assert.IsTrue( price == 8.95 ); +Assert.IsTrue( author == "Nigel Rees" ); +``` + +#### Example: Serialize To Dynamic ```csharp var serializerOptions = new JsonSerializerOptions @@ -49,7 +63,7 @@ var serializerOptions = new JsonSerializerOptions Converters = {new DynamicJsonConverter()} }; -// jsonInput is a string containing the bookstore json from the previous examples +// jsonInput contains the bookstore example var jobject = JsonSerializer.Deserialize( jsonInput, serializerOptions); Assert.IsTrue( jobject.store.bicycle.color == "red" ); diff --git a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs index 596bb164..a45d90d4 100644 --- a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs +++ b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs @@ -12,12 +12,15 @@ namespace Hyperbee.Json.Extensions; // Json path style wildcard '*', '..', and '[a,b]' multi-result selector notations are NOT supported. // // examples: -// prop1.prop2 -// prop1[0] -// 'prop.2' -// prop1[0].prop2 -// prop1['prop.2'] -// prop1.'prop.2'[0].prop3 +// $.prop1.prop2 +// $.prop1[0] +// $.prop1[0].prop2 +// $.prop1['prop.2'] +// +// also supports quoted member-name for dot child +// +// $.'prop.2' +// $.prop1.'prop.2'[0].prop3 public static class JsonPathPointerExtensions { @@ -109,10 +112,10 @@ private enum BracketContent internal JsonPathPointerSplitter( ReadOnlySpan span ) { - if ( span.StartsWith( "$." ) ) - span = span[2..]; - else if ( span.StartsWith( "$" ) ) - span = span[1..]; + if ( !span.StartsWith( "$" ) ) + throw new NotSupportedException( "Path must start with `$`." ); + + span = span.StartsWith( "$." ) ? span[2..] : span[1..]; // eat the leading $ _span = span; _scanner = Scanner.Default; diff --git a/src/Hyperbee.Json/Filters/FilterEvaluator.cs b/src/Hyperbee.Json/Filters/FilterEvaluator.cs index dc089bfe..4021d465 100644 --- a/src/Hyperbee.Json/Filters/FilterEvaluator.cs +++ b/src/Hyperbee.Json/Filters/FilterEvaluator.cs @@ -30,7 +30,7 @@ public object Evaluate( string filter, TNode current, TNode root ) } catch ( Exception ex ) { - throw new FilterEvaluatorException( "Error compiling JsonPath expression.", ex ); + throw new FilterException( "Error compiling filter expression.", ex ); } } } diff --git a/src/Hyperbee.Json/Filters/FilterEvaluatorException.cs b/src/Hyperbee.Json/Filters/FilterEvaluatorException.cs deleted file mode 100644 index 7ba399e2..00000000 --- a/src/Hyperbee.Json/Filters/FilterEvaluatorException.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Hyperbee.Json.Filters; - -[Serializable] -public class FilterEvaluatorException : Exception -{ - public FilterEvaluatorException() - : base( "JsonPath filter evaluator exception." ) - { - } - - public FilterEvaluatorException( string message ) - : base( message ) - { - } - - public FilterEvaluatorException( string message, Exception innerException ) - : base( message, innerException ) - { - } -} diff --git a/src/Hyperbee.Json/Filters/FilterException.cs b/src/Hyperbee.Json/Filters/FilterException.cs new file mode 100644 index 00000000..97de57da --- /dev/null +++ b/src/Hyperbee.Json/Filters/FilterException.cs @@ -0,0 +1,20 @@ +namespace Hyperbee.Json.Filters; + +[Serializable] +public class FilterException : Exception +{ + public FilterException() + : base( "JsonPath filter evaluator exception." ) + { + } + + public FilterException( string message ) + : base( message ) + { + } + + public FilterException( string message, Exception innerException ) + : base( message, innerException ) + { + } +} diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index 246b2c5a..2e9fd54d 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -10,7 +10,6 @@ #endregion using System.Linq.Expressions; -using System.Reflection; using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Parser.Expressions; @@ -51,7 +50,7 @@ internal static Expression Parse( ref ParserState state, FilterContext co throw new ArgumentNullException( nameof( context ) ); if ( state.EndOfBuffer || state.IsTerminal ) - throw new ArgumentException( $"Invalid filter: \"{state.Buffer}\"", nameof( state ) ); + throw new ArgumentException( $"Invalid filter: \"{state.Buffer}\".", nameof( state ) ); // parse the expression var items = new List(); diff --git a/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs b/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs index 778159a0..194ba31c 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs @@ -2,29 +2,28 @@ using System.Linq.Expressions; using System.Reflection; -namespace Hyperbee.Json.Filters.Parser +namespace Hyperbee.Json.Filters.Parser; + +public static class FilterTruthyExpression { - public static class FilterTruthyExpression - { - private static readonly MethodInfo IsTruthyMethodInfo = typeof( FilterTruthyExpression ).GetMethod( nameof( IsTruthy ) ); + private static readonly MethodInfo IsTruthyMethodInfo = typeof( FilterTruthyExpression ).GetMethod( nameof( IsTruthy ) ); - public static Expression IsTruthyExpression( Expression expression ) => - expression.Type == typeof( bool ) - ? expression - : Expression.Call( IsTruthyMethodInfo, expression ); + public static Expression IsTruthyExpression( Expression expression ) => + expression.Type == typeof( bool ) + ? expression + : Expression.Call( IsTruthyMethodInfo, expression ); - public static bool IsTruthy( object value ) + public static bool IsTruthy( object value ) + { + return value switch { - return value switch - { - null => false, - bool boolValue => boolValue, - string str => !string.IsNullOrEmpty( str ) && !str.Equals( "false", StringComparison.OrdinalIgnoreCase ), - Array array => array.Length > 0, - IEnumerable enumerable => enumerable.Cast().Any(), - IConvertible convertible => Convert.ToBoolean( convertible ), - _ => true - }; - } + null => false, + bool boolValue => boolValue, + string str => !string.IsNullOrEmpty( str ) && !str.Equals( "false", StringComparison.OrdinalIgnoreCase ), + Array array => array.Length > 0, + IEnumerable enumerable => enumerable.Cast().Any(), + IConvertible convertible => Convert.ToBoolean( convertible ), + _ => true + }; } } diff --git a/src/Hyperbee.Json/JsonPath.cs b/src/Hyperbee.Json/JsonPath.cs index 66645e78..28bb5acf 100644 --- a/src/Hyperbee.Json/JsonPath.cs +++ b/src/Hyperbee.Json/JsonPath.cs @@ -146,8 +146,8 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N // to push and pop values onto the stack that we know will not be used. if ( segmentNext.IsFinal ) { - // theoretically, we should yield here, but we can't because we need to - // preserve the order of the results as per the RFC. so we push the + // we could just yield here, but we can't because we want to preserve + // the order of the results as per the RFC. so we push the current // value onto the stack without prepending the childKey or childKind // to set up for an immediate return on the next iteration. //Push( stack, value, childValue, childKey, segmentNext ); @@ -170,8 +170,10 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N stack.Push( value, childValue, childKey, segmentCurrent ); // Descendant } - // Union Processing After Descent: If a union operator follows a descent operator, - // either directly or after intermediary selectors, it should only process simple values. + // Union Processing After Descent: If a union operator immediately follows a + // descendant operator, the union should only process simple values. This is + // to prevent duplication of complex objects that would result from both the + // current node and the union processing the same items. stack.Push( parent, value, null, segmentNext, NodeFlags.AfterDescent ); // process the current value continue; @@ -179,7 +181,7 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N // group - for ( var i = 0; i < segmentCurrent.Selectors.Length; i++ ) // use 'for' for performance + for ( var i = 0; i < segmentCurrent.Selectors.Length; i++ ) // using 'for' for performance { if ( i != 0 ) (selector, selectorKind) = segmentCurrent.Selectors[i]; @@ -190,7 +192,7 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N { foreach ( var (childValue, childKey, childKind) in accessor.EnumerateChildren( value ) ) { - var result = filterEvaluator.Evaluate( selector[1..], childValue, root ); // remove leading '?' + var result = filterEvaluator.Evaluate( selector[1..], childValue, root ); // remove the leading '?' character if ( !Truthy( result ) ) continue; diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs index 0a747303..1f3fc32c 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs @@ -31,21 +31,22 @@ public void Should_CallCustomFunction() Assert.IsTrue( results.Count == 1 ); Assert.AreEqual( "$.store.book[2].title", results[0].GetPath() ); } -} - -public class PathNodeFunction() : FilterExtensionFunction( argumentCount: 1 ) -{ - public const string Name = "path"; - private static readonly Expression PathExpression = Expression.Constant( (Func, string>) Path ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + private class PathNodeFunction() : FilterExtensionFunction( argumentCount: 1 ) { - return Expression.Invoke( PathExpression, arguments[0] ); - } - - public static string Path( IEnumerable nodes ) - { - var node = nodes.FirstOrDefault(); - return node?.GetPath(); + public const string Name = "path"; + private static readonly Expression PathExpression = Expression.Constant( (Func, string>) Path ); + + protected override Expression GetExtensionExpression( Expression[] arguments ) + { + return Expression.Invoke( PathExpression, arguments[0] ); + } + + private static string Path( IEnumerable nodes ) + { + var node = nodes.FirstOrDefault(); + return node?.GetPath(); + } } } + From 6f56d329d9ac375298f892433b73b8c586f53a4c Mon Sep 17 00:00:00 2001 From: Matt Edwards Date: Fri, 12 Jul 2024 17:28:37 -0400 Subject: [PATCH 03/18] Feature/30 feature test with jsonpath compliance test suite (#35) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This project pulls the cts.json directly from the Compliance Test Suite - Improved handling of whitespace - Validation of non-singular queries and constants - Added support for I-Regexp format (RFC-9485)​. - Improved filter expression parsing - Switched to using an internal type system for comparing values - Memory and performance improvements --------- Co-authored-by: Brenton Farmer --- Hyperbee.Json.sln | 9 +- Hyperbee.Json.sln.DotSettings | 1 + README.md | 93 +- .../Element/ElementTypeDescriptor.cs | 7 + .../Element/ElementValueAccessor.cs | 30 +- .../Element/Functions/CountElementFunction.cs | 25 +- .../Functions/LengthElementFunction.cs | 38 +- .../Element/Functions/MatchElementFunction.cs | 35 +- .../Functions/SearchElementFunction.cs | 35 +- .../Element/Functions/ValueElementFunction.cs | 47 +- .../Descriptors/ITypeDescriptor.cs | 4 + .../Descriptors/IValueAccessor.cs | 7 +- .../Node/Functions/CountNodeFunction.cs | 22 +- .../Node/Functions/LengthNodeFunction.cs | 39 +- .../Node/Functions/MatchNodeFunction.cs | 37 +- .../Node/Functions/SearchNodeFunction.cs | 34 +- .../Node/Functions/ValueNodeFunction.cs | 39 +- .../Descriptors/Node/NodeTypeDescriptor.cs | 7 + .../Descriptors/Node/NodeValueAccessor.cs | 29 +- src/Hyperbee.Json/Extensions/JsonHelper.cs | 75 +- .../Extensions/JsonPathPointerExtensions.cs | 277 +- src/Hyperbee.Json/Filters/FilterEvaluator.cs | 14 +- src/Hyperbee.Json/Filters/IFilterEvaluator.cs | 2 +- src/Hyperbee.Json/Filters/IRegexp.cs | 115 + .../Filters/Parser/ExpressionInfo.cs | 7 + .../Filters/Parser/ExpressionKind.cs | 13 + .../Expressions/ComparerExpressionFactory.cs | 246 - .../Expressions/FunctionExpressionFactory.cs | 15 +- .../Parser/Expressions/IExpressionFactory.cs | 2 +- .../Expressions/JsonExpressionFactory.cs | 8 +- .../Expressions/LiteralExpressionFactory.cs | 28 +- .../Expressions/NotExpressionFactory.cs | 9 +- .../Expressions/ParenExpressionFactory.cs | 11 +- .../Expressions/SelectExpressionFactory.cs | 37 +- .../Filters/Parser/FilterContext.cs | 16 - .../Filters/Parser/FilterExtensionFunction.cs | 56 +- .../Filters/Parser/FilterExtensionInfo.cs | 9 + .../Filters/Parser/FilterParser.cs | 367 +- .../Filters/Parser/FilterParserContext.cs | 12 + .../Filters/Parser/FilterTruthyExpression.cs | 26 +- .../NodeTypeComparerBinderExpression.cs | 25 + .../Filters/Parser/NodeTypeExpression.cs | 64 + src/Hyperbee.Json/Filters/Parser/Operator.cs | 61 +- .../Filters/Parser/ParserState.cs | 24 +- src/Hyperbee.Json/Filters/Values/Constants.cs | 10 + src/Hyperbee.Json/Filters/Values/INodeType.cs | 8 + .../Filters/Values/NodeTypeComparer.cs | 236 + .../Filters/Values/NodeTypeKind.cs | 10 + src/Hyperbee.Json/Filters/Values/NodesType.cs | 17 + src/Hyperbee.Json/Filters/Values/Nothing.cs | 7 + src/Hyperbee.Json/Filters/Values/Null.cs | 7 + src/Hyperbee.Json/Filters/Values/ValueType.cs | 10 + src/Hyperbee.Json/Internal/SpanBuilder.cs | 76 + src/Hyperbee.Json/Internal/SpanHelper.cs | 132 + src/Hyperbee.Json/JsonPath.cs | 252 +- src/Hyperbee.Json/JsonPathQueryParser.cs | 752 +- src/Hyperbee.Json/JsonPathSegment.cs | 21 + .../JsonPathSliceSyntaxHelper.cs | 27 +- .../FilterExpressionParserEvaluator.cs | 12 +- test/Hyperbee.Json.Cts/AssertExtensions.cs | 61 + .../Hyperbee.Json.Cts.csproj | 27 + test/Hyperbee.Json.Cts/TestHelper.cs | 54 + .../Tests/cts-basic-tests.cs | 1068 ++ .../Tests/cts-filter-tests.cs | 3884 ++++++++ .../Tests/cts-functions-tests.cs | 1772 ++++ .../Tests/cts-index-selector-tests.cs | 203 + .../Tests/cts-name-selector-tests.cs | 1393 +++ .../Tests/cts-slice-selector-tests.cs | 1057 ++ .../Tests/cts-whitespace-tests.cs | 4929 ++++++++++ test/Hyperbee.Json.Cts/cts.json | 8624 +++++++++++++++++ test/Hyperbee.Json.Cts/generate_tests.ps1 | 296 + .../Extensions/JsonExtensionTests.cs | 20 - .../Parsers/FilterExtensionFunctionTests.cs | 25 +- .../Parsers/FilterParserTests.cs | 84 +- .../Parsers/JsonPathQueryParserTests.cs | 4 +- .../Query/JsonComparerComparandTests.cs | 131 - .../Query/JsonPathArrayTests.cs | 20 - .../Query/JsonPathBracketNotationTests.cs | 40 +- .../Query/JsonPathFilterExpressionTests.cs | 90 +- .../Query/NodeTypeComparerTests.cs | 149 + 80 files changed, 25858 insertions(+), 1707 deletions(-) create mode 100644 src/Hyperbee.Json/Filters/IRegexp.cs create mode 100644 src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs create mode 100644 src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/Expressions/ComparerExpressionFactory.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/FilterContext.cs create mode 100644 src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs create mode 100644 src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs create mode 100644 src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs create mode 100644 src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs create mode 100644 src/Hyperbee.Json/Filters/Values/Constants.cs create mode 100644 src/Hyperbee.Json/Filters/Values/INodeType.cs create mode 100644 src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs create mode 100644 src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs create mode 100644 src/Hyperbee.Json/Filters/Values/NodesType.cs create mode 100644 src/Hyperbee.Json/Filters/Values/Nothing.cs create mode 100644 src/Hyperbee.Json/Filters/Values/Null.cs create mode 100644 src/Hyperbee.Json/Filters/Values/ValueType.cs create mode 100644 src/Hyperbee.Json/Internal/SpanBuilder.cs create mode 100644 src/Hyperbee.Json/Internal/SpanHelper.cs create mode 100644 test/Hyperbee.Json.Cts/AssertExtensions.cs create mode 100644 test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj create mode 100644 test/Hyperbee.Json.Cts/TestHelper.cs create mode 100644 test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs create mode 100644 test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs create mode 100644 test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs create mode 100644 test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs create mode 100644 test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs create mode 100644 test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs create mode 100644 test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs create mode 100644 test/Hyperbee.Json.Cts/cts.json create mode 100644 test/Hyperbee.Json.Cts/generate_tests.ps1 delete mode 100644 test/Hyperbee.Json.Tests/Query/JsonComparerComparandTests.cs create mode 100644 test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs diff --git a/Hyperbee.Json.sln b/Hyperbee.Json.sln index c197bd2c..144fb64c 100644 --- a/Hyperbee.Json.sln +++ b/Hyperbee.Json.sln @@ -22,13 +22,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1FA7 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{4DBDB7F5-3F66-4572-80B5-3322449C77A4}" ProjectSection(SolutionItems) = preProject - .github\workflows\create-prerelease.yml = .github\workflows\create-prerelease.yml .github\workflows\create-release.yml = .github\workflows\create-release.yml .github\workflows\format.yml = .github\workflows\format.yml .github\workflows\issue-branch.yml = .github\workflows\issue-branch.yml .github\workflows\publish.yml = .github\workflows\publish.yml .github\workflows\test-report.yml = .github\workflows\test-report.yml .github\workflows\test.yml = .github\workflows\test.yml + .github\workflows\update-version.yml = .github\workflows\update-version.yml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbee.Json.Tests", "test\Hyperbee.Json.Tests\Hyperbee.Json.Tests.csproj", "{97886205-1467-4EE6-B3DA-496CA3D086E4}" @@ -41,6 +41,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{13CB9B41-0 docs\JSONPATH-SYNTAX.md = docs\JSONPATH-SYNTAX.md EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbee.Json.Cts", "test\Hyperbee.Json.Cts\Hyperbee.Json.Cts.csproj", "{CC1D3E7F-E6F1-432B-B4D1-9402AED24119}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,6 +61,10 @@ Global {45C24D4B-4A0B-4FF1-AC66-38374D2455E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {45C24D4B-4A0B-4FF1-AC66-38374D2455E9}.Release|Any CPU.ActiveCfg = Release|Any CPU {45C24D4B-4A0B-4FF1-AC66-38374D2455E9}.Release|Any CPU.Build.0 = Release|Any CPU + {CC1D3E7F-E6F1-432B-B4D1-9402AED24119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC1D3E7F-E6F1-432B-B4D1-9402AED24119}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC1D3E7F-E6F1-432B-B4D1-9402AED24119}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC1D3E7F-E6F1-432B-B4D1-9402AED24119}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -69,6 +75,7 @@ Global {97886205-1467-4EE6-B3DA-496CA3D086E4} = {F9B24CD9-E06B-4834-84CB-8C29E5F10BE0} {45C24D4B-4A0B-4FF1-AC66-38374D2455E9} = {F9B24CD9-E06B-4834-84CB-8C29E5F10BE0} {13CB9B41-0462-4812-8B13-0BFD17F2BC18} = {870D9301-BE3D-44EA-BF9C-FCC2E87FE4CD} + {CC1D3E7F-E6F1-432B-B4D1-9402AED24119} = {F9B24CD9-E06B-4834-84CB-8C29E5F10BE0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {32874F5B-B467-4F28-A8E2-82C2536FB228} diff --git a/Hyperbee.Json.sln.DotSettings b/Hyperbee.Json.sln.DotSettings index 83de03a7..441eaa58 100644 --- a/Hyperbee.Json.sln.DotSettings +++ b/Hyperbee.Json.sln.DotSettings @@ -51,6 +51,7 @@ True True True + True True True True diff --git a/README.md b/README.md index 18b7600e..f40a3e5e 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ The library is designed to be quick and extensible, allowing support for other J - **`IEnumerable` Results:** Deferred execution queries with `IEnumerable`. - **Conformant:** Adheres to the JSONPath Specification [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html). -## JSONPath Consensus +## JSONPath RFC -Hyperbee.Json aims to follow the RFC, and to support the [JSONPath consensus](https://cburgmer.github.io/json-path-comparison) +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. +strengthening our alignment with the RFC and the community. ## Installation @@ -193,21 +193,18 @@ You can also extend the supported function set by registering your own functions **Step 1:** Create a custom function that returns the path of a `JsonNode`. ```csharp -public class PathNodeFunction() : FilterExtensionFunction( argumentCount: 1 ) +public class PathNodeFunction() : FilterExtensionFunction( PathMethodInfo, FilterExtensionInfo.MustCompare ) { public const string Name = "path"; - private static readonly Expression PathExpression = Expression.Constant( (Func, string>) Path ); + private static readonly MethodInfo PathMethodInfo = GetMethod( nameof( Path ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + private static INodeType Path( INodeType arg ) { - return Expression.Invoke( PathExpression, arguments[0] ); - } + if ( arg is not NodesType nodes ) + return Constants.Null; - public static string Path( IEnumerable nodes ) - { var node = nodes.FirstOrDefault(); - return node?.GetPath(); - } + return new ValueType( node?.GetPath() ); } ``` @@ -231,7 +228,6 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. ### [JsonPath.Net](https://docs.json-everything.net/path/basics/) Json-Everything - **Pros:** - - Extensive JSON ecosystem. - Comprehensive feature set. - Deferred execution queries with `IEnumerable`. - Strong community support. @@ -268,7 +264,7 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. - Supports both `JsonElement`, and `JsonNode`. - Deferred execution queries with `IEnumerable`. - Extendable to support additional JSON document types and functions. -- RFC and Consensus focused JSONPath implementation. +- RFC conforming JSONPath implementation. ## Benchmarks @@ -314,41 +310,41 @@ Here is a performance comparison of various queries on the standard book store d ``` ``` -| Method | Filter | Mean | Error | StdDev | Allocated -|:----------------------- |:-------------------------------- |:--------- |:--------- |:--------- |:--------- -| Hyperbee_JsonElement | $..* `First()` | 3.026 us | 0.3647 us | 0.0200 us | 4.22 KB -| JsonEverything_JsonNode | $..* `First()` | 3.170 us | 0.3034 us | 0.0166 us | 3.53 KB -| Hyperbee_JsonNode | $..* `First()` | 3.275 us | 1.7533 us | 0.0961 us | 3.37 KB -| JsonCons_JsonElement | $..* `First()` | 5.699 us | 0.2191 us | 0.0120 us | 8.48 KB -| Newtonsoft_JObject | $..* `First()` | 8.671 us | 1.7810 us | 0.0976 us | 14.22 KB -| | | | | | -| JsonCons_JsonElement | $..* | 5.772 us | 3.8960 us | 0.2136 us | 8.45 KB -| Hyperbee_JsonElement | $..* | 8.179 us | 4.9380 us | 0.2707 us | 11.02 KB -| Newtonsoft_JObject | $..* | 9.867 us | 0.9006 us | 0.0494 us | 14.86 KB -| Hyperbee_JsonNode | $..* | 10.188 us | 2.0528 us | 0.1125 us | 10.83 KB -| JsonEverything_JsonNode | $..* | 21.124 us | 5.1117 us | 0.2802 us | 36.81 KB -| | | | | | -| Hyperbee_JsonElement | $..price | 4.867 us | 0.1883 us | 0.0103 us | 6.37 KB -| JsonCons_JsonElement | $..price | 4.924 us | 1.5997 us | 0.0877 us | 5.65 KB -| Hyperbee_JsonNode | $..price | 7.827 us | 5.0475 us | 0.2767 us | 8.77 KB -| Newtonsoft_JObject | $..price | 9.442 us | 1.0020 us | 0.0549 us | 14.4 KB -| JsonEverything_JsonNode | $..price | 15.865 us | 2.1515 us | 0.1179 us | 27.63 KB -| | | | | | -| Hyperbee_JsonElement | $.store.book[?(@.price == 8.99)] | 4.550 us | 1.0340 us | 0.0567 us | 9.08 KB -| JsonCons_JsonElement | $.store.book[?(@.price == 8.99)] | 5.341 us | 1.0738 us | 0.0589 us | 5.05 KB -| Hyperbee_JsonNode | $.store.book[?(@.price == 8.99)] | 7.341 us | 3.6147 us | 0.1981 us | 10.63 KB -| Newtonsoft_JObject | $.store.book[?(@.price == 8.99)] | 9.621 us | 5.1553 us | 0.2826 us | 15.84 KB -| JsonEverything_JsonNode | $.store.book[?(@.price == 8.99)] | 11.789 us | 5.2457 us | 0.2875 us | 15.85 KB -| | | | | | -| Hyperbee_JsonElement | $.store.book[0] | 2.896 us | 0.1069 us | 0.0059 us | 3.41 KB -| JsonCons_JsonElement | $.store.book[0] | 2.967 us | 0.1084 us | 0.0059 us | 3.21 KB -| Hyperbee_JsonNode | $.store.book[0] | 3.352 us | 0.1778 us | 0.0097 us | 3.58 KB -| JsonEverything_JsonNode | $.store.book[0] | 4.779 us | 2.9031 us | 0.1591 us | 5.96 KB -| Newtonsoft_JObject | $.store.book[0] | 8.714 us | 2.5518 us | 0.1399 us | 14.56 KB -``` +| Method | Filter | Mean | Error | StdDev | Allocated +|------------------------ |--------------------------------- |---------- |----------- |---------- |---------- +| Hyperbee_JsonElement | $..* `First()` | 3.186 us | 0.6615 us | 0.0363 us | 4.3 KB +| Hyperbee_JsonNode | $..* `First()` | 3.521 us | 0.1192 us | 0.0065 us | 3.45 KB +| JsonEverything_JsonNode | $..* `First()` | 3.545 us | 0.7400 us | 0.0406 us | 3.53 KB +| JsonCons_JsonElement | $..* `First()` | 5.793 us | 1.3811 us | 0.0757 us | 8.48 KB +| Newtonsoft_JObject | $..* `First()` | 9.119 us | 5.3278 us | 0.2920 us | 14.22 KB +| | | | | | +| JsonCons_JsonElement | $..* | 6.098 us | 2.0947 us | 0.1148 us | 8.45 KB +| Hyperbee_JsonElement | $..* | 8.812 us | 1.6812 us | 0.0922 us | 11.1 KB +| Hyperbee_JsonNode | $..* | 10.621 us | 1.2452 us | 0.0683 us | 10.91 KB +| Newtonsoft_JObject | $..* | 11.037 us | 5.4690 us | 0.2998 us | 14.86 KB +| JsonEverything_JsonNode | $..* | 23.329 us | 2.2255 us | 0.1220 us | 36.81 KB +| | | | | | +| Hyperbee_JsonElement | $..price | 5.248 us | 3.4306 us | 0.1880 us | 6.45 KB +| JsonCons_JsonElement | $..price | 5.402 us | 0.3285 us | 0.0180 us | 5.65 KB +| Hyperbee_JsonNode | $..price | 8.483 us | 2.0999 us | 0.1151 us | 8.86 KB +| Newtonsoft_JObject | $..price | 10.109 us | 9.6403 us | 0.5284 us | 14.4 KB +| JsonEverything_JsonNode | $..price | 17.054 us | 10.5303 us | 0.5772 us | 27.63 KB +| | | | | | +| Hyperbee_JsonElement | $.store.book[?(@.price == 8.99)] | 4.486 us | 3.2931 us | 0.1805 us | 5.82 KB +| JsonCons_JsonElement | $.store.book[?(@.price == 8.99)] | 5.381 us | 3.3826 us | 0.1854 us | 5.05 KB +| Hyperbee_JsonNode | $.store.book[?(@.price == 8.99)] | 7.354 us | 4.9887 us | 0.2734 us | 8.47 KB +| Newtonsoft_JObject | $.store.book[?(@.price == 8.99)] | 10.519 us | 3.5514 us | 0.1947 us | 15.84 KB +| JsonEverything_JsonNode | $.store.book[?(@.price == 8.99)] | 11.912 us | 7.6346 us | 0.4185 us | 15.85 KB +| | | | | | +| Hyperbee_JsonElement | $.store.book[0] | 2.722 us | 0.5813 us | 0.0319 us | 2.27 KB +| JsonCons_JsonElement | $.store.book[0] | 3.150 us | 1.7316 us | 0.0949 us | 3.21 KB +| Hyperbee_JsonNode | $.store.book[0] | 3.339 us | 0.1733 us | 0.0095 us | 2.77 KB +| JsonEverything_JsonNode | $.store.book[0] | 4.974 us | 3.2013 us | 0.1755 us | 5.96 KB +| Newtonsoft_JObject | $.store.book[0] | 9.482 us | 7.0303 us | 0.3854 us | 14.56 KB +``` -## Additional Documentation +## Additioal Documentation Additional documentation can be found in the project's `/docs` folder. @@ -356,10 +352,11 @@ Additional documentation can be found in the project's `/docs` folder. Hyperbee.Json is built upon the great work of several open-source projects. Special thanks to: -- Stefan Goessner for the original [JSONPath implementation](https://goessner.net/articles/JsonPath/). - System.Text.Json team for their work on the `System.Text.Json` library. +- Stefan Goessner for the original [JSONPath implementation](https://goessner.net/articles/JsonPath/). - Atif Aziz's C# port of Goessner's JSONPath library [.NET JSONPath](https://github.com/atifaziz/JSONPath). - Christoph Burgmer [JSONPath consensus effort](https://cburgmer.github.io/json-path-comparison). +- [JSONPath Compliance Test Suite Team](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite). ## Contributing diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs index d58e9bf7..09687c88 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs @@ -1,6 +1,7 @@ using System.Text.Json; using Hyperbee.Json.Descriptors.Element.Functions; using Hyperbee.Json.Filters; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Element; @@ -8,6 +9,7 @@ public class ElementTypeDescriptor : ITypeDescriptor { private FilterEvaluator _evaluator; private ElementValueAccessor _accessor; + private NodeTypeComparer _comparer; public FunctionRegistry Functions { get; } = new(); @@ -17,6 +19,11 @@ public class ElementTypeDescriptor : ITypeDescriptor public IFilterEvaluator FilterEvaluator => _evaluator ??= new FilterEvaluator( this ); + public INodeTypeComparer Comparer => + _comparer ??= new NodeTypeComparer( Accessor ); + + public bool CanUsePointer => true; + public ElementTypeDescriptor() { Functions.Register( CountElementFunction.Name, () => new CountElementFunction() ); diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs index 6de13d15..3dac8a6c 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs @@ -43,9 +43,18 @@ internal class ElementValueAccessor : IValueAccessor } [MethodImpl( MethodImplOptions.AggressiveInlining )] - public JsonElement GetElementAt( in JsonElement value, int index ) + public bool TryGetElementAt( in JsonElement value, int index, out JsonElement element ) { - return value[index]; + element = default; + + if ( index < 0 ) // flip negative index to positive + index = value.GetArrayLength() + index; + + if ( index < 0 || index >= value.GetArrayLength() ) // out of bounds + return false; + + element = value[index]; + return true; } [MethodImpl( MethodImplOptions.AggressiveInlining )] @@ -67,7 +76,7 @@ public int GetArrayLength( in JsonElement value ) : 0; } - public bool TryGetChildValue( in JsonElement value, string childSelector, out JsonElement childValue ) + public bool TryGetChildValue( in JsonElement value, string childSelector, SelectorKind selectorKind, out JsonElement childValue ) { switch ( value.ValueKind ) { @@ -77,9 +86,17 @@ public bool TryGetChildValue( in JsonElement value, string childSelector, out Js break; case JsonValueKind.Array: + if ( selectorKind == SelectorKind.Name ) + break; + if ( int.TryParse( childSelector, NumberStyles.Integer, CultureInfo.InvariantCulture, out var index ) ) { - if ( index >= 0 && index < value.GetArrayLength() ) + var arrayLength = value.GetArrayLength(); + + if ( index < 0 ) // flip negative index to positive + index = arrayLength + index; + + if ( index >= 0 && index < arrayLength ) { childValue = value[index]; return true; @@ -163,4 +180,9 @@ public bool TryGetValueFromNode( JsonElement element, out object value ) return true; } + + public bool TryGetFromPointer( in JsonElement element, JsonPathSegment segment, out JsonElement childValue ) + { + return element.TryGetFromJsonPathPointer( segment, out childValue ); + } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs index c2073206..30c22efe 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs @@ -1,22 +1,25 @@ -using System.Linq.Expressions; +using System.Reflection; using System.Text.Json; - using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Element.Functions; -public class CountElementFunction() : FilterExtensionFunction( argumentCount: 1 ) +public class CountElementFunction() : FilterExtensionFunction( CountMethodInfo, FilterExtensionInfo.MustCompare ) { public const string Name = "count"; - private static readonly Expression CountExpression = Expression.Constant( (Func, float>) Count ); - - protected override Expression GetExtensionExpression( Expression[] arguments ) - { - return Expression.Invoke( CountExpression, arguments[0] ); - } + private static readonly MethodInfo CountMethodInfo = GetMethod( nameof( Count ) ); - public static float Count( IEnumerable elements ) + public static INodeType Count( INodeType input ) { - return elements.Count(); + switch ( input ) + { + case NodesType nodes: + if ( nodes.IsNormalized && !nodes.Any() ) + return new ValueType( 1F ); + return new ValueType( nodes.Count() ); + default: + return new ValueType( 1F ); + } } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs index 7a40e282..fc2372ce 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs @@ -1,28 +1,42 @@ -using System.Linq.Expressions; +using System.Reflection; using System.Text.Json; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Element.Functions; -public class LengthElementFunction() : FilterExtensionFunction( argumentCount: 1 ) +public class LengthElementFunction() : FilterExtensionFunction( LengthMethodInfo, FilterExtensionInfo.MustCompare | FilterExtensionInfo.ExpectNormalized ) { public const string Name = "length"; - private static readonly Expression LengthExpression = Expression.Constant( (Func, float>) Length ); + private static readonly MethodInfo LengthMethodInfo = GetMethod( nameof( Length ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + public static INodeType Length( INodeType input ) { - return Expression.Invoke( LengthExpression, arguments[0] ); + return input switch + { + NodesType nodes => LengthImpl( nodes.FirstOrDefault() ), + ValueType valueString => new ValueType( valueString.Value.Length ), + Null or Nothing => input, + _ => Constants.Nothing + }; } - public static float Length( IEnumerable elements ) + public static INodeType LengthImpl( object value ) { - var element = elements.FirstOrDefault(); - return element.ValueKind switch + return value switch { - JsonValueKind.String => element.GetString()?.Length ?? 0, - JsonValueKind.Array => element.GetArrayLength(), - JsonValueKind.Object => element.EnumerateObject().Count(), - _ => 0 + string str => new ValueType( str.Length ), + Array array => new ValueType( array.Length ), + System.Collections.ICollection collection => new ValueType( collection.Count ), + System.Collections.IEnumerable enumerable => new ValueType( enumerable.Cast().Count() ), + JsonElement node => node.ValueKind switch + { + JsonValueKind.String => new ValueType( node.GetString()?.Length ?? 0 ), + JsonValueKind.Array => new ValueType( node.EnumerateArray().Count() ), + JsonValueKind.Object => new ValueType( node.EnumerateObject().Count() ), + _ => Constants.Null + }, + _ => Constants.Null }; } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs index 323a6331..652c9c5f 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs @@ -1,30 +1,39 @@ -using System.Linq.Expressions; +using System.Reflection; using System.Text.Json; using System.Text.RegularExpressions; +using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Element.Functions; -public class MatchElementFunction() : FilterExtensionFunction( argumentCount: 2 ) +public class MatchElementFunction() : FilterExtensionFunction( MatchMethodInfo, FilterExtensionInfo.MustNotCompare ) { public const string Name = "match"; - private static readonly Expression MatchExpression = Expression.Constant( (Func, string, bool>) Match ); + private static readonly MethodInfo MatchMethodInfo = GetMethod( nameof( Match ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + public static INodeType Match( INodeType input, INodeType regex ) { - return Expression.Invoke( MatchExpression, arguments[0], arguments[1] ); + return input switch + { + NodesType nodes when regex is ValueType stringValue => + MatchImpl( nodes, stringValue.Value ), + NodesType nodes when regex is NodesType stringValue => + MatchImpl( nodes, stringValue.Value.FirstOrDefault().GetString() ), + _ => Constants.False + }; } - public static bool Match( IEnumerable elements, string regex ) + public static INodeType MatchImpl( NodesType nodes, string regex ) { - var value = elements.FirstOrDefault().GetString(); + var value = nodes.FirstOrDefault(); - if ( value == null ) - { - return false; - } + if ( value.ValueKind != JsonValueKind.String ) + return Constants.False; + + var stringValue = value.GetString() ?? string.Empty; - var regexPattern = new Regex( regex.Trim( '\"', '\'' ) ); - return regexPattern.IsMatch( $"^{value}$" ); + var regexPattern = new Regex( $"^{IRegexp.ConvertToIRegexp( regex )}$" ); + return new ValueType( regexPattern.IsMatch( stringValue ) ); } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs index 47686c5e..28d4a40b 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs @@ -1,30 +1,39 @@ -using System.Linq.Expressions; +using System.Reflection; using System.Text.Json; using System.Text.RegularExpressions; +using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Element.Functions; -public class SearchElementFunction() : FilterExtensionFunction( argumentCount: 2 ) +public class SearchElementFunction() : FilterExtensionFunction( SearchMethodInfo, FilterExtensionInfo.MustNotCompare ) { public const string Name = "search"; - private static readonly Expression SearchExpression = Expression.Constant( (Func, string, bool>) Search ); + private static readonly MethodInfo SearchMethodInfo = GetMethod( nameof( Search ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + public static INodeType Search( INodeType input, INodeType regex ) { - return Expression.Invoke( SearchExpression, arguments[0], arguments[1] ); + return input switch + { + NodesType nodes when regex is ValueType stringValue => + SearchImpl( nodes, stringValue.Value ), + NodesType nodes when regex is NodesType stringValue => + SearchImpl( nodes, stringValue.Value.FirstOrDefault().GetString() ), + _ => Constants.False + }; } - public static bool Search( IEnumerable elements, string regex ) + public static INodeType SearchImpl( NodesType nodes, string regex ) { - var value = elements.FirstOrDefault().GetString(); + var value = nodes.FirstOrDefault(); - if ( value == null ) - { - return false; - } + if ( value.ValueKind != JsonValueKind.String ) + return Constants.False; + + var stringValue = value.GetString() ?? string.Empty; - var regexPattern = new Regex( regex.Trim( '\"', '\'' ) ); - return regexPattern.IsMatch( value ); + var regexPattern = new Regex( IRegexp.ConvertToIRegexp( regex ) ); + return new ValueType( regexPattern.IsMatch( stringValue ) ); } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs index 8e3ce63b..8c04d5c4 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs @@ -1,42 +1,43 @@ -using System.Linq.Expressions; +using System.Reflection; using System.Text.Json; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Element.Functions; -public class ValueElementFunction() : FilterExtensionFunction( argumentCount: 1 ) +public class ValueElementFunction() : FilterExtensionFunction( ValueMethodInfo, FilterExtensionInfo.MustCompare ) { public const string Name = "value"; - public static readonly Expression ValueExpression = Expression.Constant( (Func, object>) Value ); + private static readonly MethodInfo ValueMethodInfo = GetMethod( nameof( Value ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + public static INodeType Value( INodeType arg ) { - return Expression.Invoke( ValueExpression, arguments[0] ); - } + if ( arg.Kind != NodeTypeKind.NodeList ) + throw new NotSupportedException( $"Function {Name} does not support kind {arg.Kind}" ); - public static object Value( IEnumerable elements ) - { - var element = elements.FirstOrDefault(); + var nodeArray = ((NodesType) arg).ToArray(); + + if ( nodeArray.Length != 1 ) + return Constants.Nothing; + + var node = nodeArray.FirstOrDefault(); - return element.ValueKind switch + return node.ValueKind switch { - JsonValueKind.Number => element.GetSingle(), - JsonValueKind.String => element.GetString(), - JsonValueKind.Object => IsNotEmpty( element ), - JsonValueKind.Array => IsNotEmpty( element ), - JsonValueKind.True => true, - JsonValueKind.False => false, - JsonValueKind.Null => false, - JsonValueKind.Undefined => false, - _ => false + JsonValueKind.Number => new ValueType( node.GetSingle() ), + JsonValueKind.String => new ValueType( node.GetString() ), + JsonValueKind.Object or JsonValueKind.Array => new ValueType( IsNotEmpty( node ) ), + JsonValueKind.True => Constants.True, + JsonValueKind.False or JsonValueKind.Null or JsonValueKind.Undefined => Constants.False, + _ => Constants.False }; - static bool IsNotEmpty( JsonElement element ) + static bool IsNotEmpty( JsonElement node ) { - return element.ValueKind switch + return node.ValueKind switch { - JsonValueKind.Array => element.EnumerateArray().Any(), - JsonValueKind.Object => element.EnumerateObject().Any(), + JsonValueKind.Array => node.EnumerateArray().Any(), + JsonValueKind.Object => node.EnumerateObject().Any(), _ => false }; } diff --git a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs index dcffa35a..6ce777d2 100644 --- a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs @@ -1,5 +1,6 @@ using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors; @@ -15,6 +16,9 @@ public interface ITypeDescriptor : ITypeDescriptor public IValueAccessor Accessor { get; } public IFilterEvaluator FilterEvaluator { get; } + public INodeTypeComparer Comparer { get; } + bool CanUsePointer { get; } + public void Deconstruct( out IValueAccessor valueAccessor, out IFilterEvaluator filterEvaluator ) { valueAccessor = Accessor; diff --git a/src/Hyperbee.Json/Descriptors/IValueAccessor.cs b/src/Hyperbee.Json/Descriptors/IValueAccessor.cs index 2898343f..c390baaa 100644 --- a/src/Hyperbee.Json/Descriptors/IValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/IValueAccessor.cs @@ -3,11 +3,12 @@ public interface IValueAccessor { IEnumerable<(TNode, string, SelectorKind)> EnumerateChildren( TNode value, bool includeValues = true ); - TNode GetElementAt( in TNode value, int index ); + bool TryGetElementAt( in TNode value, int index, out TNode element ); NodeKind GetNodeKind( in TNode value ); int GetArrayLength( in TNode value ); - bool TryGetChildValue( in TNode value, string childSelector, out TNode childValue ); + bool TryGetChildValue( in TNode value, string childSelector, SelectorKind selectorKind, out TNode childValue ); bool TryParseNode( ReadOnlySpan item, out TNode value ); bool DeepEquals( TNode left, TNode right ); - bool TryGetValueFromNode( TNode item, out object o ); + bool TryGetValueFromNode( TNode item, out object value ); + bool TryGetFromPointer( in TNode value, JsonPathSegment segment, out TNode childValue ); } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs index 170f06cf..38480824 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs @@ -1,21 +1,25 @@ -using System.Linq.Expressions; +using System.Reflection; using System.Text.Json.Nodes; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Node.Functions; -public class CountNodeFunction() : FilterExtensionFunction( argumentCount: 1 ) +public class CountNodeFunction() : FilterExtensionFunction( CountMethodInfo, FilterExtensionInfo.MustCompare ) { public const string Name = "count"; - private static readonly Expression CountExpression = Expression.Constant( (Func, float>) Count ); + private static readonly MethodInfo CountMethodInfo = GetMethod( nameof( Count ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + public static INodeType Count( INodeType arg ) { - return Expression.Invoke( CountExpression, arguments[0] ); - } + if ( arg.Kind != NodeTypeKind.NodeList ) + throw new NotSupportedException( $"Function {Name} must be a node list." ); - public static float Count( IEnumerable nodes ) - { - return nodes.Count(); + var nodes = (NodesType) arg; + + if ( nodes.IsNormalized && !nodes.Any() ) + return new ValueType( 1 ); + + return new ValueType( nodes.Count() ); } } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs index 76341163..0f5792d7 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs @@ -1,29 +1,44 @@ -using System.Linq.Expressions; +using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Node.Functions; -public class LengthNodeFunction() : FilterExtensionFunction( argumentCount: 1 ) +public class LengthNodeFunction() : FilterExtensionFunction( LengthMethodInfo, FilterExtensionInfo.MustCompare | FilterExtensionInfo.ExpectNormalized ) { public const string Name = "length"; - private static readonly Expression LengthExpression = Expression.Constant( (Func, float>) Length ); + private static readonly MethodInfo LengthMethodInfo = GetMethod( nameof( Length ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + public static INodeType Length( INodeType input ) { - return Expression.Invoke( LengthExpression, arguments[0] ); + return input switch + { + NodesType nodes => LengthImpl( nodes.FirstOrDefault() ), + ValueType valueString => new ValueType( valueString.Value.Length ), + Null or Nothing => input, + _ => Constants.Nothing + }; } - public static float Length( IEnumerable nodes ) + public static INodeType LengthImpl( object value ) { - var node = nodes.FirstOrDefault(); - return node?.GetValueKind() switch + return value switch { - JsonValueKind.String => node.GetValue()?.Length ?? 0, - JsonValueKind.Array => node.AsArray().Count, - JsonValueKind.Object => node.AsObject().Count, - _ => 0 + string str => new ValueType( str.Length ), + Array array => new ValueType( array.Length ), + System.Collections.ICollection collection => new ValueType( collection.Count ), + System.Collections.IEnumerable enumerable => new ValueType( enumerable.Cast().Count() ), + JsonNode node => node.GetValueKind() switch + { + JsonValueKind.String => new ValueType( node.GetValue()?.Length ?? 0 ), + JsonValueKind.Array => new ValueType( node.AsArray().Count ), + JsonValueKind.Object => new ValueType( node.AsObject().Count ), + _ => Constants.Nothing + }, + _ => Constants.Nothing }; } + } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs index 6b259720..443d9fcb 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs @@ -1,30 +1,41 @@ -using System.Linq.Expressions; +using System.Reflection; +using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; +using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Node.Functions; -public class MatchNodeFunction() : FilterExtensionFunction( argumentCount: 2 ) +public class MatchNodeFunction() : FilterExtensionFunction( MatchMethodInfo, FilterExtensionInfo.MustNotCompare ) { public const string Name = "match"; - private static readonly Expression MatchExpression = Expression.Constant( (Func, string, bool>) Match ); + private static readonly MethodInfo MatchMethodInfo = GetMethod( nameof( Match ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + public static INodeType Match( INodeType input, INodeType regex ) { - return Expression.Invoke( MatchExpression, arguments[0], arguments[1] ); + return input switch + { + NodesType nodes when regex is ValueType stringValue => + MatchImpl( nodes, stringValue.Value ), + NodesType nodes when regex is NodesType stringValue => + MatchImpl( nodes, stringValue.Value.FirstOrDefault()?.GetValue() ), + _ => Constants.False + }; } - public static bool Match( IEnumerable nodes, string regex ) + private static INodeType MatchImpl( NodesType nodes, string regex ) { - var value = nodes.FirstOrDefault()?.GetValue(); + var value = nodes.FirstOrDefault(); - if ( value == null ) - { - return false; - } + if ( value?.GetValueKind() != JsonValueKind.String ) + return Constants.False; - var regexPattern = new Regex( regex.Trim( '\"', '\'' ) ); - return regexPattern.IsMatch( $"^{value}$" ); + var stringValue = value.GetValue(); + + var regexPattern = new Regex( $"^{IRegexp.ConvertToIRegexp( regex )}$" ); + return new ValueType( regexPattern.IsMatch( stringValue ) ); } } + diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs index 8f1f4e1a..40f26776 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs @@ -1,30 +1,38 @@ -using System.Linq.Expressions; +using System.Reflection; +using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; +using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Node.Functions; -public class SearchNodeFunction() : FilterExtensionFunction( argumentCount: 2 ) +public class SearchNodeFunction() : FilterExtensionFunction( SearchMethodInfo, FilterExtensionInfo.MustNotCompare ) { public const string Name = "search"; - private static readonly Expression SearchExpression = Expression.Constant( (Func, string, bool>) Search ); + private static readonly MethodInfo SearchMethodInfo = GetMethod( nameof( Search ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + public static INodeType Search( INodeType input, INodeType regex ) { - return Expression.Invoke( SearchExpression, arguments[0], arguments[1] ); + return input switch + { + NodesType nodes when regex is ValueType stringValue => + SearchImpl( nodes, stringValue.Value ), + NodesType nodes when regex is NodesType stringValue => + SearchImpl( nodes, stringValue.Value.FirstOrDefault()?.GetValue() ), + _ => Constants.False + }; } - public static bool Search( IEnumerable nodes, string regex ) + public static INodeType SearchImpl( NodesType nodes, string regex ) { - var value = nodes.FirstOrDefault()?.GetValue(); + var value = nodes.FirstOrDefault(); - if ( value == null ) - { - return false; - } + if ( value?.GetValueKind() != JsonValueKind.String ) + return Constants.False; - var regexPattern = new Regex( regex.Trim( '\"', '\'' ) ); - return regexPattern.IsMatch( value ); + var regexPattern = new Regex( IRegexp.ConvertToIRegexp( regex ) ); + return new ValueType( regexPattern.IsMatch( value.GetValue() ) ); } } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs index 94dde195..36ae42f6 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs @@ -1,37 +1,36 @@ -using System.Linq.Expressions; -using System.Numerics; +using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Node.Functions; -public class ValueNodeFunction() : FilterExtensionFunction( argumentCount: 1 ) +public class ValueNodeFunction() : FilterExtensionFunction( ValueMethodInfo, FilterExtensionInfo.MustCompare ) { public const string Name = "value"; - public static readonly Expression ValueExpression = Expression.Constant( (Func, object>) Value ); + private static readonly MethodInfo ValueMethodInfo = GetMethod( nameof( Value ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + public static INodeType Value( INodeType arg ) { - return Expression.Invoke( ValueExpression, arguments[0] ); - } + if ( arg.Kind != NodeTypeKind.NodeList ) + throw new NotSupportedException( $"Function {Name} does not support kind {arg.Kind}" ); - public static object Value( IEnumerable nodes ) - { - var node = nodes.FirstOrDefault(); + var nodeArray = ((NodesType) arg).ToArray(); + + if ( nodeArray.Length != 1 ) + return Constants.Nothing; + + var node = nodeArray.FirstOrDefault(); return node?.GetValueKind() switch { - JsonValueKind.Number => node.GetNumber(), - JsonValueKind.String => node.GetValue(), - JsonValueKind.Object => IsNotEmpty( node ), - JsonValueKind.Array => IsNotEmpty( node ), - JsonValueKind.True => true, - JsonValueKind.False => false, - JsonValueKind.Null => false, - JsonValueKind.Undefined => false, - _ => false + JsonValueKind.Number => new ValueType( node.GetValue() ), + JsonValueKind.String => new ValueType( node.GetValue() ), + JsonValueKind.Object or JsonValueKind.Array => new ValueType( IsNotEmpty( node ) ), + JsonValueKind.True => Constants.True, + JsonValueKind.False or JsonValueKind.Null or JsonValueKind.Undefined => Constants.False, + _ => Constants.False }; static bool IsNotEmpty( JsonNode node ) diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs index d73674ee..bdfd97b4 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs @@ -1,6 +1,7 @@ using System.Text.Json.Nodes; using Hyperbee.Json.Descriptors.Node.Functions; using Hyperbee.Json.Filters; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Node; @@ -8,6 +9,7 @@ public class NodeTypeDescriptor : ITypeDescriptor { private FilterEvaluator _evaluator; private NodeValueAccessor _accessor; + private NodeTypeComparer _comparer; public FunctionRegistry Functions { get; } = new(); @@ -17,6 +19,11 @@ public class NodeTypeDescriptor : ITypeDescriptor public IFilterEvaluator FilterEvaluator => _evaluator ??= new FilterEvaluator( this ); + public INodeTypeComparer Comparer => + _comparer ??= new NodeTypeComparer( Accessor ); + + public bool CanUsePointer => true; + public NodeTypeDescriptor() { Functions.Register( CountNodeFunction.Name, () => new CountNodeFunction() ); diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs index 9b50425f..852b0163 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; namespace Hyperbee.Json.Descriptors.Node; @@ -13,7 +14,6 @@ internal class NodeValueAccessor : IValueAccessor { case JsonArray arrayValue: for ( var index = arrayValue.Count - 1; index >= 0; index-- ) - { var child = arrayValue[index]; @@ -40,9 +40,19 @@ internal class NodeValueAccessor : IValueAccessor } [MethodImpl( MethodImplOptions.AggressiveInlining )] - public JsonNode GetElementAt( in JsonNode value, int index ) + public bool TryGetElementAt( in JsonNode value, int index, out JsonNode element ) { - return value[index]; + var array = (JsonArray) value; + element = null; + + if ( index < 0 ) // flip negative index to positive + index = array.Count + index; + + if ( index < 0 || index >= array.Count ) // out of bounds + return false; + + element = value[index]; + return true; } [MethodImpl( MethodImplOptions.AggressiveInlining )] @@ -65,7 +75,7 @@ public int GetArrayLength( in JsonNode value ) return 0; } - public bool TryGetChildValue( in JsonNode value, string childSelector, out JsonNode childValue ) + public bool TryGetChildValue( in JsonNode value, string childSelector, SelectorKind selectorKind, out JsonNode childValue ) { switch ( value ) { @@ -78,8 +88,14 @@ public bool TryGetChildValue( in JsonNode value, string childSelector, out JsonN } case JsonArray valueArray: { + if ( selectorKind == SelectorKind.Name ) + break; + if ( int.TryParse( childSelector, NumberStyles.Integer, CultureInfo.InvariantCulture, out var index ) ) { + if ( index < 0 ) // flip negative index to positive + index = valueArray.Count + index; + if ( index >= 0 && index < valueArray.Count ) { childValue = value[index]; @@ -161,4 +177,9 @@ public bool TryGetValueFromNode( JsonNode node, out object value ) return true; } + + public bool TryGetFromPointer( in JsonNode node, JsonPathSegment segment, out JsonNode childValue ) + { + return node.TryGetFromJsonPathPointer( segment, out childValue ); + } } diff --git a/src/Hyperbee.Json/Extensions/JsonHelper.cs b/src/Hyperbee.Json/Extensions/JsonHelper.cs index a40ffa8a..1de2809e 100644 --- a/src/Hyperbee.Json/Extensions/JsonHelper.cs +++ b/src/Hyperbee.Json/Extensions/JsonHelper.cs @@ -1,6 +1,4 @@ -using System.Buffers; -using System.Text; -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Nodes; using Hyperbee.Json.Dynamic; @@ -10,78 +8,7 @@ public static class JsonHelper { // conversion - public static ReadOnlySpan ConvertToBracketNotation( ReadOnlySpan path ) - { - var segments = JsonPathQueryParser.ParseNoCache( path ); - - var builder = new StringBuilder(); - - foreach ( var token in segments.AsEnumerable() ) - { - builder.Append( '[' ); - - foreach ( var selector in token.Selectors ) - { - switch ( selector.SelectorKind ) - { - case SelectorKind.Root: - builder.Append( "'$'" ); - break; - case SelectorKind.DotName: - case SelectorKind.Name: - builder.Append( $"'{selector.Value}'" ); - break; - case SelectorKind.Wildcard: - builder.Append( '*' ); - break; - case SelectorKind.Descendant: - builder.Append( ".." ); - break; - case SelectorKind.Slice: - case SelectorKind.Filter: - case SelectorKind.Index: - builder.Append( selector.Value ); - break; - - case SelectorKind.Undefined: - default: - throw new NotSupportedException( $"Unsupported {nameof( SelectorKind )}." ); - } - } - - builder.Append( ']' ); - } - - return builder.ToString(); - } - public static dynamic ConvertToDynamic( JsonNode value ) => new DynamicJsonNode( ref value ); public static dynamic ConvertToDynamic( JsonElement value, string path = null ) => new DynamicJsonElement( ref value, path ); public static dynamic ConvertToDynamic( JsonDocument value ) => ConvertToDynamic( value.RootElement, "$" ); - - public static T ConvertToObject( JsonElement value, JsonSerializerOptions options = null ) - where T : new() - { - var bufferWriter = new ArrayBufferWriter(); - using var writer = new Utf8JsonWriter( bufferWriter ); - - value.WriteTo( writer ); - writer.Flush(); - - var reader = new Utf8JsonReader( bufferWriter.WrittenSpan ); - return JsonSerializer.Deserialize( ref reader, options ); - } - - public static T ConvertToObject( JsonNode value, JsonSerializerOptions options = null ) - where T : new() - { - var bufferWriter = new ArrayBufferWriter(); - using var writer = new Utf8JsonWriter( bufferWriter ); - - value.WriteTo( writer ); - writer.Flush(); - - var reader = new Utf8JsonReader( bufferWriter.WrittenSpan ); - return JsonSerializer.Deserialize( ref reader, options ); - } } diff --git a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs index a45d90d4..a088c5a3 100644 --- a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs +++ b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs @@ -10,239 +10,132 @@ namespace Hyperbee.Json.Extensions; // syntax supports absolute paths; dotted notation, quoted names, and simple bracketed array accessors only. // // Json path style wildcard '*', '..', and '[a,b]' multi-result selector notations are NOT supported. -// -// examples: -// $.prop1.prop2 -// $.prop1[0] -// $.prop1[0].prop2 -// $.prop1['prop.2'] -// -// also supports quoted member-name for dot child -// -// $.'prop.2' -// $.prop1.'prop.2'[0].prop3 public static class JsonPathPointerExtensions { public static JsonElement FromJsonPathPointer( this JsonElement jsonElement, ReadOnlySpan pointer ) { - if ( IsNullOrUndefined( jsonElement ) || pointer.IsEmpty ) - return default; - - var splitter = new JsonPathPointerSplitter( pointer ); - - while ( splitter.TryMoveNext( out var name ) ) - { - if ( jsonElement.ValueKind == JsonValueKind.Array && int.TryParse( name, out var index ) ) - { - jsonElement = jsonElement.EnumerateArray().ElementAtOrDefault( index ); - continue; - } - - jsonElement = jsonElement.TryGetProperty( name!, out var value ) ? value : default; - - if ( IsNullOrUndefined( jsonElement ) ) - return default; - } - - return jsonElement; + var query = JsonPathQueryParser.Parse( pointer ); + var segment = query.Segments.Next; // skip the root segment - static bool IsNullOrUndefined( JsonElement value ) => value.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined; + return TryGetFromJsonPathPointer( jsonElement, segment, out var value ) ? value : default; } - public static JsonNode FromJsonPathPointer( this JsonNode jsonNode, ReadOnlySpan pointer ) + internal static bool TryGetFromJsonPathPointer( this JsonElement jsonElement, JsonPathSegment segment, out JsonElement value ) { - if ( jsonNode == null || pointer.IsEmpty ) - return default; + if ( !segment.IsNormalized ) + throw new NotSupportedException( "Unsupported JsonPath pointer query format." ); - var splitter = new JsonPathPointerSplitter( pointer ); + var current = jsonElement; + value = default; - while ( splitter.TryMoveNext( out var name ) ) + while ( !segment.IsFinal ) { - if ( jsonNode is JsonArray valueArray && int.TryParse( name, out var index ) ) + var (selectorValue, selectorKind) = segment.Selectors[0]; + + switch ( selectorKind ) { - jsonNode = valueArray[index]; + case SelectorKind.Name: + { + if ( current.ValueKind != JsonValueKind.Object ) + return false; - if ( jsonNode == null ) - return default; + if ( !current.TryGetProperty( selectorValue, out var child ) ) + return false; - continue; - } + current = child; + break; + } - jsonNode = jsonNode.AsObject().TryGetPropertyValue( name!.ToString(), out var value ) ? value : default; + case SelectorKind.Index: + { + if ( current.ValueKind != JsonValueKind.Array ) + return false; - if ( jsonNode == null ) - return default; - } + var length = current.GetArrayLength(); + var index = int.Parse( selectorValue ); - return jsonNode; - } + if ( index < 0 ) + index = length + index; - private ref struct JsonPathPointerSplitter //TODO Support escaping of \' and bracket counting in literals. Add to unit tests. - { - // zero allocation helper that splits a json path in to parts + if ( index < 0 || index >= length ) + return false; - // this splitter only works on simple property 'keys' it does not work - // with complex selectors ( '..', '*', '[a,b,c]' ). + current = current[index]; + break; + } - private ReadOnlySpan _span; - private Scanner _scanner; + default: + throw new NotSupportedException( $"Unsupported {nameof( SelectorKind )}." ); + } - private enum Scanner - { - Default, - Quoted, - Bracket, - Trailing + segment = segment.Next; } - private enum SpanAction - { - ReadNext, - TruncateLeadingCharacter, - YieldIdentifier - } + value = current; + return true; + } - private enum BracketContent - { - Undefined, - Quoted, - Number - } + public static JsonNode FromJsonPathPointer( this JsonNode jsonNode, ReadOnlySpan pointer ) + { + var query = JsonPathQueryParser.Parse( pointer ); + var segment = query.Segments.Next; // skip the root segment - internal JsonPathPointerSplitter( ReadOnlySpan span ) - { - if ( !span.StartsWith( "$" ) ) - throw new NotSupportedException( "Path must start with `$`." ); + return TryGetFromJsonPathPointer( jsonNode, segment, out var value ) ? value : default; + } - span = span.StartsWith( "$." ) ? span[2..] : span[1..]; // eat the leading $ + public static bool TryGetFromJsonPathPointer( this JsonNode jsonNode, JsonPathSegment segment, out JsonNode value ) + { + if ( !segment.IsNormalized ) + throw new NotSupportedException( "Unsupported JsonPath pointer query format." ); - _span = span; - _scanner = Scanner.Default; - } + var current = jsonNode; + value = default; - private void TakeIdentifier( int i, out ReadOnlySpan identifier ) + while ( !segment.IsFinal ) { - identifier = i > 0 ? _span[..i].Trim( '\'' ) : default; - _span = _span[Math.Min( i + 1, _span.Length )..]; - } + var (selectorValue, selectorKind) = segment.Selectors[0]; - // ReSharper disable once RedundantAssignment - private void TakeLeadingCharacter( ref int i ) - { - _span = _span[1..]; - i = 0; - } + switch ( selectorKind ) + { + case SelectorKind.Name: + { + if ( current is not JsonObject jsonObject ) + return false; - public bool TryMoveNext( out ReadOnlySpan identifier ) - { - identifier = default; - var i = 0; + if ( !jsonObject.TryGetPropertyValue( selectorValue, out var child ) ) + return false; - var bracketContent = BracketContent.Undefined; + current = child; + break; + } - do - { - if ( _span.IsEmpty || i >= _span.Length ) - return false; - - var c = _span[i]; - var action = SpanAction.ReadNext; - - switch ( _scanner ) - { - case Scanner.Default: - switch ( c ) - { - case '\'': - _scanner = Scanner.Quoted; - break; - case '[': - _scanner = Scanner.Bracket; - action = SpanAction.YieldIdentifier; - break; - case '.': - action = SpanAction.YieldIdentifier; - break; - case ' ': - case '\t': - case ']': - case '$' when i > 0: - throw new JsonException( $"Invalid character '{c}' at pos {i}." ); - default: - if ( i + 1 == _span.Length ) // take if at the end - { - i++; // capture the final character - action = SpanAction.YieldIdentifier; - } - - break; - } + case SelectorKind.Index: + { + if ( current is not JsonArray jsonArray ) + return false; - break; - case Scanner.Quoted: - switch ( c ) - { - case '\'': - _scanner = Scanner.Trailing; - action = SpanAction.YieldIdentifier; - break; - } + var length = jsonArray.Count; + var index = int.Parse( selectorValue ); - break; - case Scanner.Bracket: - switch ( c ) - { - case ']': - _scanner = Scanner.Trailing; - action = SpanAction.YieldIdentifier; - break; - case var _ when bracketContent == BracketContent.Undefined: - if ( c == '\'' ) - bracketContent = BracketContent.Quoted; - else if ( char.IsNumber( c ) ) - bracketContent = BracketContent.Number; - else - throw new JsonException( $"Invalid character '{c}' in bracket at pos {i}." ); - break; - case var _ when bracketContent == BracketContent.Number && !char.IsNumber( c ): - throw new JsonException( $"Invalid non-numeric {c}' in bracket at pos {i}." ); - } + if ( index < 0 ) + index = length + index; - break; - case Scanner.Trailing: - switch ( c ) - { - case '[': - _scanner = Scanner.Bracket; - break; - case '.': - _scanner = Scanner.Default; - break; - default: - throw new JsonException( $"Invalid character '{c}' after identifier at pos {i}." ); - } - - action = SpanAction.TruncateLeadingCharacter; - break; - } + if ( index < 0 || index >= length ) + return false; - switch ( action ) - { - case SpanAction.ReadNext: - i++; - break; - case SpanAction.TruncateLeadingCharacter: - TakeLeadingCharacter( ref i ); + current = jsonArray[index]; break; - case SpanAction.YieldIdentifier: - TakeIdentifier( i, out identifier ); - break; - } + } - } while ( identifier.IsEmpty ); + default: + throw new NotSupportedException( $"Unsupported {nameof( SelectorKind )}." ); + } - return true; + segment = segment.Next; } + + value = current; + return true; } } diff --git a/src/Hyperbee.Json/Filters/FilterEvaluator.cs b/src/Hyperbee.Json/Filters/FilterEvaluator.cs index 4021d465..146b6c4f 100644 --- a/src/Hyperbee.Json/Filters/FilterEvaluator.cs +++ b/src/Hyperbee.Json/Filters/FilterEvaluator.cs @@ -7,7 +7,7 @@ namespace Hyperbee.Json.Filters; public sealed class FilterEvaluator : IFilterEvaluator { - private static readonly ConcurrentDictionary> Compiled = new(); + private static readonly ConcurrentDictionary, bool>> Compiled = new(); private readonly ITypeDescriptor _typeDescriptor; @@ -16,17 +16,23 @@ public FilterEvaluator( ITypeDescriptor typeDescriptor ) _typeDescriptor = typeDescriptor; } - public object Evaluate( string filter, TNode current, TNode root ) + public bool Evaluate( string filter, TNode current, TNode root ) { + // Feature: split type descriptor into design/parse and runtime. (functions and json parsing are design time) var compiled = Compiled.GetOrAdd( filter, _ => FilterParser.Compile( filter, _typeDescriptor ) ); try { - return compiled( current, root ); + var runtimeContext = new FilterRuntimeContext( current, root, _typeDescriptor ); + return compiled( runtimeContext ); } catch ( RuntimeBinderException ) { - return null; // missing members should act falsy + return false; // missing members should act falsy + } + catch ( NotSupportedException ) + { + throw; } catch ( Exception ex ) { diff --git a/src/Hyperbee.Json/Filters/IFilterEvaluator.cs b/src/Hyperbee.Json/Filters/IFilterEvaluator.cs index 15a15999..a2ca7dec 100644 --- a/src/Hyperbee.Json/Filters/IFilterEvaluator.cs +++ b/src/Hyperbee.Json/Filters/IFilterEvaluator.cs @@ -3,5 +3,5 @@ namespace Hyperbee.Json.Filters; public interface IFilterEvaluator { - public object Evaluate( string filter, TNode current, TNode root ); + public bool Evaluate( string filter, TNode current, TNode root ); } diff --git a/src/Hyperbee.Json/Filters/IRegexp.cs b/src/Hyperbee.Json/Filters/IRegexp.cs new file mode 100644 index 00000000..26c66468 --- /dev/null +++ b/src/Hyperbee.Json/Filters/IRegexp.cs @@ -0,0 +1,115 @@ +namespace Hyperbee.Json.Filters; + +public static class IRegexp +{ + public static string ConvertToIRegexp( ReadOnlySpan pattern ) + { + // RFC-9535 States that regular expressions must conform to the I-Regexp format (RFC-9485)​. + // + // This requirement impacts DotNet regex for the dot( . ) character and treatment of Surrogate Pairs. + // + // I-Regexp, addresses the expectation for the dot (.) character in regular expressions: + // The dot( . ) character should match any character except newline characters, including + // surrogate pairs, which are treated as single characters in the context of matching. + // + // Surrogate pairs are used to represent characters outside the BMP (Basic Multilingual Plane) + // in UTF-16 encoding. They consist of a high surrogate (D800-DBFF) and a low surrogate (DC00-DFFF), + // which are combined to represent a single character. DotNet does not handle surrogate pairs nicely. + // + // Further, DotNet regex does not match dot( . ) on `\r`, which is an expectation of the RFC-9535 + // compliance test suite. + // + // To address this, we need to rewrite the regex pattern to match the dot( . ) character as expected. + + // stackalloc a span to track positions for replacement + + if ( pattern.IsEmpty ) + return string.Empty; + + var patternSize = pattern.Length; + Span dotPositions = patternSize > 256 + ? new bool[patternSize] + : stackalloc bool[patternSize]; + + var inCharacterClass = false; + var dotCount = 0; + + for ( var i = 0; i < pattern.Length; i++ ) + { + var currentChar = pattern[i]; + + switch ( currentChar ) + { + case '\\': + i++; + break; + case '[': + inCharacterClass = true; + break; + case ']' when inCharacterClass: + inCharacterClass = false; + break; + case '.' when !inCharacterClass: + dotPositions[i] = true; + dotCount++; + break; + } + } + + if ( dotCount == 0 ) + return pattern.ToString(); + + /* + * Regex Rewrite Explanation: + * + * 1. Non-Capturing Group `(?: ... )` + * - The entire pattern is wrapped in a non-capturing group to group the regex parts together + * without capturing the matched text. + * + * 2. Negative Lookahead `(?! ... )` + * - `(?[\r\n])`: Match any character that is not a carriage return (`\r`) or newline (`\n`). + * + * 3. Surrogate Pair `\p{Cs}\p{Cs}` + * - `\p{Cs}`: Matches any character in the "Cs" (surrogate) Unicode category. + * - `\p{Cs}\p{Cs}`: Matches a surrogate pair, which consists of two surrogate characters in sequence. + * + * Overall Pattern: + * - The pattern matches either: + * 1. Any character that is not a newline (`\r` or `\n`), or + * 2. A surrogate pair (two surrogate characters in sequence). + * + * This ensures that the regex matches any character except newline and carriage return characters, + * while correctly handling surrogate pairs which are necessary for certain Unicode characters. + * + * Pattern: + * (?: + * (?[^\r\n]) # Match any character except \r and \n + * | + * \p{Cs}\p{Cs} # Match a surrogate pair (two surrogates in sequence) + * ) + */ + var replacement = @"(?:[^\r\n]|\p{Cs}\p{Cs})".AsSpan(); // (?:(?![\r\n])\P{Cs}|\p{Cs}\p{Cs}) + + var newSize = pattern.Length + dotCount * (replacement.Length - 1); // '.' is 1 char, so extra (pattern-length - 1) chars per '.' + Span buffer = newSize > 512 + ? new char[newSize] + : stackalloc char[newSize]; + + var bufferIndex = 0; + + for ( var i = 0; i < pattern.Length; i++ ) + { + if ( dotPositions[i] ) + { + replacement.CopyTo( buffer[bufferIndex..] ); + bufferIndex += replacement.Length; + } + else + { + buffer[bufferIndex++] = pattern[i]; + } + } + + return new string( buffer[..bufferIndex] ); + } +} diff --git a/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs b/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs new file mode 100644 index 00000000..0d494ad5 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs @@ -0,0 +1,7 @@ +namespace Hyperbee.Json.Filters.Parser; + +internal class ExpressionInfo +{ + public ExpressionKind Kind { get; set; } + public FilterExtensionInfo FunctionInfo { get; set; } +} diff --git a/src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs b/src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs new file mode 100644 index 00000000..9231d05d --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs @@ -0,0 +1,13 @@ +namespace Hyperbee.Json.Filters.Parser; + +internal enum ExpressionKind +{ + Unspecified, + Function, + Json, + Literal, + Not, + Paren, + Select, + Merged +} diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/ComparerExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/ComparerExpressionFactory.cs deleted file mode 100644 index e296e135..00000000 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/ComparerExpressionFactory.cs +++ /dev/null @@ -1,246 +0,0 @@ -using System.Diagnostics; -using System.Linq.Expressions; -using Hyperbee.Json.Descriptors; - -namespace Hyperbee.Json.Filters.Parser.Expressions; - -public static class ComparerExpressionFactory -{ - // ReSharper disable once StaticMemberInGenericType - private static readonly ConstantExpression CreateComparandExpression; - - static ComparerExpressionFactory() - { - // Pre-compile the delegate to call the Comparand constructor - - var accessorParam = Expression.Parameter( typeof( IValueAccessor ), "accessor" ); - var valueParam = Expression.Parameter( typeof( object ), "value" ); - - var constructorInfo = typeof( Comparand ).GetConstructor( [typeof( IValueAccessor ), typeof( object )] ); - var newExpression = Expression.New( constructorInfo!, accessorParam, valueParam ); - - var creator = Expression.Lambda, object, Comparand>>( - newExpression, accessorParam, valueParam ).Compile(); - - CreateComparandExpression = Expression.Constant( creator ); - } - - public static Expression GetComparand( IValueAccessor accessor, Expression expression ) - { - // Handles Not operator since it maybe not have a left side. - if ( expression == null ) - return null; - - // Create an expression representing the instance of the accessor - var accessorExpression = Expression.Constant( accessor ); - - // Use the compiled delegate to create an expression to call the Comparand constructor - return Expression.Invoke( CreateComparandExpression, accessorExpression, - Expression.Convert( expression, typeof( object ) ) ); - } - - [DebuggerDisplay( "Value = {Value}" )] - public readonly struct Comparand( IValueAccessor accessor, object value ) : IComparable, IEquatable - { - private const float Tolerance = 1e-6F; // Define a tolerance for float comparisons - - private IValueAccessor Accessor { get; } = accessor; - - private object Value { get; } = value; - - public int CompareTo( Comparand other ) => Compare( this, other ); - public bool Equals( Comparand other ) => Compare( this, other ) == 0; - public override bool Equals( object obj ) => obj is Comparand other && Equals( other ); - - public static bool operator ==( Comparand left, Comparand right ) => Compare( left, right ) == 0; - public static bool operator !=( Comparand left, Comparand right ) => Compare( left, right ) != 0; - public static bool operator <( Comparand left, Comparand right ) => Compare( left, right, lessThan: true ) < 0; - public static bool operator >( Comparand left, Comparand right ) => Compare( left, right ) > 0; - public static bool operator <=( Comparand left, Comparand right ) => Compare( left, right, lessThan: true ) <= 0; - public static bool operator >=( Comparand left, Comparand right ) => Compare( left, right ) >= 0; - - public override int GetHashCode() - { - if ( Value == null ) - return 0; - - var valueHash = Value switch - { - IConvertible convertible => convertible.GetHashCode(), - IEnumerable enumerable => enumerable.GetHashCode(), - _ => Value.GetHashCode() - }; - - return HashCode.Combine( Value.GetType().GetHashCode(), valueHash ); - } - - /* - * Comparison Rules (according to JSONPath RFC 9535): - * - * 1. Compare Value to Value: - * - Two values are equal if they are of the same type and have the same value. - * - For float comparisons, use a tolerance to handle precision issues. - * - Comparisons between different types yield false. - * - * 2. Compare Node to Node: - * - Since a Node is essentially an enumerable with a single item, compare the single items directly. - * - Apply the same value comparison rules to the single items. - * - * 3. Compare NodeList to NodeList: - * - Two NodeLists are equal if they are sequence equal. - * - Sequence equality should consider deep equality of Node items. - * - Return 0 if sequences are equal. - * - Return -1 if the left sequence is less. - * - Return 1 if the left sequence is greater. - * - * 4. Compare NodeList to Value: - * - A NodeList is equal to a value if any node in the NodeList matches the value. - * - Return 0 if any node matches the value. - * - Return -1 if the value is less than all nodes. - * - Return 1 if the value is greater than all nodes. - * - * 5. Compare Value to NodeList: - * - Similar to the above, true if the value is found in the NodeList. - * - * 6. Compare Node to NodeList and vice versa: - * - Since Node is a single item enumerable, treat it similarly to Value in comparison to NodeList. - * - * 7. Truthiness Rules: - * - Falsy values: null, false, 0, "", NaN. - * - Truthy values: Anything not falsy, including non-empty strings, non-zero numbers, true, arrays, and objects. - * - Truthiness is generally not used for comparison operators (==, <) in filter expressions. - * - Type mismatches (e.g., string vs. number) result in false for equality (==) and true for inequality (!=). - * - * Order of Operations: - * - Check if both are NodeLists. - * - Check if one is a NodeList and the other is a Value. - * - Compare directly if both are Values. - */ - private static int Compare( Comparand left, Comparand right, bool lessThan = false ) - { - if ( left.Value is IEnumerable leftEnumerable && right.Value is IEnumerable rightEnumerable ) - { - return CompareEnumerables( left.Accessor, leftEnumerable, rightEnumerable ); - } - - if ( left.Value is IEnumerable leftEnumerable1 ) - { - var compare = CompareEnumerableToValue( left.Accessor, leftEnumerable1, right.Value, out var nodeCount ); - return NormalizeResult( compare, nodeCount, lessThan ); - } - - if ( right.Value is IEnumerable rightEnumerable1 ) - { - var compare = CompareEnumerableToValue( left.Accessor, rightEnumerable1, left.Value, out var nodeCount ); - return NormalizeResult( compare, nodeCount, lessThan ); - } - - return CompareValues( left.Value, right.Value ); - - static int NormalizeResult( int compare, int nodeCount, bool lessThan ) - { - // When comparing a NodeList to a Value, '<' and '>' type operators only have meaning when the - // NodeList has a single node. - // - // 1. When there is a single node, the comparison is based on the unwrapped node value. - // This results in a meaningful value to value comparison for equality, and greater-than and - // less-than operations. - // - // 2. When there is more than on node, or an empty node list, equality is based on finding the - // value in the set of nodes. The result is true if the value is found in the set, and false - // otherwise. - // - // In this case, the result is not meaningful for greater-than and less-than operations, since - // the comparison is based on the set of nodes, and not on two single values. - // - // However, the comparison result will still be used in the context of a greater-than or less-than - // operation, which will yield indeterminate results based on the left or right order of operands. - // To handle this, we need to normalize the result of the comparison. In this case, we want to - // normalize the result so that greater-than and less-than always return false, regardless of the - // left or right order of the comparands. - - if ( lessThan && nodeCount != 1 ) // Test for an empty or multi-node set - { - // invert the comparison result to make sure less-than and greater-than return false - return -compare; - } - - return compare; - } - } - - private static int CompareEnumerables( IValueAccessor accessor, IEnumerable left, IEnumerable right ) - { - using var leftEnumerator = left.GetEnumerator(); - using var rightEnumerator = right.GetEnumerator(); - - while ( leftEnumerator.MoveNext() ) - { - if ( !rightEnumerator.MoveNext() ) - return 1; // Left has more elements, so it is greater - - if ( !accessor.DeepEquals( leftEnumerator.Current, rightEnumerator.Current ) ) - return -1; // Elements are not deeply equal - } - - if ( rightEnumerator.MoveNext() ) - return -1; // Right has more elements, so left is less - - return 0; // Sequences are equal - } - - private static int CompareEnumerableToValue( IValueAccessor accessor, IEnumerable enumeration, object value, out int nodeCount ) - { - nodeCount = 0; - var lastCompare = -1; - - foreach ( var item in enumeration ) - { - nodeCount++; - - if ( !accessor.TryGetValueFromNode( item, out var itemValue ) ) - continue; // Skip if value cannot be extracted - - lastCompare = CompareValues( itemValue, value ); - - if ( lastCompare == 0 ) - return 0; // Return 0 if any node matches the value - } - - if ( nodeCount == 0 ) - return -1; // Return -1 if the list is empty (no nodes match the value) - - return nodeCount != 1 ? -1 : lastCompare; // Return the last comparison if there is only one node - } - - private static int CompareValues( object left, object right ) - { - if ( left == null && right == null ) - { - return 0; - } - - if ( left?.GetType() != right?.GetType() ) - { - return -1; - } - - if ( left is string leftString && right is string rightString ) - { - return string.Compare( leftString, rightString, StringComparison.Ordinal ); - } - - if ( left is bool leftBool && right is bool rightBool ) - { - return leftBool.CompareTo( rightBool ); - } - - if ( left is float leftFloat && right is float rightFloat ) - { - return Math.Abs( leftFloat - rightFloat ) < Tolerance ? 0 : leftFloat.CompareTo( rightFloat ); - } - - return Comparer.Default.Compare( left, right ); - } - } -} diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs index 9b927088..8d8cfc62 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs @@ -4,13 +4,20 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class FunctionExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, FilterContext context ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) { - if ( context.Descriptor.Functions.TryGetCreator( state.Item.ToString(), out var functionCreator ) ) + if ( parserContext.Descriptor.Functions.TryGetCreator( state.Item.ToString(), out var functionCreator ) ) { - expression = functionCreator() - .GetExpression( ref state, context ); // will recurse for each function argument. + if ( state.TrailingWhitespace ) + throw new NotSupportedException( "Whitespace is not allowed after a function name." ); + var function = functionCreator(); + + expression = function + .GetExpression( ref state, parserContext ); // will recurse for each function argument. + + expressionInfo.Kind = ExpressionKind.Function; + expressionInfo.FunctionInfo = function.FunctionInfo; return true; } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs index 6f13ac97..f7535ad1 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs @@ -4,5 +4,5 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal interface IExpressionFactory { - static abstract bool TryGetExpression( ref ParserState state, out Expression expression, FilterContext context ); + static abstract bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ); } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs index 9584ce16..846587e0 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs @@ -1,14 +1,16 @@ using System.Linq.Expressions; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser.Expressions; internal class JsonExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, FilterContext context ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) { - if ( context.Descriptor.Accessor.TryParseNode( state.Item.ToString(), out var node ) ) + if ( parserContext.Descriptor.Accessor.TryParseNode( state.Item.ToString(), out var node ) ) { - expression = Expression.Constant( new[] { node } ); + expression = Expression.Constant( new NodesType( [node], isNormalized: true ) ); + expressionInfo.Kind = ExpressionKind.Json; return true; } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs index ac037998..b3f1c538 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs @@ -1,40 +1,50 @@ using System.Linq.Expressions; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser.Expressions; internal class LiteralExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, FilterContext context ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) { - expression = GetLiteralExpression( state.Item, context ); - return expression != null; + expression = GetLiteralExpression( state.Item ); + + if ( expression == null ) + return false; + + expressionInfo.Kind = ExpressionKind.Literal; + return true; + } - private static ConstantExpression GetLiteralExpression( ReadOnlySpan item, FilterContext context ) + private static ConstantExpression GetLiteralExpression( ReadOnlySpan item ) { // Check for known literals (true, false, null) first if ( item.Equals( "true", StringComparison.OrdinalIgnoreCase ) ) - return Expression.Constant( true ); + return Expression.Constant( Constants.True ); if ( item.Equals( "false", StringComparison.OrdinalIgnoreCase ) ) - return Expression.Constant( false ); + return Expression.Constant( Constants.False ); if ( item.Equals( "null", StringComparison.OrdinalIgnoreCase ) ) - return Expression.Constant( null ); + return Expression.Constant( Constants.Null ); // Check for quoted strings if ( item.Length >= 2 && (item[0] == '"' && item[^1] == '"' || item[0] == '\'' && item[^1] == '\'') ) - return Expression.Constant( item[1..^1].ToString() ); // remove quotes + return Expression.Constant( new ValueType( item[1..^1].ToString() ) ); // remove quotes // Check for numbers // // The current design treats all numbers are floats since we don't // know what's in the data or the other side of the operator yet. + if ( item.Length > 0 && item[^1] == '.' ) // incomplete floating-point number. we can parse it but the RFC doesn't like it. + throw new NotSupportedException( $"Incomplete floating-point number `{item.ToString()}`" ); + return float.TryParse( item, out float result ) - ? Expression.Constant( result ) + ? Expression.Constant( new ValueType( result ) ) : null; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs index 55afe51f..72594ee5 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs @@ -4,9 +4,14 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class NotExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, FilterContext context ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) { expression = null; - return state.Operator == Operator.Not; + + if ( state.Operator != Operator.Not ) + return false; + + expressionInfo.Kind = ExpressionKind.Not; + return true; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs index b6f8de67..ea6e671d 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs @@ -4,12 +4,17 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class ParenExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, FilterContext context ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) { if ( state.Operator == Operator.OpenParen && state.Item.IsEmpty ) { - var localState = state with { Terminal = FilterParser.EndArg }; - expression = FilterParser.Parse( ref localState, context ); // will recurse. + var localState = state with + { + Terminal = FilterParser.ArgClose + }; + + expression = FilterParser.Parse( ref localState, parserContext ); // will recurse. + expressionInfo.Kind = ExpressionKind.Paren; return true; } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs index 195545d5..7220ee9a 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs @@ -1,20 +1,26 @@ using System.Linq.Expressions; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser.Expressions; internal class SelectExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, FilterContext context ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo itemContext, FilterParserContext parserContext ) { - expression = ExpressionHelper.GetExpression( state.Item, context ); - return expression != null; + expression = ExpressionHelper.GetExpression( state.Item, state.IsArgument, parserContext ); + + if ( expression == null ) + return false; + + itemContext.Kind = ExpressionKind.Select; + return true; } static class ExpressionHelper { - private static readonly Expression SelectExpression = Expression.Constant( (Func>) Select ); + private static readonly Expression SelectExpression = Expression.Constant( (Func, INodeType>) Select ); - public static Expression GetExpression( ReadOnlySpan item, FilterContext context ) + public static Expression GetExpression( ReadOnlySpan item, bool allowDotWhitespace, FilterParserContext parserContext ) { if ( item.IsEmpty ) return null; @@ -22,17 +28,22 @@ public static Expression GetExpression( ReadOnlySpan item, FilterContext Select( TNode current, TNode root, string query ) + private static INodeType Select( string query, bool allowDotWhitespace, FilterRuntimeContext runtimeContext ) { - return JsonPath.SelectInternal( current, root, query ); + var compileQuery = JsonPathQueryParser.Parse( query, allowDotWhitespace ); + + // Current becomes root + return query[0] == '$' + ? new NodesType( JsonPath.SelectInternal( runtimeContext.Root, runtimeContext.Root, compileQuery ), compileQuery.Normalized ) + : new NodesType( JsonPath.SelectInternal( runtimeContext.Current, runtimeContext.Root, compileQuery ), compileQuery.Normalized ); } + } } diff --git a/src/Hyperbee.Json/Filters/Parser/FilterContext.cs b/src/Hyperbee.Json/Filters/Parser/FilterContext.cs deleted file mode 100644 index e7d4ca8f..00000000 --- a/src/Hyperbee.Json/Filters/Parser/FilterContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Json.Descriptors; - -namespace Hyperbee.Json.Filters.Parser; - -internal record FilterContext -{ - public FilterContext( ITypeDescriptor descriptor ) - { - Descriptor = descriptor; - } - - public ParameterExpression Current { get; init; } = Expression.Parameter( typeof( TNode ), "current" ); - public ParameterExpression Root { get; } = Expression.Parameter( typeof( TNode ), "root" ); - public ITypeDescriptor Descriptor { get; } -} diff --git a/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs b/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs index ab549e01..51efee57 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs @@ -1,21 +1,29 @@ using System.Linq.Expressions; +using System.Reflection; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser; public abstract class FilterExtensionFunction { private readonly int _argumentCount; + private readonly MethodInfo _methodInfo; + private MethodInfo _throwIfNotNormalizedMethodInfo; - protected FilterExtensionFunction( int argumentCount ) + public FilterExtensionInfo FunctionInfo { get; } + + protected FilterExtensionFunction( MethodInfo methodInfo, FilterExtensionInfo filterInfo ) { - _argumentCount = argumentCount; - } + _argumentCount = methodInfo.GetParameters().Length; + _methodInfo = methodInfo; - protected abstract Expression GetExtensionExpression( Expression[] arguments ); + FunctionInfo = filterInfo; + } - internal Expression GetExpression( ref ParserState state, FilterContext context ) + internal Expression GetExpression( ref ParserState state, FilterParserContext parserContext ) { var arguments = new Expression[_argumentCount]; + var expectNormalized = FunctionInfo.HasFlag( FilterExtensionInfo.ExpectNormalized ); for ( var i = 0; i < _argumentCount; i++ ) { @@ -23,16 +31,42 @@ internal Expression GetExpression( ref ParserState state, FilterContext.Parse( ref localState, context ); + if ( localState.EndOfBuffer ) + throw new NotSupportedException( $"Invalid arguments for filter: \"{state.Buffer}\"." ); + + var argument = FilterParser.Parse( ref localState, parserContext ); - arguments[i] = argument; + // Create expression that throws if not normalized. + if ( expectNormalized ) + { + _throwIfNotNormalizedMethodInfo ??= GetMethod( nameof( ThrowIfNotNormalized ) ) + .MakeGenericMethod( typeof( TNode ) ); + + arguments[i] = Expression.Call( _throwIfNotNormalizedMethodInfo, + Expression.Constant( _methodInfo.Name ), + Expression.Convert( argument, typeof( INodeType ) ) ); + } + else + { + arguments[i] = Expression.Convert( argument, typeof( INodeType ) ); + } } - return GetExtensionExpression( arguments ); + return Expression.Call( _methodInfo, arguments ); } -} + public static INodeType ThrowIfNotNormalized( string methodName, INodeType node ) + { + if ( node is NodesType { IsNormalized: false } ) + throw new NotSupportedException( $"Function {methodName} does not support non-singular arguments." ); + + return node; + } + + protected static MethodInfo GetMethod( string methodName ) => typeof( T ).GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ); +} diff --git a/src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs b/src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs new file mode 100644 index 00000000..394a6f9e --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs @@ -0,0 +1,9 @@ +namespace Hyperbee.Json.Filters.Parser; + +[Flags] +public enum FilterExtensionInfo +{ + MustCompare = 0x01, + MustNotCompare = 0x02, + ExpectNormalized = 0x10, +} diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index 2e9fd54d..4e0c0ba8 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -9,6 +9,7 @@ #endregion +using System.Diagnostics; using System.Linq.Expressions; using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Parser.Expressions; @@ -17,40 +18,43 @@ namespace Hyperbee.Json.Filters.Parser; public abstract class FilterParser { - public const char EndLine = '\n'; - public const char EndArg = ')'; - public const char ArgSeparator = ','; + public const char EndLine = '\0'; // use null instead of newline + public const char ArgClose = ')'; + public const char ArgComma = ','; } public class FilterParser : FilterParser { - public static Func Compile( ReadOnlySpan filter, ITypeDescriptor descriptor ) + public static Func, bool> Compile( ReadOnlySpan filter, ITypeDescriptor descriptor ) { - var context = new FilterContext( descriptor ); + var context = new FilterParserContext( descriptor ); var expression = Parse( filter, context ); - return Expression.Lambda>( expression, context.Current, context.Root ).Compile(); + return Expression.Lambda, bool>>( expression, context.RuntimeContext ).Compile(); } - internal static Expression Parse( ReadOnlySpan filter, FilterContext context ) + internal static Expression Parse( ReadOnlySpan filter, FilterParserContext parserContext ) { + filter = filter.Trim(); // remove leading and trailing whitespace to simplify parsing + var pos = 0; - var state = new ParserState( filter, [], ref pos, Operator.Nop, EndLine ); + var parenDepth = 0; + var state = new ParserState( filter, [], ref pos, ref parenDepth, Operator.NonOperator, EndLine ); - var expression = Parse( ref state, context ); + var expression = Parse( ref state, parserContext ); return FilterTruthyExpression.IsTruthyExpression( expression ); } - internal static Expression Parse( ref ParserState state, FilterContext context ) // recursion entrypoint + internal static Expression Parse( ref ParserState state, FilterParserContext parserContext ) // recursion entrypoint { // validate input - if ( context == null ) - throw new ArgumentNullException( nameof( context ) ); + if ( parserContext == null ) + throw new ArgumentNullException( nameof( parserContext ) ); - if ( state.EndOfBuffer || state.IsTerminal ) - throw new ArgumentException( $"Invalid filter: \"{state.Buffer}\".", nameof( state ) ); + if ( state.EndOfBuffer ) + throw new NotSupportedException( $"Invalid filter: \"{state.Buffer}\"." ); // parse the expression var items = new List(); @@ -58,52 +62,55 @@ internal static Expression Parse( ref ParserState state, FilterContext co do { MoveNext( ref state ); - items.Add( GetExprItem( ref state, context ) ); + items.Add( GetExprItem( ref state, parserContext ) ); // will recurse for nested expressions } while ( state.IsParsing ); - // advance to next character for recursive calls. - if ( !state.EndOfBuffer && state.IsTerminal ) - state.Pos++; + // check for paren mismatch + if ( state.EndOfBuffer && state.ParenDepth != 0 ) + throw new NotSupportedException( $"Unbalanced parenthesis in filter: \"{state.Buffer}\"." ); // merge the expressions var baseItem = items[0]; var index = 1; - return Merge( baseItem, ref index, items, context.Descriptor ); + return Merge( in state, baseItem, ref index, items, parserContext ); } - private static ExprItem GetExprItem( ref ParserState state, FilterContext context ) + + private static ExprItem GetExprItem( ref ParserState state, FilterParserContext parserContext ) { - if ( NotExpressionFactory.TryGetExpression( ref state, out var expression, context ) ) - return ExprItem( ref state, expression ); + var expressionInfo = new ExpressionInfo(); + + if ( NotExpressionFactory.TryGetExpression( ref state, out var expression, ref expressionInfo, parserContext ) ) + return ExprItem( ref state, expression, expressionInfo ); - if ( ParenExpressionFactory.TryGetExpression( ref state, out expression, context ) ) // will recurse. - return ExprItem( ref state, expression ); + if ( ParenExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) // will recurse. + return ExprItem( ref state, expression, expressionInfo ); - if ( SelectExpressionFactory.TryGetExpression( ref state, out expression, context ) ) - return ExprItem( ref state, expression ); + if ( SelectExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) + return ExprItem( ref state, expression, expressionInfo ); - if ( FunctionExpressionFactory.TryGetExpression( ref state, out expression, context ) ) // may recurse for each function argument. - return ExprItem( ref state, expression ); + if ( FunctionExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) // may recurse for each function argument. + return ExprItem( ref state, expression, expressionInfo ); - if ( LiteralExpressionFactory.TryGetExpression( ref state, out expression, context ) ) - return ExprItem( ref state, expression ); + if ( LiteralExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) + return ExprItem( ref state, expression, expressionInfo ); - if ( JsonExpressionFactory.TryGetExpression( ref state, out expression, context ) ) - return ExprItem( ref state, expression ); + if ( JsonExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) + return ExprItem( ref state, expression, expressionInfo ); throw new NotSupportedException( $"Unsupported literal: {state.Buffer.ToString()}" ); // Helper method to create an expression item - static ExprItem ExprItem( ref ParserState state, Expression expression ) + static ExprItem ExprItem( ref ParserState state, Expression expression, ExpressionInfo expressionInfo ) { - UpdateOperator( ref state ); - return new ExprItem( expression, state.Operator ); + MoveNextOperator( ref state ); // will set state.Operator + return new ExprItem( expression, state.Operator, expressionInfo ); } } - private static void MoveNext( ref ParserState state ) + private static void MoveNext( ref ParserState state ) // move to the next item { char? quote = null; @@ -114,7 +121,7 @@ private static void MoveNext( ref ParserState state ) // check for end of buffer if ( state.EndOfBuffer ) { - state.Operator = Operator.Nop; + state.Operator = Operator.NonOperator; state.Item = []; return; } @@ -125,37 +132,59 @@ private static void MoveNext( ref ParserState state ) while ( true ) { - itemEnd = state.Pos; // assign before the call to NextCharacter + itemEnd = state.Pos; // store before calling NextCharacter - NextCharacter( ref state, out var nextChar, ref quote ); + NextCharacter( ref state, out var nextChar, ref quote ); // will advance state.Pos - if ( IsFinished( state.Pos - itemStart, nextChar, state.Operator, state.Terminal ) ) + if ( IsFinished( in state, nextChar ) ) + { break; + } - if ( !state.EndOfBuffer && !state.IsTerminal ) - continue; - - itemEnd = state.Pos; // fall-through: include the terminal character - break; + if ( state.EndOfBuffer ) + { + itemEnd = state.Pos; // include the final character + break; + } } state.SetItem( itemStart, itemEnd ); + return; // Helper method to determine if item parsing is finished - static bool IsFinished( int count, char ch, Operator op, char terminal ) + static bool IsFinished( in ParserState state, char ch ) { - // order of operations matters here - if ( count == 0 && ch == EndArg ) + // order of operations matters + + if ( state.BracketDepth != 0 ) return false; - if ( op != Operator.Nop && op != Operator.ClosedParen ) + if ( state.Operator.IsNonOperator() == false ) return true; - if ( ch == terminal || ch == EndArg || ch == EndLine ) - return true; + return ch == state.Terminal; // terminal character [ '\0' or ',' or ')' ] + } + } + + private static void MoveNextOperator( ref ParserState state ) // move to the next operator + { + if ( state.Operator.IsLogical() || state.Operator.IsComparison() ) + { + return; + } + + if ( !state.IsParsing ) + { + state.Operator = Operator.NonOperator; + return; + } + + char? quoteChar = null; - return false; + while ( !(state.Operator.IsLogical() || state.Operator.IsComparison()) && !state.EndOfBuffer ) + { + NextCharacter( ref state, out _, ref quoteChar ); } } @@ -163,6 +192,21 @@ private static void NextCharacter( ref ParserState state, out char nextChar, ref { nextChar = state.Buffer[state.Pos++]; + // Handle escape characters within quotes + if ( quoteChar.HasValue ) + { + if ( nextChar == '\\' && state.Pos < state.Buffer.Length ) + { + nextChar = state.Buffer[state.Pos++]; + } + else if ( nextChar == quoteChar && (state.Pos <= 1 || state.Buffer[state.Pos - 2] != '\\') ) + { + quoteChar = null; // Exiting a quoted string + } + return; + } + + // Normal character handling switch ( nextChar ) { case '&' when Next( ref state, '&' ): @@ -193,20 +237,30 @@ private static void NextCharacter( ref ParserState state, out char nextChar, ref state.Operator = Operator.Not; break; case '(': + state.ParenDepth++; state.Operator = Operator.OpenParen; break; case ')': + state.ParenDepth--; state.Operator = Operator.ClosedParen; break; - case ' ' or '\t' when quoteChar == null: - state.Operator = Operator.Nop; + case ' ' or '\t' or '\r' or '\n': + state.Operator = Operator.Whitespace; + break; + case '[': + state.BracketDepth++; + state.Operator = Operator.Bracket; break; - case '\'' or '\"' when state.Pos > 0 && state.Previous != '\\': - quoteChar = quoteChar == null ? nextChar : null; - state.Operator = Operator.Nop; + case ']': + state.BracketDepth--; + state.Operator = Operator.Bracket; + break; + case '\'' or '\"': + quoteChar = nextChar; // Entering a quoted string + state.Operator = Operator.Quotes; break; default: - state.Operator = Operator.Nop; + state.Operator = Operator.Token; break; } @@ -215,7 +269,7 @@ private static void NextCharacter( ref ParserState state, out char nextChar, ref // Helper method to check if the next character is the expected character static bool Next( ref ParserState state, char expected ) { - if ( state.EndOfBuffer || state.Current != expected ) + if ( state.EndOfBuffer || state.Buffer[state.Pos] != expected ) return false; state.Pos++; @@ -223,167 +277,132 @@ static bool Next( ref ParserState state, char expected ) } } - private static void UpdateOperator( ref ParserState state ) + private static Expression Merge( in ParserState state, ExprItem left, ref int index, List items, FilterParserContext parserContext, bool mergeOneOnly = false ) { - if ( !IsParenOrNop( state.Operator ) ) - return; - - if ( state.EndOfBuffer ) - { - state.Operator = Operator.Nop; - return; - } - - if ( state.IsTerminal ) + if ( items.Count == 1 ) { - state.Operator = Operator.ClosedParen; - return; + ThrowIfInvalidComparison( in state, left, null ); // single item, no recursion } - - char? quoteChar = null; - var startPos = state.Pos; - - while ( IsParenOrNop( state.Operator ) && !state.EndOfBuffer ) + else { - NextCharacter( ref state, out _, ref quoteChar ); - } + while ( index < items.Count ) + { + var right = items[index++]; - if ( IsParen( state.Operator ) && state.Pos > startPos ) - { - state.Pos--; - } + while ( !CanMergeItems( left, right ) ) + { + Merge( in state, right, ref index, items, parserContext, mergeOneOnly: true ); // recursive call - right becomes left + } - return; + ThrowIfInvalidComparison( in state, left, right ); - // Helper method to determine if an operator is a parenthesis or a no-op - static bool IsParenOrNop( Operator op ) => op is Operator.OpenParen or Operator.ClosedParen or Operator.Nop; - static bool IsParen( Operator op ) => op is Operator.OpenParen or Operator.ClosedParen; - } + MergeItems( left, right, parserContext ); - private static Expression Merge( ExprItem current, ref int index, List items, ITypeDescriptor descriptor, bool mergeOneOnly = false ) - { - while ( index < items.Count ) - { - var next = items[index++]; - - while ( !CanMergeItems( current, next ) ) - { - Merge( next, ref index, items, descriptor, mergeOneOnly: true ); // recursive call + if ( mergeOneOnly ) + return left.Expression; } - - MergeItems( current, next, descriptor ); - - if ( mergeOneOnly ) - return current.Expression; } - return current.Expression; + return left.Expression; // Helper method to determine if two items can be merged static bool CanMergeItems( ExprItem left, ExprItem right ) { // "Not" can never be a right side operator - return right.Operator != Operator.Not && GetPriority( left.Operator ) >= GetPriority( right.Operator ); + return right.Operator != Operator.Not && GetPrecedence( left.Operator ) >= GetPrecedence( right.Operator ); } // Helper method to get the priority of an operator - static int GetPriority( Operator type ) + static int GetPrecedence( Operator type ) { - return type switch + return type switch // higher number means greater precedence { Operator.Not => 1, - Operator.And or - Operator.Or => 2, + Operator.Or => 2, + Operator.And => 3, Operator.Equals or Operator.NotEquals or Operator.GreaterThan or Operator.GreaterThanOrEqual or Operator.LessThan or - Operator.LessThanOrEqual => 3, + Operator.LessThanOrEqual => 4, _ => 0, }; } } - private static void MergeItems( ExprItem left, ExprItem right, ITypeDescriptor descriptor ) + private static void MergeItems( ExprItem left, ExprItem right, FilterParserContext parserContext ) { - switch ( left.Operator ) + left.Expression = NodeTypeComparerBinderExpression.BindComparerExpression( parserContext, left.Expression ); + right.Expression = NodeTypeComparerBinderExpression.BindComparerExpression( parserContext, right.Expression ); + + left.Expression = left.Operator switch { - case Operator.Equals: - left.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, left.Expression ); - right.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, right.Expression ); + Operator.Equals => NodeTypeExpression.Equal( left.Expression, right.Expression ), + Operator.NotEquals => NodeTypeExpression.NotEqual( left.Expression, right.Expression ), + Operator.GreaterThan => NodeTypeExpression.GreaterThan( left.Expression, right.Expression ), + Operator.GreaterThanOrEqual => NodeTypeExpression.GreaterThanOrEqual( left.Expression, right.Expression ), + Operator.LessThan => NodeTypeExpression.LessThan( left.Expression, right.Expression ), + Operator.LessThanOrEqual => NodeTypeExpression.LessThanOrEqual( left.Expression, right.Expression ), + Operator.And => NodeTypeExpression.And( left.Expression, right.Expression ), + Operator.Or => NodeTypeExpression.Or( left.Expression, right.Expression ), + Operator.Not => NodeTypeExpression.Not( right.Expression ), + _ => throw new InvalidOperationException( $"Invalid operator {left.Operator}" ) + }; + + left.Expression = FilterTruthyExpression.ConvertBoolToValueTypeExpression( left.Expression ); - left.Expression = Expression.Equal( left.Expression, right.Expression ); - break; - case Operator.NotEquals: - left.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, left.Expression ); - right.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, right.Expression ); + left.Operator = right.Operator; + left.ExpressionInfo.Kind = ExpressionKind.Merged; + } - left.Expression = Expression.NotEqual( left.Expression, right.Expression ); - break; - case Operator.GreaterThan: - left.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, left.Expression ); - right.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, right.Expression ); + // Throw helpers - left.Expression = Expression.GreaterThan( left.Expression, right.Expression ); - break; - case Operator.GreaterThanOrEqual: - left.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, left.Expression ); - right.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, right.Expression ); + private static void ThrowIfInvalidComparison( in ParserState state, ExprItem left, ExprItem right ) + { + ThrowIfConstantIsNotCompared( in state, left, right ); + ThrowIfFunctionInvalidCompare( in state, left ); + } - left.Expression = Expression.GreaterThanOrEqual( left.Expression, right.Expression ); - break; - case Operator.LessThan: - left.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, left.Expression ); - right.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, right.Expression ); + private static void ThrowIfFunctionInvalidCompare( in ParserState state, ExprItem item ) + { + if ( state.IsArgument ) + return; - left.Expression = Expression.LessThan( left.Expression, right.Expression ); - break; - case Operator.LessThanOrEqual: - left.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, left.Expression ); - right.Expression = ComparerExpressionFactory.GetComparand( descriptor.Accessor, right.Expression ); + if ( item.ExpressionInfo.Kind != ExpressionKind.Function ) + return; - left.Expression = Expression.LessThanOrEqual( left.Expression, right.Expression ); - break; - case Operator.And: - left.Expression = Expression.AndAlso( - FilterTruthyExpression.IsTruthyExpression( left.Expression! ), - FilterTruthyExpression.IsTruthyExpression( right.Expression ) - ); - break; - case Operator.Or: - left.Expression = Expression.OrElse( - FilterTruthyExpression.IsTruthyExpression( left.Expression! ), - FilterTruthyExpression.IsTruthyExpression( right.Expression ) - ); - break; - case Operator.Not: - left.Expression = Expression.Not( - FilterTruthyExpression.IsTruthyExpression( right.Expression ) - ); - break; - case Operator.Nop: - case Operator.OpenParen: - case Operator.ClosedParen: - default: - left.Expression = left.Expression; - break; + if ( (item.ExpressionInfo.FunctionInfo & FilterExtensionInfo.MustCompare) == FilterExtensionInfo.MustCompare && + !item.Operator.IsComparison() ) + { + throw new NotSupportedException( $"Function must compare: {state.Buffer.ToString()}." ); } - // Wrap left expression in a try-catch block to handle exceptions - left.Expression = left.Expression == null - ? left.Expression - : Expression.TryCatch( - left.Expression, - Expression.Catch( typeof( NotSupportedException ), Expression.Rethrow( left.Expression.Type ) ), - Expression.Catch( typeof( Exception ), Expression.Constant( false ) ) - ); + if ( (item.ExpressionInfo.FunctionInfo & FilterExtensionInfo.MustNotCompare) == FilterExtensionInfo.MustNotCompare && + item.Operator.IsComparison() ) + { + throw new NotSupportedException( $"Function must not compare: {state.Buffer.ToString()}." ); + } + } - left.Operator = right.Operator; + private static void ThrowIfConstantIsNotCompared( in ParserState state, ExprItem left, ExprItem right ) + { + if ( state.IsArgument ) + return; + + if ( left.ExpressionInfo.Kind == ExpressionKind.Literal && !left.Operator.IsComparison() ) + throw new NotSupportedException( $"Unsupported literal without comparison: {state.Buffer.ToString()}." ); + + if ( right != null && right.ExpressionInfo.Kind == ExpressionKind.Literal && !left.Operator.IsComparison() ) + throw new NotSupportedException( $"Unsupported literal without comparison: {state.Buffer.ToString()}." ); } - private class ExprItem( Expression expression, Operator op ) + // ExprItem + + [DebuggerDisplay( "Operator = {Operator}, {ExpressionInfo.Kind}" )] + private class ExprItem( Expression expression, Operator op, ExpressionInfo expressionInfo ) { + public ExpressionInfo ExpressionInfo { get; } = expressionInfo; public Expression Expression { get; set; } = expression; public Operator Operator { get; set; } = op; } diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs b/src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs new file mode 100644 index 00000000..286cdb58 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs @@ -0,0 +1,12 @@ +using System.Linq.Expressions; +using Hyperbee.Json.Descriptors; + +namespace Hyperbee.Json.Filters.Parser; + +internal record FilterParserContext( ITypeDescriptor Descriptor ) +{ + public ParameterExpression RuntimeContext { get; init; } = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); +} + + +public record FilterRuntimeContext( TNode Current, TNode Root, ITypeDescriptor Descriptor ); diff --git a/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs b/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs index 194ba31c..1e2820d7 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs @@ -1,29 +1,41 @@ using System.Collections; using System.Linq.Expressions; using System.Reflection; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser; public static class FilterTruthyExpression { private static readonly MethodInfo IsTruthyMethodInfo = typeof( FilterTruthyExpression ).GetMethod( nameof( IsTruthy ) ); + private static readonly MethodInfo ConvertBoolToValueTypeMethodInfo = typeof( FilterTruthyExpression ).GetMethod( nameof( ConvertBoolToValueType ) ); public static Expression IsTruthyExpression( Expression expression ) => expression.Type == typeof( bool ) ? expression : Expression.Call( IsTruthyMethodInfo, expression ); + public static Expression ConvertBoolToValueTypeExpression( Expression expression ) => + Expression.Call( ConvertBoolToValueTypeMethodInfo, expression ); + public static bool IsTruthy( object value ) { - return value switch + var truthy = value switch { - null => false, - bool boolValue => boolValue, - string str => !string.IsNullOrEmpty( str ) && !str.Equals( "false", StringComparison.OrdinalIgnoreCase ), - Array array => array.Length > 0, - IEnumerable enumerable => enumerable.Cast().Any(), - IConvertible convertible => Convert.ToBoolean( convertible ), + Nothing => false, + Null => false, + ValueType valueBool => valueBool.Value, + ValueType floatValue => floatValue.Value != 0, + ValueType valueString => !string.IsNullOrEmpty( valueString.Value ) && !valueString.Value.Equals( "false", StringComparison.OrdinalIgnoreCase ), + IEnumerable enumerable => enumerable.Cast().Any(), // NodesType _ => true }; + + return truthy; + } + + public static INodeType ConvertBoolToValueType( bool value ) + { + return value ? Constants.True : Constants.False; } } diff --git a/src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs b/src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs new file mode 100644 index 00000000..b6ca6328 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs @@ -0,0 +1,25 @@ +using System.Linq.Expressions; +using Hyperbee.Json.Filters.Values; + +namespace Hyperbee.Json.Filters.Parser; + +public static class NodeTypeComparerBinderExpression +{ + private static readonly Expression BindComparerExpressionConst = Expression.Constant( (Func, INodeType, INodeType>) BindComparer ); + internal static Expression BindComparerExpression( FilterParserContext parserContext, Expression expression ) + { + if ( expression == null ) + return null; + + var parserContextExp = Expression.Constant( parserContext ); + + return Expression.Invoke( BindComparerExpressionConst, parserContextExp, + Expression.Convert( expression, typeof( INodeType ) ) ); + } + + internal static INodeType BindComparer( FilterParserContext parserContext, INodeType item ) + { + item.Comparer = parserContext.Descriptor.Comparer; + return item; + } +} diff --git a/src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs b/src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs new file mode 100644 index 00000000..8b969121 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs @@ -0,0 +1,64 @@ +using System.Linq.Expressions; +using System.Reflection; +using Hyperbee.Json.Filters.Values; + +namespace Hyperbee.Json.Filters.Parser; + +public static class NodeTypeExpression +{ + private static readonly MethodInfo AreEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( AreEqual ) ); + private static readonly MethodInfo AreNotEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( AreNotEqual ) ); + private static readonly MethodInfo IsLessThanMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsLessThan ) ); + private static readonly MethodInfo IsLessThanOrEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsLessThanOrEqual ) ); + private static readonly MethodInfo IsGreaterThanMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsGreaterThan ) ); + private static readonly MethodInfo IsGreaterThanOrEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsGreaterThanOrEqual ) ); + + private static readonly MethodInfo AndAlsoMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( AndAlso ) ); + private static readonly MethodInfo OrElseMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( OrElse ) ); + private static readonly MethodInfo NotMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( NotBoolean ) ); + + public static Expression Equal( Expression left, Expression right ) => Expression.Call( AreEqualMethodInfo, left, right ); + public static Expression NotEqual( Expression left, Expression right ) => Expression.Call( AreNotEqualMethodInfo, left, right ); + public static Expression LessThan( Expression left, Expression right ) => Expression.Call( IsLessThanMethodInfo, left, right ); + public static Expression LessThanOrEqual( Expression left, Expression right ) => Expression.Call( IsLessThanOrEqualMethodInfo, left, right ); + public static Expression GreaterThan( Expression left, Expression right ) => Expression.Call( IsGreaterThanMethodInfo, left, right ); + public static Expression GreaterThanOrEqual( Expression left, Expression right ) => Expression.Call( IsGreaterThanOrEqualMethodInfo, left, right ); + + // Binary operators + public static Expression And( Expression left, Expression right ) => Expression.Call( AndAlsoMethodInfo, left, right ); + public static Expression Or( Expression left, Expression right ) => Expression.Call( OrElseMethodInfo, left, right ); + public static Expression Not( Expression expression ) => Expression.Call( NotMethodInfo, expression ); + + public static bool AreEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.Equals ) == 0; + public static bool AreNotEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.NotEquals ) != 0; + public static bool IsLessThan( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.LessThan ) < 0; + public static bool IsLessThanOrEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.LessThanOrEqual ) <= 0; + public static bool IsGreaterThan( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.GreaterThan ) > 0; + public static bool IsGreaterThanOrEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.GreaterThanOrEqual ) >= 0; + + public static bool AndAlso( INodeType left, INodeType right ) + { + if ( left is ValueType leftBoolValue && right is ValueType rightBoolValue ) + return leftBoolValue.Value && rightBoolValue.Value; + + return left.Comparer.Exists( left ) && + right.Comparer.Exists( right ); + } + + public static bool OrElse( INodeType left, INodeType right ) + { + if ( left is ValueType leftBoolValue && right is ValueType rightBoolValue ) + return leftBoolValue.Value || rightBoolValue.Value; + + return left.Comparer.Exists( left ) || + right.Comparer.Exists( right ); + } + + public static bool NotBoolean( INodeType value ) + { + if ( value is ValueType { Value: false } ) + return true; + + return !value.Comparer.Exists( value ); + } +} diff --git a/src/Hyperbee.Json/Filters/Parser/Operator.cs b/src/Hyperbee.Json/Filters/Parser/Operator.cs index 14164f09..acbbea67 100644 --- a/src/Hyperbee.Json/Filters/Parser/Operator.cs +++ b/src/Hyperbee.Json/Filters/Parser/Operator.cs @@ -1,17 +1,54 @@ namespace Hyperbee.Json.Filters.Parser; +[Flags] public enum Operator { - Nop = 0, // used to represent an unassigned token - OpenParen, - ClosedParen, - Not, - Equals, - NotEquals, - LessThan, - LessThanOrEqual, - GreaterThan, - GreaterThanOrEqual, - Or, - And + None = 0x0, + + // Flags + NonOperator = 0x1, // 0001 + Comparison = 0x2, // 0010 + Logical = 0x4, // 0100 + Parenthesis = 0x8, // 1000 + + // Parenthesis Operators + OpenParen = 0x10 | Parenthesis, + ClosedParen = 0x20 | Parenthesis, + + // Logical Operators + Not = 0x30 | Logical, + Or = 0x40 | Logical, + And = 0x50 | Logical, + + // Comparison Operators + Equals = 0x60 | Comparison, + NotEquals = 0x70 | Comparison, + LessThan = 0x80 | Comparison, + LessThanOrEqual = 0x90 | Comparison, + GreaterThan = 0xA0 | Comparison, + GreaterThanOrEqual = 0xB0 | Comparison, + + // Specific non-operators + Whitespace = 0xC0 | NonOperator, + Quotes = 0xD0 | NonOperator, + Token = 0xE0 | NonOperator, + Bracket = 0xF0 | NonOperator, +} + +public static class OperatorExtensions +{ + public static bool IsNonOperator( this Operator op ) + { + return (op & Operator.NonOperator) == Operator.NonOperator; + } + + public static bool IsComparison( this Operator op ) + { + return (op & Operator.Comparison) == Operator.Comparison; + } + + public static bool IsLogical( this Operator op ) + { + return (op & Operator.Logical) == Operator.Logical; + } } diff --git a/src/Hyperbee.Json/Filters/Parser/ParserState.cs b/src/Hyperbee.Json/Filters/Parser/ParserState.cs index e90d673f..5dd2c720 100644 --- a/src/Hyperbee.Json/Filters/Parser/ParserState.cs +++ b/src/Hyperbee.Json/Filters/Parser/ParserState.cs @@ -1,30 +1,44 @@ -namespace Hyperbee.Json.Filters.Parser; +using System.Diagnostics; +namespace Hyperbee.Json.Filters.Parser; + +[DebuggerDisplay( "{Buffer.ToString()}, Item = {Item.ToString()}, Operator = {Operator}, Pos = {Pos.ToString()}" )] public ref struct ParserState { public ReadOnlySpan Buffer { get; } public ReadOnlySpan Item { get; internal set; } + public bool TrailingWhitespace { get; internal set; } + public bool IsArgument { get; internal set; } + public int BracketDepth { get; internal set; } + public ref int ParenDepth; + public Operator Operator { get; set; } public char Terminal { get; init; } public readonly ref int Pos; - internal ParserState( ReadOnlySpan buffer, ReadOnlySpan item, ref int pos, Operator tokenType, char terminal ) + internal ParserState( ReadOnlySpan buffer, ReadOnlySpan item, ref int pos, ref int parenDepth, Operator tokenType, char terminal ) { Buffer = buffer; Item = item; Operator = tokenType; Terminal = terminal; Pos = ref pos; + ParenDepth = ref parenDepth; } public readonly bool EndOfBuffer => Pos >= Buffer.Length; - public readonly bool IsParsing => Pos < Buffer.Length && Buffer[Pos] != Terminal; - public readonly bool IsTerminal => Buffer[Pos] == Terminal; + public readonly bool IsParsing => Pos < Buffer.Length && Previous != Terminal; public readonly char Current => Buffer[Pos]; public readonly char Previous => Buffer[Pos - 1]; - internal void SetItem( int itemStart, int itemEnd ) => Item = Buffer[itemStart..itemEnd].TrimEnd(); + internal void SetItem( int itemStart, int itemEnd ) + { + var item = Buffer[itemStart..itemEnd]; + TrailingWhitespace = !item.IsEmpty && char.IsWhiteSpace( item[^1] ); + + Item = item.TrimEnd(); + } } diff --git a/src/Hyperbee.Json/Filters/Values/Constants.cs b/src/Hyperbee.Json/Filters/Values/Constants.cs new file mode 100644 index 00000000..31f4b581 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/Constants.cs @@ -0,0 +1,10 @@ +namespace Hyperbee.Json.Filters.Values; + +public static class Constants +{ + public static ValueType True { get; } = new( true ); + public static ValueType False { get; } = new( false ); + + public static Null Null { get; } = new(); + public static Nothing Nothing { get; } = new(); +} diff --git a/src/Hyperbee.Json/Filters/Values/INodeType.cs b/src/Hyperbee.Json/Filters/Values/INodeType.cs new file mode 100644 index 00000000..a7fea226 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/INodeType.cs @@ -0,0 +1,8 @@ +namespace Hyperbee.Json.Filters.Values; + +public interface INodeType +{ + public NodeTypeKind Kind { get; } + + public INodeTypeComparer Comparer { get; set; } +} diff --git a/src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs b/src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs new file mode 100644 index 00000000..47103f4b --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs @@ -0,0 +1,236 @@ +using Hyperbee.Json.Descriptors; +using Hyperbee.Json.Filters.Parser; + +namespace Hyperbee.Json.Filters.Values; + +public interface INodeTypeComparer +{ + public int Compare( INodeType left, INodeType right, Operator operation ); + + public bool Exists( INodeType node ); +} + +public class NodeTypeComparer( IValueAccessor accessor ) : INodeTypeComparer +{ + private const float Tolerance = 1e-6F; // Define a tolerance for float comparisons + + /* + * Comparison Rules (according to JSONPath RFC 9535): + * + * 1. Compare Value to Value: + * - Two values are equal if they are of the same type and have the same value. + * - For float comparisons, use a tolerance to handle precision issues. + * - Comparisons between different types yield false. + * + * 2. Compare Node to Node: + * - Since a Node is essentially an enumerable with a single item, compare the single items directly. + * - Apply the same value comparison rules to the single items. + * + * 3. Compare NodeList to NodeList: + * - Two NodeLists are equal if they are sequence equal. + * - Sequence equality should consider deep equality of Node items. + * - Return 0 if sequences are equal. + * - Return -1 if the left sequence is less. + * - Return 1 if the left sequence is greater. + * + * 4. Compare NodeList to Value: + * - A NodeList is equal to a value if any node in the NodeList matches the value. + * - Return 0 if any node matches the value. + * - Return -1 if the value is less than all nodes. + * - Return 1 if the value is greater than all nodes. + * + * 5. Compare Value to NodeList: + * - Similar to the above, true if the value is found in the NodeList. + * + * 6. Compare Node to NodeList and vice versa: + * - Since Node is a single item enumerable, treat it similarly to Value in comparison to NodeList. + * + * 7. Truthiness Rules: + * - Falsy values: null, false, 0, "", NaN. + * - Truthy values: Anything not falsy, including non-empty strings, non-zero numbers, true, arrays, and objects. + * - Truthiness is generally not used for comparison operators (==, <) in filter expressions. + * - Type mismatches (e.g., string vs. number) result in false for equality (==) and true for inequality (!=). + * + * Order of Operations: + * - Check if both are NodeLists. + * - Check if one is a NodeList and the other is a Value. + * - Compare directly if both are Values. + */ + public int Compare( INodeType left, INodeType right, Operator operation ) + { + ThrowIfNotNormalized( left ); + ThrowIfNotNormalized( right ); + + if ( left is NodesType leftEnumerable && right is NodesType rightEnumerable ) + { + return CompareEnumerables( leftEnumerable, rightEnumerable ); + } + + if ( left is NodesType leftEnumerable1 ) + { + var compare = CompareEnumerableToValue( leftEnumerable1, right, out var typeMismatch, out var nodeCount ); + return AdjustResult( compare, nodeCount, operation, typeMismatch ); + } + + if ( right is NodesType rightEnumerable1 ) + { + var compare = CompareEnumerableToValue( rightEnumerable1, left, out var typeMismatch, out var nodeCount ); + return AdjustResult( compare, nodeCount, operation, typeMismatch ); + } + + return CompareValues( left, right, out _ ); + + static int AdjustResult( int compare, int nodeCount, Operator operation, bool typeMismatch ) + { + // When comparing a NodeList to a Value, '<' and '>' type operators only have meaning when the + // NodeList has a single node. + // + // 1. When there is a single node, the comparison is based on the unwrapped node value. + // This results in a meaningful value to value comparison for equality, and greater-than and + // less-than operations (if the values are the same type). + // + // 2. When there is more than one node, or an empty node list, equality is based on finding the + // value in the set of nodes. The result is true if the value is found in the set, and false + // otherwise. + // + // In this case, the result is not meaningful for greater-than and less-than operations, since + // the comparison is based on the set of nodes, and not on two single values. + // + // However, the comparison result will still be used in the context of a greater-than or less-than + // operation, which will yield indeterminate results based on the left or right order of operands. + // To handle this, we need to normalize the result of the comparison. In this case, we want to + // normalize the result so that greater-than and less-than always return false, regardless of the + // left or right order of the comparands. + + return (nodeCount != 1 || typeMismatch) switch // Test for a non-single value set, or a type comparison mismatch + { + true when (operation == Operator.LessThan || operation == Operator.LessThanOrEqual) => compare < 0 ? -compare : compare, + true when (operation == Operator.GreaterThan || operation == Operator.GreaterThanOrEqual) => compare > 0 ? -compare : compare, + _ => compare + }; + } + + static void ThrowIfNotNormalized( INodeType nodeType ) + { + if ( nodeType is NodesType { IsNormalized: false } ) + throw new NotSupportedException( "Unsupported non-single query." ); + } + } + + public bool Exists( INodeType node ) + { + return node switch + { + ValueType boolValue => boolValue.Value, + ValueType floatValue => floatValue.Value != 0, + ValueType stringValue => !string.IsNullOrEmpty( stringValue.Value ), + NodesType nodes => nodes.Any(), + _ => false + }; + } + + private int CompareEnumerables( IEnumerable left, IEnumerable right ) + { + using var leftEnumerator = left.GetEnumerator(); + using var rightEnumerator = right.GetEnumerator(); + + while ( leftEnumerator.MoveNext() ) + { + if ( !rightEnumerator.MoveNext() ) + return 1; // Left has more elements, so it is greater + + // if the values can be extracted, compare the values directly + if ( TryGetValueType( accessor, leftEnumerator.Current, out var leftItemValue ) && + TryGetValueType( accessor, rightEnumerator.Current, out var rightItemValue ) ) + return CompareValues( leftItemValue, rightItemValue, out _ ); + + if ( !accessor.DeepEquals( leftEnumerator.Current, rightEnumerator.Current ) ) + return -1; // Elements are not deeply equal + } + + if ( rightEnumerator.MoveNext() ) + return -1; // Right has more elements, so left is less + + return 0; // Sequences are equal + } + + private int CompareEnumerableToValue( IEnumerable enumeration, INodeType value, out bool typeMismatch, out int nodeCount ) + { + nodeCount = 0; + typeMismatch = false; + var lastCompare = -1; + + foreach ( var item in enumeration ) + { + nodeCount++; + + if ( !TryGetValueType( accessor, item, out var itemValue ) ) + continue; // Skip if value cannot be extracted + + lastCompare = CompareValues( itemValue, value, out typeMismatch ); + + if ( lastCompare == 0 ) + return 0; // Return 0 if any node matches the value + } + + if ( nodeCount == 0 ) + { + if ( value is Nothing ) // Considered equal + return 0; + + return -1; + } + + return nodeCount != 1 ? -1 : lastCompare; // Return the last comparison if there is only one node + + } + + private static int CompareValues( INodeType left, INodeType right, out bool typeMismatch ) + { + typeMismatch = false; + + if ( left is Null or Nothing && right is Null or Nothing ) + { + return 0; + } + + if ( left?.GetType() != right?.GetType() ) + { + typeMismatch = true; // Type mismatch: important for non-equality comparisons + return -1; + } + + return left switch + { + ValueType leftStringValue when right is ValueType rightStringValue => + string.Compare( leftStringValue.Value, rightStringValue.Value, StringComparison.Ordinal ), + + ValueType leftBoolValue when right is ValueType rightBoolValue => + leftBoolValue.Value.CompareTo( rightBoolValue.Value ), + + ValueType leftFloatValue when right is ValueType rightFloatValue => + Math.Abs( leftFloatValue.Value - rightFloatValue.Value ) < Tolerance ? 0 : leftFloatValue.Value.CompareTo( rightFloatValue.Value ), + + _ => Comparer.Default.Compare( left, right ) + }; + } + + private static bool TryGetValueType( IValueAccessor accessor, TNode node, out INodeType nodeType ) + { + if ( accessor.TryGetValueFromNode( node, out var itemValue ) ) + { + nodeType = itemValue switch + { + string itemString => new ValueType( itemString ), + bool itemBool => new ValueType( itemBool ), + float itemFloat => new ValueType( itemFloat ), + null => Constants.Null, + _ => throw new NotSupportedException( "Unsupported value type." ) + }; + return true; + } + + nodeType = null; + return false; + } +} diff --git a/src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs b/src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs new file mode 100644 index 00000000..5e401893 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs @@ -0,0 +1,10 @@ +namespace Hyperbee.Json.Filters.Values; + +public enum NodeTypeKind +{ + Null, + Nothing, + Value, + Node, + NodeList +} diff --git a/src/Hyperbee.Json/Filters/Values/NodesType.cs b/src/Hyperbee.Json/Filters/Values/NodesType.cs new file mode 100644 index 00000000..76cb6bc0 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/NodesType.cs @@ -0,0 +1,17 @@ +using System.Collections; + +namespace Hyperbee.Json.Filters.Values; + +public struct NodesType( IEnumerable value, bool isNormalized ) : INodeType, IEnumerable +{ + public readonly bool IsNormalized => isNormalized; + public readonly NodeTypeKind Kind => NodeTypeKind.NodeList; + + public INodeTypeComparer Comparer { get; set; } + + public IEnumerable Value { get; } = value; + + public readonly IEnumerator GetEnumerator() => Value.GetEnumerator(); + + readonly IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/src/Hyperbee.Json/Filters/Values/Nothing.cs b/src/Hyperbee.Json/Filters/Values/Nothing.cs new file mode 100644 index 00000000..79a22880 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/Nothing.cs @@ -0,0 +1,7 @@ +namespace Hyperbee.Json.Filters.Values; + +public struct Nothing : INodeType +{ + public readonly NodeTypeKind Kind => NodeTypeKind.Nothing; + public INodeTypeComparer Comparer { get; set; } +} diff --git a/src/Hyperbee.Json/Filters/Values/Null.cs b/src/Hyperbee.Json/Filters/Values/Null.cs new file mode 100644 index 00000000..72a0942f --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/Null.cs @@ -0,0 +1,7 @@ +namespace Hyperbee.Json.Filters.Values; + +public struct Null : INodeType +{ + public readonly NodeTypeKind Kind => NodeTypeKind.Null; + public INodeTypeComparer Comparer { get; set; } +} diff --git a/src/Hyperbee.Json/Filters/Values/ValueType.cs b/src/Hyperbee.Json/Filters/Values/ValueType.cs new file mode 100644 index 00000000..8cba9a7d --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/ValueType.cs @@ -0,0 +1,10 @@ +namespace Hyperbee.Json.Filters.Values; + +public struct ValueType( T value ) : INodeType +{ + public readonly NodeTypeKind Kind => NodeTypeKind.Value; + + public INodeTypeComparer Comparer { get; set; } + + public T Value { get; } = value; +} diff --git a/src/Hyperbee.Json/Internal/SpanBuilder.cs b/src/Hyperbee.Json/Internal/SpanBuilder.cs new file mode 100644 index 00000000..24c423d4 --- /dev/null +++ b/src/Hyperbee.Json/Internal/SpanBuilder.cs @@ -0,0 +1,76 @@ + +using System.Runtime.CompilerServices; + +namespace Hyperbee.Json.Internal; + +using System; +using System.Buffers; + +internal ref struct SpanBuilder // use in a try finally with an explicit Dispose +{ + private char[] _buffer; + private Span _chars; + private int _pos; + + public SpanBuilder( int initialCapacity ) + { + _buffer = ArrayPool.Shared.Rent( initialCapacity ); + _chars = _buffer; + _pos = 0; + } + + public readonly bool IsEmpty => _pos == 0; + + public void Append( char value ) + { + if ( _pos >= _chars.Length ) + Grow(); + + _chars[_pos++] = value; + } + + public void Append( ReadOnlySpan value ) + { + if ( _pos + value.Length > _chars.Length ) + Grow( value.Length ); + + value.CopyTo( _chars[_pos..] ); + _pos += value.Length; + } + + public void Clear() => _pos = 0; + + public readonly ReadOnlySpan AsSpan() => _chars[.._pos]; + + private void Grow( int additionalCapacity = 0 ) + { + var newCapacity = Math.Max( _chars.Length * 2, _chars.Length + additionalCapacity ); + var newArray = ArrayPool.Shared.Rent( newCapacity ); + _chars.CopyTo( newArray ); + + ArrayPool.Shared.Return( _buffer ); + _buffer = newArray; + _chars = newArray; + } + + public override string ToString() + { + var value = _chars[.._pos].ToString(); + Dispose(); + return value; + } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + public void Dispose() + { + var array = _buffer; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + + if ( array != null ) + ArrayPool.Shared.Return( array ); + + _buffer = null; + _chars = default; + _pos = 0; + } +} diff --git a/src/Hyperbee.Json/Internal/SpanHelper.cs b/src/Hyperbee.Json/Internal/SpanHelper.cs new file mode 100644 index 00000000..af3cf370 --- /dev/null +++ b/src/Hyperbee.Json/Internal/SpanHelper.cs @@ -0,0 +1,132 @@ +namespace Hyperbee.Json.Internal; + +internal enum SpanUnescapeOptions +{ + Single, + SingleThenUnquote, + Mixed +} + +internal static class SpanHelper +{ + internal static void Unescape( ReadOnlySpan span, ref SpanBuilder builder, SpanUnescapeOptions options ) + { + if ( options == SpanUnescapeOptions.Single || options == SpanUnescapeOptions.SingleThenUnquote ) + { + if ( span.Length < 2 || span[0] != '\'' && span[0] != '"' || span[^1] != '\'' && span[^1] != '"' ) + throw new ArgumentException( "Quoted strings must start and end with a quote." ); + + if ( options == SpanUnescapeOptions.SingleThenUnquote ) + UnescapeQuotedString( span[1..^1], span[0], ref builder ); // unquote and unescape + else + UnescapeQuotedString( span, span[0], ref builder ); // unquote + } + else + { + // Scan for, and unescape, quoted strings + for ( var i = 0; i < span.Length; i++ ) + { + var current = span[i]; + + if ( current == '\'' || current == '"' ) + { + builder.Append( current ); + + var endQuotePos = UnescapeQuotedString( span[++i..], current, ref builder ); // unescape + + if ( endQuotePos == -1 ) // we expect a closing quote + throw new ArgumentException( "Closing quote not found in quoted string." ); + + i += endQuotePos; + + builder.Append( current ); + } + else + { + builder.Append( current ); + } + } + } + } + + private static int UnescapeQuotedString( ReadOnlySpan span, char quoteChar, ref SpanBuilder builder ) + { + for ( var i = 0; i < span.Length; i++ ) + { + if ( span[i] == quoteChar ) + { + // return after the closing quote + return i; + } + + if ( span[i] == '\\' && i + 1 < span.Length ) + { + i++; + switch ( span[i] ) + { + case '\'': + builder.Append( '\'' ); + break; + case '"': + builder.Append( '"' ); + break; + case '\\': + builder.Append( '\\' ); + break; + case '/': + builder.Append( '/' ); + break; + case 'b': + builder.Append( '\b' ); + break; + case 'f': + builder.Append( '\f' ); + break; + case 'n': + builder.Append( '\n' ); + break; + case 'r': + builder.Append( '\r' ); + break; + case 't': + builder.Append( '\t' ); + break; + case 'u' when i + 4 < span.Length: + builder.Append( ConvertHexToChar( span.Slice( i + 1, 4 ) ) ); + i += 4; + break; + default: + throw new ArgumentException( $"Invalid escape sequence `\\{span[i]}` in quoted string." ); + } + } + else + { + builder.Append( span[i] ); + } + } + + return -1; // no closing quote + + static char ConvertHexToChar( ReadOnlySpan hexSpan ) + { + if ( hexSpan.Length != 4 ) + { + throw new ArgumentException( "Hex span must be exactly 4 characters long." ); + } + + var value = 0; + for ( var i = 0; i < hexSpan.Length; i++ ) + { + value = (value << 4) + hexSpan[i] switch + { + >= '0' and <= '9' => hexSpan[i] - '0', + >= 'a' and <= 'f' => hexSpan[i] - 'a' + 10, + >= 'A' and <= 'F' => hexSpan[i] - 'A' + 10, + _ => throw new ArgumentException( "Invalid hex digit." ) + }; + } + + return (char) value; + } + } +} diff --git a/src/Hyperbee.Json/JsonPath.cs b/src/Hyperbee.Json/JsonPath.cs index 28bb5acf..7e56066f 100644 --- a/src/Hyperbee.Json/JsonPath.cs +++ b/src/Hyperbee.Json/JsonPath.cs @@ -33,7 +33,6 @@ #endregion using System.Diagnostics; -using System.Globalization; using System.Runtime.CompilerServices; using Hyperbee.Json.Descriptors; @@ -57,23 +56,38 @@ internal enum NodeFlags public static IEnumerable Select( in TNode value, string query, NodeProcessorDelegate processor = null ) { - return EnumerateMatches( value, value, query, processor ); + var compiledQuery = JsonPathQueryParser.Parse( query ); + return EnumerateMatches( value, value, compiledQuery, processor ); } - internal static IEnumerable SelectInternal( in TNode value, TNode root, string query, NodeProcessorDelegate processor = null ) + internal static IEnumerable SelectInternal( in TNode value, in TNode root, JsonPathQuery compiledQuery, NodeProcessorDelegate processor = null ) { - return EnumerateMatches( value, root, query, processor ); + // entry point for filter recursive calls + + // explicitly allow dot whitespace for function arguments. This is annoying + // because the RFC ABNF does not allow whitespace in the query for dot member + // notation, but it does allow it in the filter for function arguments. + + return EnumerateMatches( value, root, compiledQuery, processor ); } - private static IEnumerable EnumerateMatches( in TNode value, in TNode root, string query, NodeProcessorDelegate processor = null ) + private static IEnumerable EnumerateMatches( in TNode value, in TNode root, JsonPathQuery compiledQuery, NodeProcessorDelegate processor = null ) { - if ( string.IsNullOrWhiteSpace( query ) ) // invalid per the RFC ABNF + if ( string.IsNullOrWhiteSpace( compiledQuery.Query ) ) // invalid per the RFC ABNF return []; // Consensus: return empty array for empty query - if ( query == "$" || query == "@" ) // quick out for everything + if ( compiledQuery.Query == "$" || compiledQuery.Query == "@" ) // quick out for everything return [value]; - var segmentNext = JsonPathQueryParser.Parse( query ).Next; // The first segment is always the root; skip it + var segmentNext = compiledQuery.Segments.Next; // The first segment is always the root; skip it + + if ( Descriptor.CanUsePointer && compiledQuery.Normalized ) // we can fast path this + { + if ( Descriptor.Accessor.TryGetFromPointer( in value, segmentNext, out var result ) ) + return [result]; + + return []; + } return EnumerateMatches( root, new NodeArgs( default, value, default, segmentNext, NodeFlags.Default ), processor ); } @@ -119,161 +133,159 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N if ( nodeKind == NodeKind.Value ) continue; - // try to access object or array using name or index + // singular selector if ( segmentCurrent.IsSingular ) { if ( nodeKind == NodeKind.Object && selectorKind == SelectorKind.Index ) continue; // don't allow indexing in to objects - if ( accessor.TryGetChildValue( value, selector, out var childValue ) ) - { + // try to access object or array using name or index + if ( accessor.TryGetChildValue( value, selector, selectorKind, out var childValue ) ) stack.Push( value, childValue, selector, segmentNext ); - } continue; } - // wildcard - - if ( selectorKind == SelectorKind.Wildcard ) - { - foreach ( var (childValue, childKey, childKind) in accessor.EnumerateChildren( value ) ) - { - // optimization: quicker return for final - // - // the parser will work without this check, but we would be forcing it - // to push and pop values onto the stack that we know will not be used. - if ( segmentNext.IsFinal ) - { - // we could just yield here, but we can't because we want to preserve - // the order of the results as per the RFC. so we push the current - // value onto the stack without prepending the childKey or childKind - // to set up for an immediate return on the next iteration. - //Push( stack, value, childValue, childKey, segmentNext ); - stack.Push( value, childValue, childKey, segmentNext ); - continue; - } - - stack.Push( parent, value, childKey, segmentNext.Prepend( childKey, childKind ) ); // (Name | Index) - } - - continue; - } - - // descendant - - if ( selectorKind == SelectorKind.Descendant ) - { - foreach ( var (childValue, childKey, _) in accessor.EnumerateChildren( value, includeValues: false ) ) // child arrays or objects only - { - stack.Push( value, childValue, childKey, segmentCurrent ); // Descendant - } - - // Union Processing After Descent: If a union operator immediately follows a - // descendant operator, the union should only process simple values. This is - // to prevent duplication of complex objects that would result from both the - // current node and the union processing the same items. - - stack.Push( parent, value, null, segmentNext, NodeFlags.AfterDescent ); // process the current value - continue; - } - - // group + // group selector for ( var i = 0; i < segmentCurrent.Selectors.Length; i++ ) // using 'for' for performance { - if ( i != 0 ) + if ( i > 0 ) // we already have the first selector (selector, selectorKind) = segmentCurrent.Selectors[i]; - // [?exp] - - if ( selectorKind == SelectorKind.Filter ) + switch ( selectorKind ) { - foreach ( var (childValue, childKey, childKind) in accessor.EnumerateChildren( value ) ) - { - var result = filterEvaluator.Evaluate( selector[1..], childValue, root ); // remove the leading '?' character + // descendant + case SelectorKind.Descendant: + { + foreach ( var (childValue, childKey, _) in accessor.EnumerateChildren( value, includeValues: false ) ) // child arrays or objects only + { + stack.Push( value, childValue, childKey, segmentCurrent ); // Descendant + } + + // Union Processing After Descent: If a union operator immediately follows a + // descendant operator, the union should only process simple values. This is + // to prevent duplication of complex objects that would result from both the + // current node and the union processing the same items. - if ( !Truthy( result ) ) + stack.Push( parent, value, null, segmentNext, NodeFlags.AfterDescent ); // process the current value continue; + } - // optimization: quicker return for tail values - if ( segmentNext.IsFinal ) + // wildcard + case SelectorKind.Wildcard: { - stack.Push( value, childValue, childKey, segmentNext ); + foreach ( var (childValue, childKey, childKind) in accessor.EnumerateChildren( value ) ) + { + // optimization: quicker return for final + // + // the parser will work without this check, but we would be forcing it + // to push and pop values onto the stack that we know will not be used. + if ( segmentNext.IsFinal ) + { + // we could just yield here, but we can't because we want to preserve + // the order of the results as per the RFC. so we push the current + // value onto the stack without prepending the childKey or childKind + // to set up for an immediate return on the next iteration. + //Push( stack, value, childValue, childKey, segmentNext ); + stack.Push( value, childValue, childKey, segmentNext ); + continue; + } + + stack.Push( parent, value, childKey, segmentNext.Prepend( childKey, childKind ) ); // (Name | Index) + } + continue; } - stack.Push( parent, value, childKey, segmentNext.Prepend( childKey, childKind ) ); // (Name | Index) - } + // [?exp] + case SelectorKind.Filter: + { + foreach ( var (childValue, childKey, childKind) in accessor.EnumerateChildren( value ) ) + { + if ( !filterEvaluator.Evaluate( selector[1..], childValue, root ) ) // remove the leading '?' character + continue; - continue; - } + // optimization: quicker return for tail values + if ( segmentNext.IsFinal ) + { + stack.Push( value, childValue, childKey, segmentNext ); + continue; + } - // array [name1,name2,...] or [#,#,...] or [start:end:step] + stack.Push( parent, value, childKey, segmentNext.Prepend( childKey, childKind ) ); // (Name | Index) + } - if ( nodeKind == NodeKind.Array ) - { - // [#,#,...] + continue; + } - if ( selectorKind == SelectorKind.Index ) - { - stack.Push( value, accessor.GetElementAt( value, int.Parse( selector ) ), selector, segmentNext ); - continue; - } + // Array: [#,#,...] + case SelectorKind.Index: + { + if ( nodeKind != NodeKind.Array ) + continue; - // [start:end:step] Python slice syntax + if ( accessor.TryGetElementAt( value, int.Parse( selector ), out var childValue ) ) + stack.Push( value, childValue, selector, segmentNext ); + continue; + } - if ( selectorKind == SelectorKind.Slice ) - { - foreach ( var index in EnumerateSlice( value, selector, accessor ) ) + // Array: [start:end:step] Python slice syntax + case SelectorKind.Slice: { - stack.Push( value, accessor.GetElementAt( value, index ), index.ToString(), segmentNext ); + if ( nodeKind != NodeKind.Array ) + continue; + + foreach ( var index in EnumerateSlice( value, selector, accessor ) ) + { + if ( accessor.TryGetElementAt( value, index, out var childValue ) ) + stack.Push( value, childValue, index.ToString(), segmentNext ); + } + + continue; } - continue; - } - // [name1,name2,...] + // Array: [name1,name2,...] Names over array + case SelectorKind.Name when nodeKind == NodeKind.Array: + { + var indexSegment = segmentNext.Prepend( selector, SelectorKind.Name ); + var length = accessor.GetArrayLength( value ); - var indexSegment = segmentNext.Prepend( selector, SelectorKind.Name ); - var length = accessor.GetArrayLength( value ); + for ( var index = length - 1; index >= 0; index-- ) + { + if ( !accessor.TryGetElementAt( value, index, out var childValue ) ) + continue; - for ( var index = length - 1; index >= 0; index-- ) - { - var childValue = accessor.GetElementAt( value, index ); + if ( flags == NodeFlags.AfterDescent && accessor.GetNodeKind( childValue ) != NodeKind.Value ) + continue; - if ( flags == NodeFlags.AfterDescent && accessor.GetNodeKind( childValue ) != NodeKind.Value ) - continue; + stack.Push( value, childValue, index.ToString(), indexSegment ); + } - stack.Push( value, childValue, index.ToString(), indexSegment ); - } + continue; + } - continue; - } + // Object: [name1,name2,...] Names over object + case SelectorKind.Name when nodeKind == NodeKind.Object: + { + if ( accessor.TryGetChildValue( value, selector, selectorKind, out var childValue ) ) + stack.Push( value, childValue, selector, segmentNext ); - // object [name1,name2,...] + continue; + } - if ( nodeKind == NodeKind.Object ) - { - if ( selectorKind == SelectorKind.Slice || selectorKind == SelectorKind.Index ) - continue; + default: + { + throw new NotSupportedException( $"Unsupported {nameof( SelectorKind )}." ); + } - if ( accessor.TryGetChildValue( value, selector, out var childValue ) ) - { - stack.Push( value, childValue, selector, segmentNext ); - } - } - } + } // end switch + } // end for group selector } while ( stack.TryPop( out args ) ); } - [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static bool Truthy( object value ) - { - return value is not null and not IConvertible || Convert.ToBoolean( value, CultureInfo.InvariantCulture ); - } - private static IEnumerable EnumerateSlice( TNode value, string sliceExpr, IValueAccessor accessor ) { var length = accessor.GetArrayLength( value ); @@ -300,11 +312,13 @@ private static IEnumerable EnumerateSlice( TNode value, string sliceExpr, I } } - [DebuggerDisplay( "Parent = {Parent}, Value = {Value}, First = ({Segment?.Selectors?[0]}), IsSingular = {Segment?.IsSingular}, Count = {Segment?.Selectors?.Length}" )] + [DebuggerDisplay( "Parent = {Parent}, Value = {Value}, {Segment}" )] private record struct NodeArgs( TNode Parent, TNode Value, string Key, JsonPathSegment Segment, NodeFlags Flags ); + [DebuggerDisplay( "{_stack}" )] private sealed class NodeArgsStack( int capacity = 16 ) { + [DebuggerBrowsable( DebuggerBrowsableState.RootHidden )] private readonly Stack _stack = new( capacity ); [MethodImpl( MethodImplOptions.AggressiveInlining )] diff --git a/src/Hyperbee.Json/JsonPathQueryParser.cs b/src/Hyperbee.Json/JsonPathQueryParser.cs index c12fc9da..79f3c6f5 100644 --- a/src/Hyperbee.Json/JsonPathQueryParser.cs +++ b/src/Hyperbee.Json/JsonPathQueryParser.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; -using System.Text.RegularExpressions; +using System.Runtime.CompilerServices; +using Hyperbee.Json.Internal; namespace Hyperbee.Json; @@ -12,87 +13,69 @@ public enum SelectorKind Singular = 0x1, Group = 0x2, - // dot notation - Root = 0x4 | Singular, - DotName = 0x8 | Singular, - - // union notation + // selectors + Root = 0x8 | Singular, Name = 0x10 | Singular, Index = 0x20 | Singular, Slice = 0x40 | Group, Filter = 0x80 | Group, - - // Wildcard = 0x100 | Group, Descendant = 0x200 | Group } internal static class JsonPathQueryParser { - private static readonly ConcurrentDictionary JsonPathTokens = new(); + private static readonly ConcurrentDictionary JsonPathQueries = new(); private enum State { Undefined, + Whitespace, Start, DotChild, - UnionStart, - UnionQuotedFinal, - UnionElement, + UnionItem, UnionNext, - UnionFinal, - QuotedName, - FinalSelector, + Finish, Final } - private static string GetSelector( State state, ReadOnlySpan buffer, int start, int stop ) + internal static JsonPathQuery Parse( ReadOnlySpan query, bool allowDotWhitespace = false ) { - var adjust = state == State.FinalSelector || state == State.Final ? 0 : 1; // non-final states have already advanced to the next character, so we need to subtract 1 - var length = stop - start - adjust; - return length <= 0 ? null : buffer.Slice( start, length ).Trim().ToString(); + return Parse( query.ToString(), allowDotWhitespace ); } - private static void InsertToken( ICollection tokens, SelectorDescriptor selector ) + internal static JsonPathQuery Parse( string query, bool allowDotWhitespace = false ) { - if ( selector?.Value == null ) - return; - - InsertToken( tokens, new[] { selector } ); + return JsonPathQueries.GetOrAdd( query, x => QueryFactory( x.AsSpan(), allowDotWhitespace ) ); } - private static void InsertToken( ICollection tokens, SelectorDescriptor[] selectors ) + internal static JsonPathQuery ParseNoCache( ReadOnlySpan query, bool allowDotWhitespace = false ) { - if ( selectors == null || selectors.Length == 0 ) - return; - - tokens.Add( new JsonPathSegment( selectors ) ); + return QueryFactory( query, allowDotWhitespace ); } - internal static JsonPathSegment Parse( string query ) + private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowDotWhitespace = false ) { - return JsonPathTokens.GetOrAdd( query, x => TokenFactory( x.AsSpan() ) ); - } - - internal static JsonPathSegment ParseNoCache( ReadOnlySpan query ) - { - return TokenFactory( query ); - } - - private static JsonPathSegment TokenFactory( ReadOnlySpan query ) - { - var tokens = new List(); - - query = query.TrimEnd(); // remove trailing whitespace to simplify parsing + // RFC - query cannot start or end with whitespace + if ( !query.IsEmpty && (char.IsWhiteSpace( query[0] ) || char.IsWhiteSpace( query[^1] )) ) + throw new NotSupportedException( "Query cannot start or end with whitespace." ); var i = 0; var n = query.Length; var selectorStart = 0; + var inQuotes = false; + var inFilter = false; + var quoteChar = '\''; + bool escaped = false; var bracketDepth = 0; var parenDepth = 0; - var literalDelimiter = '\''; + + char[] whitespaceTerminators = []; + var whiteSpaceReplay = true; + + var tokens = new List(); var selectors = new List(); var state = State.Start; @@ -109,47 +92,62 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) } else // end of input { - state = State.FinalSelector; - c = '\0'; // Add null terminator to signal end of input + if ( state != State.Whitespace ) // whitespace is a sub-state, allow it to exit + state = State.Finish; + c = '\0'; // Set char to null terminator to signal end of input } // process character - + ReadOnlySpan selectorSpan; SelectorKind selectorKind; - string selectorValue; switch ( state ) { case State.Start: switch ( c ) { - case ' ': - case '\t': - break; case '@': // Technically invalid, but allows `@` to work on sub queries without changing tokenizer case '$': - if ( i < n && query[i] != '.' && query[i] != '[' ) - throw new NotSupportedException( "Invalid character after `$`." ); if ( query[^1] == '.' && query[^2] == '.' ) throw new NotSupportedException( "`..` cannot be the last segment." ); - state = State.DotChild; + InsertToken( tokens, new SelectorDescriptor { SelectorKind = SelectorKind.Root, Value = c.ToString() } ); + + whitespaceTerminators = ['.', '[']; + state = State.Whitespace; + returnState = State.DotChild; break; + default: - throw new NotSupportedException( "`$` expected." ); + throw new NotSupportedException( $"Invalid character `{c}` at pos {i - 1}." ); } break; - case State.QuotedName: - if ( c == '\\' ) // handle escaping - { - i++; // advance past the escaped character - } - else if ( c == literalDelimiter ) + case State.Whitespace: + switch ( c ) { - state = returnState; // transition back to the appropriate state + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + + if ( c != '\0' && whitespaceTerminators.Length > 0 && !whitespaceTerminators.Contains( c ) ) + throw new NotSupportedException( $"Invalid character `{c}` at pos {i - 1}." ); + + whitespaceTerminators = []; + state = returnState; // transition back to the appropriate state + selectorStart = i; // start of the next selector + + if ( whiteSpaceReplay ) + i--; // replay character + + whiteSpaceReplay = true; + + break; } break; @@ -157,130 +155,100 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) case State.DotChild: switch ( c ) { - case '[': - state = State.UnionStart; - - selectorValue = GetSelector( state, query, selectorStart, i ); - selectorKind = selectorValue switch + case '[': // end-of-child + selectorSpan = GetSelectorSpan( state, query, selectorStart, i ); + selectorKind = selectorSpan switch { - "$" when tokens.Count != 0 => throw new NotSupportedException( $"Invalid use of root `$` at pos {i - 1}." ), - "$" => SelectorKind.Root, - "@" when tokens.Count != 0 => throw new NotSupportedException( $"Invalid use of local root `$` at pos {i - 1}." ), - "@" => SelectorKind.Root, + "$" => throw new NotSupportedException( $"Invalid use of root `$` at pos {i - 1}." ), + "@" => throw new NotSupportedException( $"Invalid use of local root `$` at pos {i - 1}." ), "*" => SelectorKind.Wildcard, - _ => SelectorKind.DotName + _ => SelectorKind.Name }; - if ( selectorKind == SelectorKind.DotName && selectorValue != null ) + if ( selectorKind == SelectorKind.Name && !selectorSpan.IsEmpty ) { - ThrowIfQuoted( selectorValue ); - ThrowIfNotValidUnquotedName( selectorValue ); + ThrowIfQuoted( selectorSpan ); + ThrowIfInvalidUnquotedName( selectorSpan ); } - InsertToken( tokens, new SelectorDescriptor { SelectorKind = selectorKind, Value = selectorValue } ); + InsertToken( tokens, GetSelectorDescriptor( selectorKind, selectorSpan ) ); + state = State.Whitespace; + whiteSpaceReplay = false; + returnState = State.UnionItem; + bracketDepth = 1; + i--; // replay character break; - case '.': + + case '.': // end-of-child if ( i == n ) throw new NotSupportedException( $"Missing character after `.` at pos {i - 1}." ); - selectorValue = GetSelector( state, query, selectorStart, i ); - selectorKind = selectorValue switch + selectorSpan = GetSelectorSpan( state, query, selectorStart, i ); + selectorKind = selectorSpan switch { - "$" when tokens.Count != 0 => throw new NotSupportedException( $"Invalid use of root `$` at pos {i - 1}." ), - "$" => SelectorKind.Root, - "@" when tokens.Count != 0 => throw new NotSupportedException( $"Invalid use of local root `$` at pos {i - 1}." ), - "@" => SelectorKind.Root, + "$" => throw new NotSupportedException( $"Invalid use of root `$` at pos {i - 1}." ), + "@" => throw new NotSupportedException( $"Invalid use of local root `$` at pos {i - 1}." ), "*" => SelectorKind.Wildcard, - _ => SelectorKind.DotName + _ => SelectorKind.Name }; - if ( selectorKind == SelectorKind.DotName && selectorValue != null ) // can be null after a union + if ( selectorKind == SelectorKind.Name && !selectorSpan.IsEmpty ) // can be null after a union { - ThrowIfQuoted( selectorValue ); - ThrowIfNotValidUnquotedName( selectorValue ); + ThrowIfQuoted( selectorSpan ); + ThrowIfInvalidUnquotedName( selectorSpan ); } - InsertToken( tokens, new SelectorDescriptor { SelectorKind = selectorKind, Value = selectorValue } ); + InsertToken( tokens, GetSelectorDescriptor( selectorKind, selectorSpan ) ); if ( i < n && query[i] == '.' ) // peek next character { - InsertToken( tokens, new SelectorDescriptor { SelectorKind = SelectorKind.Descendant, Value = ".." } ); - i++; + InsertToken( tokens, GetSelectorDescriptor( SelectorKind.Descendant, ".." ) ); + i++; // advance past second `.` } selectorStart = i; break; + case '\'': case '"': throw new NotSupportedException( $"Quoted member names are not allowed in dot notation at pos {i - 1}." ); case ' ': case '\t': - throw new NotSupportedException( $"Invalid whitespace in object notation at pos {i - 1}." ); - case '\0': - state = State.FinalSelector; - i--; // step back to process the last character + case '\n': + case '\r': + if ( !allowDotWhitespace ) // filter dot notation allows whitespace, query dot notation does not + throw new NotSupportedException( $"Invalid whitespace in object notation at pos {i - 1}." ); break; } break; - case State.UnionStart: - switch ( c ) + case State.UnionItem: + + if ( inQuotes ) { - case ' ': - case '\t': - break; - case '*': - state = State.UnionFinal; - InsertToken( tokens, new SelectorDescriptor { SelectorKind = SelectorKind.Wildcard, Value = "*" } ); - break; - case '.': - if ( i > n || query[i] != '.' ) - throw new NotSupportedException( $"Invalid `.` in bracket expression at pos {i - 1}." ); + if ( c == '\\' ) // handle escaping + { + escaped = true; + i++; // advance past the escaped character + } + else if ( c == quoteChar ) + { + inQuotes = false; + } - state = State.UnionFinal; - InsertToken( tokens, new SelectorDescriptor { SelectorKind = SelectorKind.Descendant, Value = ".." } ); - i++; - break; - case '\'': - case '"': - returnState = State.UnionQuotedFinal; - state = State.QuotedName; - literalDelimiter = c; - selectorStart = i - 1; - bracketDepth = 1; - break; - default: - state = State.UnionElement; - i--; // replay character - selectorStart = i; - bracketDepth = 1; - break; + continue; } - break; - - case State.UnionQuotedFinal: switch ( c ) { - case ' ': - case '\t': - break; - case ']': - case ',': - state = State.UnionElement; - i--; // replay character + case '\'': + case '"': + quoteChar = c; + inQuotes = true; break; - default: // invalid characters after end of string - throw new NotSupportedException( $"Invalid bracket literal at pos {i - 1}." ); - } - break; - - case State.UnionElement: - switch ( c ) - { case '[': // handle nested `[` (not called for first bracket) bracketDepth++; break; @@ -292,60 +260,120 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) break; case ',': case ']': - if ( c == ']' && --bracketDepth > 0 ) // handle nested `]` + if ( c == ']' && bracketDepth-- > 1 ) // handle nested `]` + break; + if ( c == ',' && bracketDepth > 1 ) break; if ( parenDepth > 0 ) break; - // get the child item atom - - selectorValue = GetSelector( state, query, selectorStart, i ); + // get the selector + selectorSpan = GetSelectorSpan( state, query, selectorStart, i ); selectorStart = i; - // validate the extracted atom value shape - - if ( string.IsNullOrEmpty( selectorValue ) ) // [] is not valid + if ( selectorSpan.IsEmpty ) // [] is not valid throw new NotSupportedException( "Invalid bracket expression syntax. Bracket expression cannot be empty." ); - selectorKind = GetSelectorKind( selectorValue ); + // validate the selector and get its kind + selectorKind = GetValidSelectorKind( selectorSpan ); - selectorValue = selectorKind switch + // create the selector descriptor + SelectorDescriptor descriptor; + + switch ( selectorKind ) { - SelectorKind.Undefined => throw new NotSupportedException( $"Invalid bracket expression syntax. Unrecognized selector format at pos {i - 1}." ), - SelectorKind.Name => UnquoteAndUnescape( selectorValue ), - _ => selectorValue - }; + case SelectorKind.Undefined: + throw new NotSupportedException( $"Invalid bracket expression syntax. Unrecognized selector format at pos {i - 1}." ); + + case SelectorKind.Name: + ThrowIfInvalidQuotedName( selectorSpan ); + if ( escaped ) + { + var builder = new SpanBuilder( selectorSpan.Length ); + try + { + SpanHelper.Unescape( selectorSpan, ref builder, SpanUnescapeOptions.SingleThenUnquote ); // unescape and then unquote + descriptor = GetSelectorDescriptor( selectorKind, builder, nullable: false ); + escaped = false; + } + finally // ensure builder is disposed + { + builder.Dispose(); + } + } + else + { + descriptor = GetSelectorDescriptor( selectorKind, selectorSpan[1..^1], nullable: false ); // unquote + } + + break; + + case SelectorKind.Filter: + if ( escaped ) + { + var builder = new SpanBuilder( selectorSpan.Length ); + try + { + SpanHelper.Unescape( selectorSpan, ref builder, SpanUnescapeOptions.Mixed ); // unescape one or more strings + descriptor = GetSelectorDescriptor( selectorKind, builder ); + escaped = false; + } + finally // ensure builder is disposed + { + builder.Dispose(); + } + } + else + { + descriptor = GetSelectorDescriptor( selectorKind, selectorSpan ); + } + + break; - selectors.Insert( 0, new SelectorDescriptor { SelectorKind = selectorKind, Value = selectorValue } ); + default: + descriptor = GetSelectorDescriptor( selectorKind, selectorSpan ); + break; + } + + selectors.Insert( 0, descriptor ); // continue parsing the union switch ( c ) { case ',': - state = State.UnionNext; + whitespaceTerminators = []; + state = State.Whitespace; + returnState = State.UnionNext; break; case ']': - if ( i < n && query[i] != '.' && query[i] != '[' ) - throw new NotSupportedException( $"Invalid character after `]` at pos {i - 1}." ); - state = State.DotChild; - InsertToken( tokens, selectors.ToArray() ); + InsertToken( tokens, [.. selectors] ); selectors.Clear(); + + whitespaceTerminators = ['.', '[']; + state = State.Whitespace; + returnState = State.DotChild; break; } break; + + case '?': + if ( !inQuotes ) + inFilter = true; + break; + + case '.': // descent in brackets is illegal except within a filter expr + if ( i < n && query[i] == '.' && !inFilter ) + throw new NotSupportedException( $"Invalid `..` in bracket expression at pos {i - 1}." ); + break; } break; case State.UnionNext: - case State.UnionFinal: switch ( c ) { - case ' ': - case '\t': - break; case ']': if ( i < n && query[i] != '.' && query[i] != '[' ) throw new NotSupportedException( $"Invalid character after `]` at pos {i - 1}." ); @@ -354,45 +382,39 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) break; case '\'': case '"': - if ( state != State.UnionNext ) - throw new NotSupportedException( $"Invalid bracket syntax at pos {i - 1}." ); - - returnState = State.UnionQuotedFinal; - state = State.QuotedName; - literalDelimiter = c; - selectorStart = i - 1; + state = State.UnionItem; + quoteChar = c; + selectorStart = i - 1; // capture the quote character + inQuotes = true; + inFilter = false; break; default: - if ( state != State.UnionNext ) - throw new NotSupportedException( $"Invalid bracket syntax at pos {i - 1}." ); - - state = State.UnionElement; + state = State.UnionItem; i--; // replay character selectorStart = i; - break; } break; - case State.FinalSelector: - selectorValue = GetSelector( state, query, selectorStart, i ); - if ( selectorValue != null ) + case State.Finish: + selectorSpan = GetSelectorSpan( state, query, selectorStart, i ); + if ( !selectorSpan.IsEmpty ) { - var finalKind = selectorValue switch + var finalKind = selectorSpan switch { "*" => SelectorKind.Wildcard, ".." => SelectorKind.Descendant, - _ => SelectorKind.DotName + _ => SelectorKind.Name }; - if ( finalKind == SelectorKind.DotName ) + if ( finalKind == SelectorKind.Name ) { - ThrowIfQuoted( selectorValue ); - ThrowIfNotValidUnquotedName( selectorValue ); + ThrowIfQuoted( selectorSpan ); + ThrowIfInvalidUnquotedName( selectorSpan ); } - InsertToken( tokens, new SelectorDescriptor { SelectorKind = finalKind, Value = selectorValue } ); + InsertToken( tokens, GetSelectorDescriptor( finalKind, selectorSpan ) ); } state = State.Final; @@ -403,168 +425,364 @@ private static JsonPathSegment TokenFactory( ReadOnlySpan query ) } } while ( state != State.Final ); - // return tokenized query as a segment list - - return TokensToSegment( tokens ); + return BuildJsonPathQuery( query, tokens ); } - private static JsonPathSegment TokensToSegment( IList tokens ) + private static JsonPathQuery BuildJsonPathQuery( ReadOnlySpan query, IList segments ) { - if ( tokens == null || tokens.Count == 0 ) - return JsonPathSegment.Final; + if ( segments == null || segments.Count == 0 ) + return new JsonPathQuery( query.ToString(), JsonPathSegment.Final, false ); - // set the next properties + // link the segments - for ( var index = 0; index < tokens.Count; index++ ) + for ( var index = 0; index < segments.Count; index++ ) { - tokens[index].Next = index != tokens.Count - 1 - ? tokens[index + 1] + var segment = segments[index]; + + segment.Next = index != segments.Count - 1 + ? segments[index + 1] : JsonPathSegment.Final; } - return tokens.First(); + var rootSegment = segments.First(); // first segment is the root + var normalized = rootSegment.IsNormalized; + + return new JsonPathQuery( query.ToString(), rootSegment, normalized ); } - private static SelectorKind GetSelectorKind( string selector ) + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static SelectorDescriptor GetSelectorDescriptor( SelectorKind selectorKind, ReadOnlySpan selectorSpan, bool nullable = true ) { + var selectorValue = selectorSpan.IsEmpty && nullable ? null : selectorSpan.ToString(); + return new SelectorDescriptor { SelectorKind = selectorKind, Value = selectorValue }; + } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static SelectorDescriptor GetSelectorDescriptor( SelectorKind selectorKind, in SpanBuilder builder, bool nullable = true ) + { + var selectorValue = builder.IsEmpty && !nullable ? null : builder.ToString(); + return new SelectorDescriptor { SelectorKind = selectorKind, Value = selectorValue }; + } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static ReadOnlySpan GetSelectorSpan( State state, ReadOnlySpan buffer, int start, int stop ) + { + var adjust = state == State.Finish || state == State.Final ? 0 : 1; // non-final states have already advanced to the next character, so we need to subtract 1 + var length = stop - start - adjust; + return length <= 0 ? [] : buffer.Slice( start, length ).Trim(); + } + + private static SelectorKind GetValidSelectorKind( ReadOnlySpan selector ) + { + // selector order matters + + switch ( selector ) + { + case "*": + return SelectorKind.Wildcard; + case "..": + return SelectorKind.Descendant; + } + if ( IsQuoted( selector ) ) return SelectorKind.Name; - if ( IsIndex( selector ) ) + if ( IsIndex( selector, out var isValid, out var reason ) ) + { + if ( !isValid ) // it is an index, but invalid + throw new NotSupportedException( reason ); + return SelectorKind.Index; + } if ( IsFilter( selector ) ) return SelectorKind.Filter; - if ( IsSlice( selector ) ) + if ( IsSlice( selector, out isValid, out reason ) ) + { + if ( !isValid ) // it is a slice, but invalid + throw new NotSupportedException( reason ); + return SelectorKind.Slice; + } - return selector switch - { - "*" => SelectorKind.Wildcard, - ".." => SelectorKind.Descendant, - _ => SelectorKind.Undefined - }; + return SelectorKind.Undefined; + } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static void InsertToken( ICollection tokens, SelectorDescriptor selector ) + { + if ( selector?.Value == null ) // ignore null selectors + return; + + InsertToken( tokens, [selector] ); + } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static void InsertToken( ICollection tokens, SelectorDescriptor[] selectors ) + { + if ( selectors == null || selectors.Length == 0 ) // ignore empty selectors + return; + + tokens.Add( new JsonPathSegment( selectors ) ); + } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static bool IsFilter( ReadOnlySpan input ) + { + // Check if the input starts with '?' and is at least two characters long + return input.Length > 1 && input[0] == '?'; + } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static bool IsIndex( ReadOnlySpan input, out bool isValid, out string reason ) + { + return IsValidNumber( input, out isValid, out reason ); } - private static bool IsSlice( ReadOnlySpan input ) + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static bool IsQuoted( ReadOnlySpan input ) + { + return input.Length > 1 && input[0] == '"' && input[^1] == '"' || input[0] == '\'' && input[^1] == '\''; + } + + private static bool IsSlice( ReadOnlySpan input, out bool isValid, out string reason ) { var index = 0; + isValid = true; + reason = string.Empty; + var partCount = 0; - // First part (optional number) - if ( !IsOptionalNumber( input, ref index ) ) - return false; + SkipWhitespace( input, ref index ); - // Optional colon - if ( index < input.Length && input[index] == ':' ) + do { - index++; + // Validate each part (optional number) + if ( !ValidatePart( input, ref index, ref isValid, ref reason ) ) + { + if ( !isValid ) + reason = "Invalid number in slice."; + return partCount > 0; // Return true if at least one colon was found, indicating it was intended as a slice + } - // Second part (optional number) - if ( !IsOptionalNumber( input, ref index ) ) - return false; + partCount++; - // Optional second colon - if ( index < input.Length && input[index] == ':' ) - { - index++; + SkipWhitespace( input, ref index ); - // Third part (optional number) - if ( !IsOptionalNumber( input, ref index ) ) - return false; - } + // Check for optional colon + if ( index >= input.Length || input[index] != ':' ) + break; + + index++; + SkipWhitespace( input, ref index ); + + } while ( partCount < 3 && index < input.Length ); + + if ( index != input.Length ) + { + isValid = false; + reason = "Unexpected characters at the end of slice."; } - var result = index == input.Length; - return result; + return partCount > 0; // Return true if at least one colon was found, indicating it was intended as a slice - static bool IsOptionalNumber( ReadOnlySpan span, ref int idx ) + // Helper method to validate each part of the slice + static bool ValidatePart( ReadOnlySpan span, ref int idx, ref bool isValid, ref string reason ) { + SkipWhitespace( span, ref idx ); + var start = idx; + var length = span.Length; - if ( idx < span.Length && (span[idx] == '-' || span[idx] == '+') ) + if ( idx < length && (span[idx] == '-') ) idx++; - while ( idx < span.Length && char.IsDigit( span[idx] ) ) + while ( idx < length && char.IsDigit( span[idx] ) ) idx++; - var isValid = idx > start || start == idx; - return isValid; // True if there was a number or just an optional sign + // Allow empty + if ( start == idx ) + return true; + + // Check for leading zeros in unsigned or signed numbers + if ( !IsValidNumber( span[start..idx], out isValid, out reason ) ) + return false; + + var isValidNumber = idx > start || start == idx; + + if ( !isValidNumber ) + { + isValid = false; + reason = "Invalid number format."; + } + + return isValidNumber; // True if there was a number or just an optional sign + } + + // Helper method to skip whitespace + [MethodImpl( MethodImplOptions.AggressiveInlining )] + static void SkipWhitespace( ReadOnlySpan span, ref int idx ) + { + var length = span.Length; + while ( idx < length && char.IsWhiteSpace( span[idx] ) ) + idx++; } } - private static bool IsFilter( ReadOnlySpan input ) + private static bool IsValidNumber( ReadOnlySpan input, out bool isValid, out string reason ) { - if ( input.Length < 2 || input[0] != '?' ) - return false; + isValid = true; + reason = string.Empty; - var start = 1; - var end = input.Length; + var length = input.Length; - if ( input[1] == '(' ) + if ( length == 0 ) { - start = 2; - if ( input[^1] == ')' ) - end--; + isValid = false; + reason = "Input is empty."; + return false; } - var result = start < end; + int start = 0; - return result; - } - - private static bool IsIndex( ReadOnlySpan input ) - { - foreach ( var ch in input ) + // Handle optional leading negative sign + if ( input[0] == '-' ) { - if ( !char.IsDigit( ch ) ) + start = 1; + if ( length == 1 ) + { + isValid = false; + reason = "Invalid negative number."; return false; + } } - return true; - } + // Check for leading zeros + if ( input[start] == '0' && length > (start + 1) ) + { + isValid = false; + reason = "Leading zeros are not allowed."; + return false; + } - private static bool IsQuoted( ReadOnlySpan input ) - { - return (input.Length > 1 && - input[0] == '"' && input[^1] == '"' || - input[0] == '\'' && input[^1] == '\''); + // Check if all remaining characters are digits + for ( var i = start; i < length; i++ ) + { + char c = input[i]; + + if ( c >= '0' && c <= '9' ) + continue; + + isValid = false; + reason = "Input contains non-digit characters."; + return false; + } + + // Try parse to detect overflow + if ( long.TryParse( input, out _ ) ) + return true; // It's a valid number + + isValid = false; + reason = "Input is too large."; + return false; } - private static void ThrowIfQuoted( string value ) + private static void ThrowIfQuoted( ReadOnlySpan value ) { if ( IsQuoted( value ) ) throw new NotSupportedException( $"Quoted member names are not allowed in dot notation: {value}" ); } - private static void ThrowIfNotValidUnquotedName( ReadOnlySpan name ) + private static void ThrowIfInvalidUnquotedName( ReadOnlySpan name ) { if ( name.IsEmpty ) throw new NotSupportedException( "Selector name cannot be null." ); // Validate the first character - if ( !char.IsLetter( name[0] ) && name[0] != '_' && name[0] != '$' ) + if ( !IsValidFirstChar( name[0] ) ) throw new NotSupportedException( $"Selector name cannot start with `{name[0]}`." ); // Validate subsequent characters for ( int i = 1; i < name.Length; i++ ) { - if ( !char.IsLetterOrDigit( name[i] ) && name[i] != '_' && name[i] != '-' && name[i] != '$' ) + if ( !IsValidSubsequentChar( name[i] ) ) throw new NotSupportedException( $"Selector name cannot contain `{name[i]}`." ); } + + return; + + static bool IsValidFirstChar( char c ) => char.IsLetter( c ) || c == '_' || c >= 0x80; + static bool IsValidSubsequentChar( char c ) => char.IsLetterOrDigit( c ) || c == '_' || c == '-' || c >= 0x80; } - private static string UnquoteAndUnescape( string value ) + private static void ThrowIfInvalidQuotedName( ReadOnlySpan name ) { - if ( value.Length <= 0 ) - return null; + if ( name.IsEmpty ) + throw new NotSupportedException( "Selector name cannot be empty." ); - value = value.Trim(); + char quoteChar = name[0]; + if ( name.Length < 2 || (quoteChar != '"' && quoteChar != '\'') || name[^1] != quoteChar ) + throw new NotSupportedException( "Quoted name must start and end with the same quote character, either double or single quote." ); - if ( IsQuoted( value ) ) - return Regex.Unescape( value[1..^1] ); // unquote and unescape + for ( int i = 1; i < name.Length - 1; i++ ) + { + if ( name[i] == '\\' ) + { + // Check if it's a valid escape sequence + if ( i + 1 >= name.Length - 1 || !IsValidEscapeChar( name[i + 1], quoteChar ) ) + throw new NotSupportedException( "Invalid escape sequence in quoted name." ); + + if ( name[i + 1] == 'u' ) + { + // Ensure it's a valid Unicode escape sequence (e.g., \u263a) + if ( i + 5 >= name.Length - 1 || !IsValidUnicodeEscapeSequence( name.Slice( i, 6 ) ) ) + throw new NotSupportedException( "Invalid Unicode escape sequence in quoted name." ); + i += 5; // Skip the Unicode escape sequence + } + else + { + i++; // Skip the regular escape character + } + } + else if ( name[i] == quoteChar ) + { + // Unescaped quotes are not allowed inside the quoted name. + throw new NotSupportedException( "Unescaped quote characters are not allowed inside a quoted name." ); + } + else if ( name[i] <= '\u001F' ) + { + // Control characters (U+0000 to U+001F) are not allowed. + throw new NotSupportedException( $"Control character '\\u{(int) name[i]:x4}' is not allowed in a quoted name." ); + } + } + + return; + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + static bool IsValidEscapeChar( char escapeChar, char quoteChar ) + { + return + escapeChar == quoteChar || + escapeChar == '\\' || + escapeChar == '/' || escapeChar == 'b' || + escapeChar == 'f' || escapeChar == 'n' || + escapeChar == 'r' || escapeChar == 't' || + escapeChar == 'u' + ; + } + + static bool IsValidUnicodeEscapeSequence( ReadOnlySpan span ) + { + if ( span.Length != 6 || span[1] != 'u' ) + return false; - ThrowIfNotValidUnquotedName( value ); - return value; + for ( int i = 2; i < 6; i++ ) + { + if ( !char.IsAsciiHexDigit( span[i] ) ) + return false; + } + + return true; + } } } diff --git a/src/Hyperbee.Json/JsonPathSegment.cs b/src/Hyperbee.Json/JsonPathSegment.cs index 5d95c1c4..f78422df 100644 --- a/src/Hyperbee.Json/JsonPathSegment.cs +++ b/src/Hyperbee.Json/JsonPathSegment.cs @@ -2,6 +2,9 @@ namespace Hyperbee.Json; +public record JsonPathQuery( string Query, JsonPathSegment Segments, bool Normalized ); + + [DebuggerDisplay( "{Value}, SelectorKind = {SelectorKind}" )] public record SelectorDescriptor { @@ -63,6 +66,24 @@ public IEnumerable AsEnumerable() } } + public bool IsNormalized + { + get + { + var current = this; + + while ( current != Final ) + { + if ( !current.IsSingular ) + return false; + + current = current.Next; + } + + return true; + } + } + private bool InitIsSingular() { // singular is one selector that is not a group diff --git a/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs b/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs index ecec7863..c3bb999b 100644 --- a/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs +++ b/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs @@ -5,6 +5,7 @@ namespace Hyperbee.Json; internal static class JsonPathSliceSyntaxHelper { + // parse slice expression and return normalized bounds public static (int Lower, int Upper, int Step) ParseExpression( ReadOnlySpan sliceExpr, int length, bool reverse = false ) { // parse the slice expression and return normalized bounds @@ -34,7 +35,7 @@ public static (int Lower, int Upper, int Step) ParseExpression( ReadOnlySpan part, int length ) + { + // a little magic for overflow and underflow conditions cause by massive steps. + // just scope the step to length + 1 or -length - 1. + + if ( !part.IsEmpty && long.TryParse( part, NumberStyles.Integer, CultureInfo.InvariantCulture, out var n ) ) + { + return n switch + { + > 0 when n > length => length + 1, + < 0 when -n > length => -(length + 1), + _ => (int) n + }; + } + + return 1; + } + static int ParsePart( ReadOnlySpan part, int defaultValue ) { - if ( !part.IsEmpty ) - return int.TryParse( part, NumberStyles.Integer, CultureInfo.InvariantCulture, out var n ) ? n : defaultValue; + if ( !part.IsEmpty && int.TryParse( part, NumberStyles.Integer, CultureInfo.InvariantCulture, out var n ) ) + return n; return defaultValue; } @@ -86,7 +105,7 @@ private static (int Lower, int Upper, int Step) GetBoundedValues( int start, int private static (int Lower, int Upper, int Step) ReverseBoundedValues( int lower, int upper, int step ) { - step *= -1; + step = -step; // adjust upper for correct reverse iteration // upper may not be lower + (n * step) aligned diff --git a/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs b/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs index 1d7b301d..b2df8b6f 100644 --- a/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs +++ b/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs @@ -9,8 +9,8 @@ namespace Hyperbee.Json.Benchmark; public class FilterExpressionParserEvaluator { - private FilterContext _nodeExecutionContext; - private FilterContext _elementExecutionContext; + private FilterParserContext _nodeExecutionParserContext; + private FilterParserContext _elementExecutionParserContext; [Params( "(\"world\" == 'world') && (true || false)" )] public string Filter; @@ -18,20 +18,20 @@ public class FilterExpressionParserEvaluator [GlobalSetup] public void Setup() { - _nodeExecutionContext = new FilterContext( new NodeTypeDescriptor() ); + _nodeExecutionParserContext = new FilterParserContext( new NodeTypeDescriptor() ); - _elementExecutionContext = new FilterContext( new ElementTypeDescriptor() ); + _elementExecutionParserContext = new FilterParserContext( new ElementTypeDescriptor() ); } [Benchmark] public void JsonPathFilterParser_JsonElement() { - FilterParser.Parse( Filter, _elementExecutionContext ); + FilterParser.Parse( Filter, _elementExecutionParserContext ); } [Benchmark] public void JsonPathFilterParser_JsonNode() { - FilterParser.Parse( Filter, _nodeExecutionContext ); + FilterParser.Parse( Filter, _nodeExecutionParserContext ); } } diff --git a/test/Hyperbee.Json.Cts/AssertExtensions.cs b/test/Hyperbee.Json.Cts/AssertExtensions.cs new file mode 100644 index 00000000..4e0f379b --- /dev/null +++ b/test/Hyperbee.Json.Cts/AssertExtensions.cs @@ -0,0 +1,61 @@ +namespace Hyperbee.Json.Cts; + +public static class AssertExtensions +{ + public static void ThrowsAny( Action action ) + where T1 : Exception + where T2 : Exception + { + ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ) ); + } + + public static void ThrowsAny( Action action ) + where T1 : Exception + where T2 : Exception + where T3 : Exception + { + ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ), typeof( T3 ) ); + } + + public static void ThrowsAny( Action action ) + where T1 : Exception + where T2 : Exception + where T3 : Exception + where T4 : Exception + { + ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ), typeof( T3 ), typeof( T4 ) ); + } + + public static void ThrowsAny( Action action ) + where T1 : Exception + where T2 : Exception + where T3 : Exception + where T4 : Exception + where T5 : Exception + { + ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ), typeof( T3 ), typeof( T4 ), typeof( T5 ) ); + } + + private static void ThrowsAnyInternal( Action action, params Type[] expectedExceptionTypes ) + { + Exception? caughtException = null; + + try + { + action(); + } + catch ( Exception? ex ) + { + caughtException = ex; + } + + if ( caughtException == null ) + { + Assert.Fail( $"No exception was thrown. Expected one of: {string.Join( ", ", expectedExceptionTypes.Select( t => t.Name ) )}" ); + } + else if ( !expectedExceptionTypes.Any( e => e.IsInstanceOfType( caughtException ) ) ) + { + Assert.Fail( $"Exception of type {caughtException.GetType().Name} was thrown, but none of the expected types were: {string.Join( ", ", expectedExceptionTypes.Select( t => t.Name ) )}" ); + } + } +} diff --git a/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj b/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj new file mode 100644 index 00000000..5448cc6f --- /dev/null +++ b/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + diff --git a/test/Hyperbee.Json.Cts/TestHelper.cs b/test/Hyperbee.Json.Cts/TestHelper.cs new file mode 100644 index 00000000..d7bfd2c1 --- /dev/null +++ b/test/Hyperbee.Json.Cts/TestHelper.cs @@ -0,0 +1,54 @@ +using System.Text.Json.Nodes; + +namespace Hyperbee.Json.Cts; + +internal static class TestHelper +{ + // Result Helpers + + public static JsonArray ConvertToJsonArraySet( JsonNode jsonNode ) + { + if ( jsonNode is JsonArray jsonArray && jsonArray[0] is JsonArray ) + return jsonArray; + + JsonArray jsonArraySet = new JsonArray( jsonNode ); + + return jsonArraySet; + } + + public static JsonArray ConvertToJsonArray( IEnumerable nodes, bool force = false ) + { + var nodeArray = nodes.ToArray(); + + if ( !force && nodeArray.Length == 1 && nodeArray[0] is JsonArray array ) + return array; + + var jsonArray = new JsonArray(); + + foreach ( var node in nodeArray ) + { + jsonArray.Add( CopyNode( node ) ); + } + + return jsonArray; + + static JsonNode? CopyNode( JsonNode? node ) + { + return node == null ? null : JsonNode.Parse( node.ToJsonString() ); + } + } + + public static bool MatchAny( IEnumerable results, JsonNode expected ) + { + var expectedSet = ConvertToJsonArraySet( expected ); + var compare = ConvertToJsonArray( results ); + return expectedSet.Any( expect => JsonNode.DeepEquals( expect, compare ) ); + } + + public static bool MatchOne( IEnumerable results, JsonNode expected ) + { + var expect = expected as JsonArray; + var compare = ConvertToJsonArray( results, force: true ); + return JsonNode.DeepEquals( expect, compare ); + } +} diff --git a/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs new file mode 100644 index 00000000..43a743a7 --- /dev/null +++ b/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs @@ -0,0 +1,1068 @@ +// This file was auto generated. + +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Cts.Tests +{ + [TestClass] + public class CtsBasicTest + { + + [TestMethod( @"root (1)" )] + public void Test_root_1() + { + var selector = "$"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + [ + "first", + "second" + ] + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"no leading whitespace (2)" )] + public void Test_no_leading_whitespace_2() + { + var selector = " $"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"no trailing whitespace (3)" )] + public void Test_no_trailing_whitespace_3() + { + var selector = "$ "; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"name shorthand (4)" )] + public void Test_name_shorthand_4() + { + var selector = "$.a"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"name shorthand, extended unicode ☺ (5)" )] + public void Test_name_shorthand__extended_unicode___5() + { + var selector = "$.☺"; + var document = JsonNode.Parse( + """ + { + "☺": "A", + "b": "B" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"name shorthand, underscore (6)" )] + public void Test_name_shorthand__underscore_6() + { + var selector = "$._"; + var document = JsonNode.Parse( + """ + { + "_": "A", + "_foo": "B" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"name shorthand, symbol (7)" )] + public void Test_name_shorthand__symbol_7() + { + var selector = "$.&"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"name shorthand, number (8)" )] + public void Test_name_shorthand__number_8() + { + var selector = "$.1"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"name shorthand, absent data (9)" )] + public void Test_name_shorthand__absent_data_9() + { + var selector = "$.c"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"name shorthand, array data (10)" )] + public void Test_name_shorthand__array_data_10() + { + var selector = "$.a"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"wildcard shorthand, object data (11)" )] + public void Test_wildcard_shorthand__object_data_11() + { + var selector = "$.*"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B" + } + """ ); + var results = document.Select( selector ); + var expectOneOf = JsonNode.Parse( + """ + [ + [ + "A", + "B" + ], + [ + "B", + "A" + ] + ] + """ ); + + var match = TestHelper.MatchAny( results, expectOneOf! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"wildcard shorthand, array data (12)" )] + public void Test_wildcard_shorthand__array_data_12() + { + var selector = "$.*"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"wildcard selector, array data (13)" )] + public void Test_wildcard_selector__array_data_13() + { + var selector = "$[*]"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"wildcard shorthand, then name shorthand (14)" )] + public void Test_wildcard_shorthand__then_name_shorthand_14() + { + var selector = "$.*.a"; + var document = JsonNode.Parse( + """ + { + "x": { + "a": "Ax", + "b": "Bx" + }, + "y": { + "a": "Ay", + "b": "By" + } + } + """ ); + var results = document.Select( selector ); + var expectOneOf = JsonNode.Parse( + """ + [ + [ + "Ax", + "Ay" + ], + [ + "Ay", + "Ax" + ] + ] + """ ); + + var match = TestHelper.MatchAny( results, expectOneOf! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors (15)" )] + public void Test_multiple_selectors_15() + { + var selector = "$[0,2]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 2 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, space instead of comma (16)" )] + public void Test_multiple_selectors__space_instead_of_comma_16() + { + var selector = "$[0 2]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"multiple selectors, name and index, array data (17)" )] + public void Test_multiple_selectors__name_and_index__array_data_17() + { + var selector = "$['a',1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, name and index, object data (18)" )] + public void Test_multiple_selectors__name_and_index__object_data_18() + { + var selector = "$['a',1]"; + var document = JsonNode.Parse( + """ + { + "a": 1, + "b": 2 + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, index and slice (19)" )] + public void Test_multiple_selectors__index_and_slice_19() + { + var selector = "$[1,5:7]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1, + 5, + 6 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, index and slice, overlapping (20)" )] + public void Test_multiple_selectors__index_and_slice__overlapping_20() + { + var selector = "$[1,0:3]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1, + 0, + 1, + 2 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, duplicate index (21)" )] + public void Test_multiple_selectors__duplicate_index_21() + { + var selector = "$[1,1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, wildcard and index (22)" )] + public void Test_multiple_selectors__wildcard_and_index_22() + { + var selector = "$[*,1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, wildcard and name (23)" )] + public void Test_multiple_selectors__wildcard_and_name_23() + { + var selector = "$[*,'a']"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B" + } + """ ); + var results = document.Select( selector ); + var expectOneOf = JsonNode.Parse( + """ + [ + [ + "A", + "B", + "A" + ], + [ + "B", + "A", + "A" + ] + ] + """ ); + + var match = TestHelper.MatchAny( results, expectOneOf! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, wildcard and slice (24)" )] + public void Test_multiple_selectors__wildcard_and_slice_24() + { + var selector = "$[*,0:2]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, multiple wildcards (25)" )] + public void Test_multiple_selectors__multiple_wildcards_25() + { + var selector = "$[*,*]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 0, + 1, + 2 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"empty segment (26)" )] + public void Test_empty_segment_26() + { + var selector = "$[]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"descendant segment, index (27)" )] + public void Test_descendant_segment__index_27() + { + var selector = "$..[1]"; + var document = JsonNode.Parse( + """ + { + "o": [ + 0, + 1, + [ + 2, + 3 + ] + ] + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1, + 3 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"descendant segment, name shorthand (28)" )] + public void Test_descendant_segment__name_shorthand_28() + { + var selector = "$..a"; + var document = JsonNode.Parse( + """ + { + "o": [ + { + "a": "b" + }, + { + "a": "c" + } + ] + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "b", + "c" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"descendant segment, wildcard shorthand, array data (29)" )] + public void Test_descendant_segment__wildcard_shorthand__array_data_29() + { + var selector = "$..*"; + var document = JsonNode.Parse( + """ + [ + 0, + 1 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"descendant segment, wildcard selector, array data (30)" )] + public void Test_descendant_segment__wildcard_selector__array_data_30() + { + var selector = "$..[*]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"descendant segment, wildcard selector, nested arrays (31)" )] + public void Test_descendant_segment__wildcard_selector__nested_arrays_31() + { + var selector = "$..[*]"; + var document = JsonNode.Parse( + """ + [ + [ + [ + 1 + ] + ], + [ + 2 + ] + ] + """ ); + var results = document.Select( selector ); + var expectOneOf = JsonNode.Parse( + """ + [ + [ + [ + [ + 1 + ] + ], + [ + 2 + ], + [ + 1 + ], + 1, + 2 + ], + [ + [ + [ + 1 + ] + ], + [ + 2 + ], + [ + 1 + ], + 2, + 1 + ] + ] + """ ); + + var match = TestHelper.MatchAny( results, expectOneOf! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"descendant segment, wildcard selector, nested objects (32)" )] + public void Test_descendant_segment__wildcard_selector__nested_objects_32() + { + var selector = "$..[*]"; + var document = JsonNode.Parse( + """ + { + "a": { + "c": { + "e": 1 + } + }, + "b": { + "d": 2 + } + } + """ ); + var results = document.Select( selector ); + var expectOneOf = JsonNode.Parse( + """ + [ + [ + { + "c": { + "e": 1 + } + }, + { + "d": 2 + }, + { + "e": 1 + }, + 1, + 2 + ], + [ + { + "c": { + "e": 1 + } + }, + { + "d": 2 + }, + { + "e": 1 + }, + 2, + 1 + ], + [ + { + "c": { + "e": 1 + } + }, + { + "d": 2 + }, + 2, + { + "e": 1 + }, + 1 + ], + [ + { + "d": 2 + }, + { + "c": { + "e": 1 + } + }, + { + "e": 1 + }, + 1, + 2 + ], + [ + { + "d": 2 + }, + { + "c": { + "e": 1 + } + }, + { + "e": 1 + }, + 2, + 1 + ], + [ + { + "d": 2 + }, + { + "c": { + "e": 1 + } + }, + 2, + { + "e": 1 + }, + 1 + ] + ] + """ ); + + var match = TestHelper.MatchAny( results, expectOneOf! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"descendant segment, wildcard shorthand, object data (33)" )] + public void Test_descendant_segment__wildcard_shorthand__object_data_33() + { + var selector = "$..*"; + var document = JsonNode.Parse( + """ + { + "a": "b" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "b" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"descendant segment, wildcard shorthand, nested data (34)" )] + public void Test_descendant_segment__wildcard_shorthand__nested_data_34() + { + var selector = "$..*"; + var document = JsonNode.Parse( + """ + { + "o": [ + { + "a": "b" + } + ] + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + [ + { + "a": "b" + } + ], + { + "a": "b" + }, + "b" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"descendant segment, multiple selectors (35)" )] + public void Test_descendant_segment__multiple_selectors_35() + { + var selector = "$..['a','d']"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "b", + "e", + "c", + "f" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"descendant segment, object traversal, multiple selectors (36)" )] + public void Test_descendant_segment__object_traversal__multiple_selectors_36() + { + var selector = "$..['a','d']"; + var document = JsonNode.Parse( + """ + { + "x": { + "a": "b", + "d": "e" + }, + "y": { + "a": "c", + "d": "f" + } + } + """ ); + var results = document.Select( selector ); + var expectOneOf = JsonNode.Parse( + """ + [ + [ + "b", + "e", + "c", + "f" + ], + [ + "c", + "f", + "b", + "e" + ] + ] + """ ); + + var match = TestHelper.MatchAny( results, expectOneOf! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"bald descendant segment (37)" )] + public void Test_bald_descendant_segment_37() + { + var selector = "$.."; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + } +} + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs new file mode 100644 index 00000000..d3287966 --- /dev/null +++ b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs @@ -0,0 +1,3884 @@ +// This file was auto generated. + +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Cts.Tests +{ + [TestClass] + public class CtsFilterTest + { + + [TestMethod( @"existence, without segments (1)" )] + public void Test_existence__without_segments_1() + { + var selector = "$[?@]"; + var document = JsonNode.Parse( + """ + { + "a": 1, + "b": null + } + """ ); + var results = document.Select( selector ); + var expectOneOf = JsonNode.Parse( + """ + [ + [ + 1, + null + ], + [ + null, + 1 + ] + ] + """ ); + + var match = TestHelper.MatchAny( results, expectOneOf! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"existence (2)" )] + public void Test_existence_2() + { + var selector = "$[?@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"existence, present with null (3)" )] + public void Test_existence__present_with_null_3() + { + var selector = "$[?@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": null, + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": null, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals string, single quotes (4)" )] + public void Test_equals_string__single_quotes_4() + { + var selector = "$[?@.a=='b']"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals numeric string, single quotes (5)" )] + public void Test_equals_numeric_string__single_quotes_5() + { + var selector = "$[?@.a=='1']"; + var document = JsonNode.Parse( + """ + [ + { + "a": "1", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "1", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals string, double quotes (6)" )] + public void Test_equals_string__double_quotes_6() + { + var selector = "$[?@.a==\"b\"]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals numeric string, double quotes (7)" )] + public void Test_equals_numeric_string__double_quotes_7() + { + var selector = "$[?@.a==\"1\"]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "1", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "1", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number (8)" )] + public void Test_equals_number_8() + { + var selector = "$[?@.a==1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": 2, + "d": "f" + }, + { + "a": "1", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals null (9)" )] + public void Test_equals_null_9() + { + var selector = "$[?@.a==null]"; + var document = JsonNode.Parse( + """ + [ + { + "a": null, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": null, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals null, absent from data (10)" )] + public void Test_equals_null__absent_from_data_10() + { + var selector = "$[?@.a==null]"; + var document = JsonNode.Parse( + """ + [ + { + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals true (11)" )] + public void Test_equals_true_11() + { + var selector = "$[?@.a==true]"; + var document = JsonNode.Parse( + """ + [ + { + "a": true, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": true, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals false (12)" )] + public void Test_equals_false_12() + { + var selector = "$[?@.a==false]"; + var document = JsonNode.Parse( + """ + [ + { + "a": false, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": false, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals self (13)" )] + public void Test_equals_self_13() + { + var selector = "$[?@==@]"; + var document = JsonNode.Parse( + """ + [ + 1, + null, + true, + { + "a": "b" + }, + [ + false + ] + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1, + null, + true, + { + "a": "b" + }, + [ + false + ] + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"deep equality, arrays (14)" )] + public void Test_deep_equality__arrays_14() + { + var selector = "$[?@.a==@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": false, + "b": [ + 1, + 2 + ] + }, + { + "a": [ + [ + 1, + [ + 2 + ] + ] + ], + "b": [ + [ + 1, + [ + 2 + ] + ] + ] + }, + { + "a": [ + [ + 1, + [ + 2 + ] + ] + ], + "b": [ + [ + [ + 2 + ], + 1 + ] + ] + }, + { + "a": [ + [ + 1, + [ + 2 + ] + ] + ], + "b": [ + [ + 1, + 2 + ] + ] + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": [ + [ + 1, + [ + 2 + ] + ] + ], + "b": [ + [ + 1, + [ + 2 + ] + ] + ] + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"deep equality, objects (15)" )] + public void Test_deep_equality__objects_15() + { + var selector = "$[?@.a==@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": false, + "b": { + "x": 1, + "y": { + "z": 1 + } + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "x": 1, + "y": { + "z": 1 + } + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "y": { + "z": 1 + }, + "x": 1 + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "x": 1 + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "x": 1, + "y": { + "z": 2 + } + } + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "x": 1, + "y": { + "z": 1 + } + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "y": { + "z": 1 + }, + "x": 1 + } + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals string, single quotes (16)" )] + public void Test_not_equals_string__single_quotes_16() + { + var selector = "$[?@.a!='b']"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals numeric string, single quotes (17)" )] + public void Test_not_equals_numeric_string__single_quotes_17() + { + var selector = "$[?@.a!='1']"; + var document = JsonNode.Parse( + """ + [ + { + "a": "1", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals string, single quotes, different type (18)" )] + public void Test_not_equals_string__single_quotes__different_type_18() + { + var selector = "$[?@.a!='b']"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals string, double quotes (19)" )] + public void Test_not_equals_string__double_quotes_19() + { + var selector = "$[?@.a!=\"b\"]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals numeric string, double quotes (20)" )] + public void Test_not_equals_numeric_string__double_quotes_20() + { + var selector = "$[?@.a!=\"1\"]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "1", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals string, double quotes, different types (21)" )] + public void Test_not_equals_string__double_quotes__different_types_21() + { + var selector = "$[?@.a!=\"b\"]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals number (22)" )] + public void Test_not_equals_number_22() + { + var selector = "$[?@.a!=1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "e" + }, + { + "a": 2, + "d": "f" + }, + { + "a": "1", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 2, + "d": "f" + }, + { + "a": "1", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals number, different types (23)" )] + public void Test_not_equals_number__different_types_23() + { + var selector = "$[?@.a!=1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals null (24)" )] + public void Test_not_equals_null_24() + { + var selector = "$[?@.a!=null]"; + var document = JsonNode.Parse( + """ + [ + { + "a": null, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals null, absent from data (25)" )] + public void Test_not_equals_null__absent_from_data_25() + { + var selector = "$[?@.a!=null]"; + var document = JsonNode.Parse( + """ + [ + { + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals true (26)" )] + public void Test_not_equals_true_26() + { + var selector = "$[?@.a!=true]"; + var document = JsonNode.Parse( + """ + [ + { + "a": true, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"not-equals false (27)" )] + public void Test_not_equals_false_27() + { + var selector = "$[?@.a!=false]"; + var document = JsonNode.Parse( + """ + [ + { + "a": false, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"less than string, single quotes (28)" )] + public void Test_less_than_string__single_quotes_28() + { + var selector = "$[?@.a<'c']"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"less than string, double quotes (29)" )] + public void Test_less_than_string__double_quotes_29() + { + var selector = "$[?@.a<\"c\"]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"less than number (30)" )] + public void Test_less_than_number_30() + { + var selector = "$[?@.a<10]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "e" + }, + { + "a": 10, + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": 20, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"less than null (31)" )] + public void Test_less_than_null_31() + { + var selector = "$[?@.a( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"non-singular query in comparison, all children (65)" )] + public void Test_non_singular_query_in_comparison__all_children_65() + { + var selector = "$[?@[*]==0]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"non-singular query in comparison, descendants (66)" )] + public void Test_non_singular_query_in_comparison__descendants_66() + { + var selector = "$[?@..a==0]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"non-singular query in comparison, combined (67)" )] + public void Test_non_singular_query_in_comparison__combined_67() + { + var selector = "$[?@.a[*].a==0]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"nested (68)" )] + public void Test_nested_68() + { + var selector = "$[?@[?@>1]]"; + var document = JsonNode.Parse( + """ + [ + [ + 0 + ], + [ + 0, + 1 + ], + [ + 0, + 1, + 2 + ], + [ + 42 + ] + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + [ + 0, + 1, + 2 + ], + [ + 42 + ] + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"name segment on primitive, selects nothing (69)" )] + public void Test_name_segment_on_primitive__selects_nothing_69() + { + var selector = "$[?@.a == 1]"; + var document = JsonNode.Parse( + """ + { + "a": 1 + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"name segment on array, selects nothing (70)" )] + public void Test_name_segment_on_array__selects_nothing_70() + { + var selector = "$[?@['0'] == 5]"; + var document = JsonNode.Parse( + """ + [ + [ + 5, + 6 + ] + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"index segment on object, selects nothing (71)" )] + public void Test_index_segment_on_object__selects_nothing_71() + { + var selector = "$[?@[0] == 5]"; + var document = JsonNode.Parse( + """ + [ + { + "0": 5 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"relative non-singular query, index, equal (72)" )] + public void Test_relative_non_singular_query__index__equal_72() + { + var selector = "$[?(@[0, 0]==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, index, not equal (73)" )] + public void Test_relative_non_singular_query__index__not_equal_73() + { + var selector = "$[?(@[0, 0]!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, index, less-or-equal (74)" )] + public void Test_relative_non_singular_query__index__less_or_equal_74() + { + var selector = "$[?(@[0, 0]<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, name, equal (75)" )] + public void Test_relative_non_singular_query__name__equal_75() + { + var selector = "$[?(@['a', 'a']==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, name, not equal (76)" )] + public void Test_relative_non_singular_query__name__not_equal_76() + { + var selector = "$[?(@['a', 'a']!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, name, less-or-equal (77)" )] + public void Test_relative_non_singular_query__name__less_or_equal_77() + { + var selector = "$[?(@['a', 'a']<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, combined, equal (78)" )] + public void Test_relative_non_singular_query__combined__equal_78() + { + var selector = "$[?(@[0, '0']==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, combined, not equal (79)" )] + public void Test_relative_non_singular_query__combined__not_equal_79() + { + var selector = "$[?(@[0, '0']!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, combined, less-or-equal (80)" )] + public void Test_relative_non_singular_query__combined__less_or_equal_80() + { + var selector = "$[?(@[0, '0']<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, wildcard, equal (81)" )] + public void Test_relative_non_singular_query__wildcard__equal_81() + { + var selector = "$[?(@.*==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, wildcard, not equal (82)" )] + public void Test_relative_non_singular_query__wildcard__not_equal_82() + { + var selector = "$[?(@.*!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, wildcard, less-or-equal (83)" )] + public void Test_relative_non_singular_query__wildcard__less_or_equal_83() + { + var selector = "$[?(@.*<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, slice, equal (84)" )] + public void Test_relative_non_singular_query__slice__equal_84() + { + var selector = "$[?(@[0:0]==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, slice, not equal (85)" )] + public void Test_relative_non_singular_query__slice__not_equal_85() + { + var selector = "$[?(@[0:0]!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"relative non-singular query, slice, less-or-equal (86)" )] + public void Test_relative_non_singular_query__slice__less_or_equal_86() + { + var selector = "$[?(@[0:0]<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, index, equal (87)" )] + public void Test_absolute_non_singular_query__index__equal_87() + { + var selector = "$[?($[0, 0]==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, index, not equal (88)" )] + public void Test_absolute_non_singular_query__index__not_equal_88() + { + var selector = "$[?($[0, 0]!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, index, less-or-equal (89)" )] + public void Test_absolute_non_singular_query__index__less_or_equal_89() + { + var selector = "$[?($[0, 0]<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, name, equal (90)" )] + public void Test_absolute_non_singular_query__name__equal_90() + { + var selector = "$[?($['a', 'a']==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, name, not equal (91)" )] + public void Test_absolute_non_singular_query__name__not_equal_91() + { + var selector = "$[?($['a', 'a']!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, name, less-or-equal (92)" )] + public void Test_absolute_non_singular_query__name__less_or_equal_92() + { + var selector = "$[?($['a', 'a']<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, combined, equal (93)" )] + public void Test_absolute_non_singular_query__combined__equal_93() + { + var selector = "$[?($[0, '0']==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, combined, not equal (94)" )] + public void Test_absolute_non_singular_query__combined__not_equal_94() + { + var selector = "$[?($[0, '0']!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, combined, less-or-equal (95)" )] + public void Test_absolute_non_singular_query__combined__less_or_equal_95() + { + var selector = "$[?($[0, '0']<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, wildcard, equal (96)" )] + public void Test_absolute_non_singular_query__wildcard__equal_96() + { + var selector = "$[?($.*==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, wildcard, not equal (97)" )] + public void Test_absolute_non_singular_query__wildcard__not_equal_97() + { + var selector = "$[?($.*!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, wildcard, less-or-equal (98)" )] + public void Test_absolute_non_singular_query__wildcard__less_or_equal_98() + { + var selector = "$[?($.*<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, slice, equal (99)" )] + public void Test_absolute_non_singular_query__slice__equal_99() + { + var selector = "$[?($[0:0]==42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, slice, not equal (100)" )] + public void Test_absolute_non_singular_query__slice__not_equal_100() + { + var selector = "$[?($[0:0]!=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"absolute non-singular query, slice, less-or-equal (101)" )] + public void Test_absolute_non_singular_query__slice__less_or_equal_101() + { + var selector = "$[?($[0:0]<=42)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"multiple selectors (102)" )] + public void Test_multiple_selectors_102() + { + var selector = "$[?@.a,?@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, comparison (103)" )] + public void Test_multiple_selectors__comparison_103() + { + var selector = "$[?@.a=='b',?@.b=='x']"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, overlapping (104)" )] + public void Test_multiple_selectors__overlapping_104() + { + var selector = "$[?@.a,?@.d]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, filter and index (105)" )] + public void Test_multiple_selectors__filter_and_index_105() + { + var selector = "$[?@.a,1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, filter and wildcard (106)" )] + public void Test_multiple_selectors__filter_and_wildcard_106() + { + var selector = "$[?@.a,*]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, filter and slice (107)" )] + public void Test_multiple_selectors__filter_and_slice_107() + { + var selector = "$[?@.a,1:]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + }, + { + "g": "h" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + }, + { + "g": "h" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"multiple selectors, comparison filter, index and slice (108)" )] + public void Test_multiple_selectors__comparison_filter__index_and_slice_108() + { + var selector = "$[1, ?@.a=='b', 1:]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "b": "c", + "d": "f" + }, + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number, zero and negative zero (109)" )] + public void Test_equals_number__zero_and_negative_zero_109() + { + var selector = "$[?@.a==-0]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 0, + "d": "e" + }, + { + "a": 0.1, + "d": "f" + }, + { + "a": "0", + "d": "g" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 0, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number, with and without decimal fraction (110)" )] + public void Test_equals_number__with_and_without_decimal_fraction_110() + { + var selector = "$[?@.a==1.0]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "e" + }, + { + "a": 2, + "d": "f" + }, + { + "a": "1", + "d": "g" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number, exponent (111)" )] + public void Test_equals_number__exponent_111() + { + var selector = "$[?@.a==1e2]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 100, + "d": "e" + }, + { + "a": 100.1, + "d": "f" + }, + { + "a": "100", + "d": "g" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 100, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number, positive exponent (112)" )] + public void Test_equals_number__positive_exponent_112() + { + var selector = "$[?@.a==1e+2]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 100, + "d": "e" + }, + { + "a": 100.1, + "d": "f" + }, + { + "a": "100", + "d": "g" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 100, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number, negative exponent (113)" )] + public void Test_equals_number__negative_exponent_113() + { + var selector = "$[?@.a==1e-2]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 0.01, + "d": "e" + }, + { + "a": 0.02, + "d": "f" + }, + { + "a": "0.01", + "d": "g" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 0.01, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number, decimal fraction (114)" )] + public void Test_equals_number__decimal_fraction_114() + { + var selector = "$[?@.a==1.1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1.1, + "d": "e" + }, + { + "a": 1, + "d": "f" + }, + { + "a": "1.1", + "d": "g" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1.1, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number, decimal fraction, no fractional digit (115)" )] + public void Test_equals_number__decimal_fraction__no_fractional_digit_115() + { + var selector = "$[?@.a==1.]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"equals number, decimal fraction, exponent (116)" )] + public void Test_equals_number__decimal_fraction__exponent_116() + { + var selector = "$[?@.a==1.1e2]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 110, + "d": "e" + }, + { + "a": 110.1, + "d": "f" + }, + { + "a": "110", + "d": "g" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 110, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number, decimal fraction, positive exponent (117)" )] + public void Test_equals_number__decimal_fraction__positive_exponent_117() + { + var selector = "$[?@.a==1.1e+2]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 110, + "d": "e" + }, + { + "a": 110.1, + "d": "f" + }, + { + "a": "110", + "d": "g" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 110, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals number, decimal fraction, negative exponent (118)" )] + public void Test_equals_number__decimal_fraction__negative_exponent_118() + { + var selector = "$[?@.a==1.1e-2]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 0.011, + "d": "e" + }, + { + "a": 0.012, + "d": "f" + }, + { + "a": "0.011", + "d": "g" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 0.011, + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals, special nothing (119)" )] + public void Test_equals__special_nothing_119() + { + var selector = "$.values[?length(@.a) == value($..c)]"; + var document = JsonNode.Parse( + """ + { + "c": "cd", + "values": [ + { + "a": "ab" + }, + { + "c": "d" + }, + { + "a": null + } + ] + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "c": "d" + }, + { + "a": null + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals, empty node list and empty node list (120)" )] + public void Test_equals__empty_node_list_and_empty_node_list_120() + { + var selector = "$[?@.a == @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "c": 3 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"equals, empty node list and special nothing (121)" )] + public void Test_equals__empty_node_list_and_special_nothing_121() + { + var selector = "$[?@.a == length(@.b)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"object data (122)" )] + public void Test_object_data_122() + { + var selector = "$[?@<3]"; + var document = JsonNode.Parse( + """ + { + "a": 1, + "b": 2, + "c": 3 + } + """ ); + var results = document.Select( selector ); + var expectOneOf = JsonNode.Parse( + """ + [ + [ + 1, + 2 + ], + [ + 2, + 1 + ] + ] + """ ); + + var match = TestHelper.MatchAny( results, expectOneOf! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"and binds more tightly than or (123)" )] + public void Test_and_binds_more_tightly_than_or_123() + { + var selector = "$[?@.a || @.b && @.c]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2, + "c": 3 + }, + { + "c": 3 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2, + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"left to right evaluation (124)" )] + public void Test_left_to_right_evaluation_124() + { + var selector = "$[?@.a && @.b || @.c]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 3 + }, + { + "b": 1, + "c": 3 + }, + { + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 3 + }, + { + "b": 1, + "c": 3 + }, + { + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"group terms, left (125)" )] + public void Test_group_terms__left_125() + { + var selector = "$[?(@.a || @.b) && @.c]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 3 + }, + { + "b": 2, + "c": 3 + }, + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "c": 3 + }, + { + "b": 2, + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"group terms, right (126)" )] + public void Test_group_terms__right_126() + { + var selector = "$[?@.a && (@.b || @.c)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 2 + }, + { + "b": 2 + }, + { + "c": 2 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 2 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"string literal, single quote in double quotes (127)" )] + public void Test_string_literal__single_quote_in_double_quotes_127() + { + var selector = "$[?@ == \"quoted' literal\"]"; + var document = JsonNode.Parse( + """ + [ + "quoted' literal", + "a", + "quoted\\' literal" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "quoted' literal" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"string literal, double quote in single quotes (128)" )] + public void Test_string_literal__double_quote_in_single_quotes_128() + { + var selector = "$[?@ == 'quoted\" literal']"; + var document = JsonNode.Parse( + """ + [ + "quoted\" literal", + "a", + "quoted\\\" literal", + "'quoted\" literal'" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "quoted\" literal" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"string literal, escaped single quote in single quotes (129)" )] + public void Test_string_literal__escaped_single_quote_in_single_quotes_129() + { + var selector = "$[?@ == 'quoted\\' literal']"; + var document = JsonNode.Parse( + """ + [ + "quoted' literal", + "a", + "quoted\\' literal", + "'quoted\" literal'" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "quoted' literal" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"string literal, escaped double quote in double quotes (130)" )] + public void Test_string_literal__escaped_double_quote_in_double_quotes_130() + { + var selector = "$[?@ == \"quoted\\\" literal\"]"; + var document = JsonNode.Parse( + """ + [ + "quoted\" literal", + "a", + "quoted\\\" literal", + "'quoted\" literal'" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "quoted\" literal" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"literal true must be compared (131)" )] + public void Test_literal_true_must_be_compared_131() + { + var selector = "$[?true]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"literal false must be compared (132)" )] + public void Test_literal_false_must_be_compared_132() + { + var selector = "$[?false]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"literal string must be compared (133)" )] + public void Test_literal_string_must_be_compared_133() + { + var selector = "$[?'abc']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"literal int must be compared (134)" )] + public void Test_literal_int_must_be_compared_134() + { + var selector = "$[?2]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"literal float must be compared (135)" )] + public void Test_literal_float_must_be_compared_135() + { + var selector = "$[?2.2]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"literal null must be compared (136)" )] + public void Test_literal_null_must_be_compared_136() + { + var selector = "$[?null]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"and, literals must be compared (137)" )] + public void Test_and__literals_must_be_compared_137() + { + var selector = "$[?true && false]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"or, literals must be compared (138)" )] + public void Test_or__literals_must_be_compared_138() + { + var selector = "$[?true || false]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"and, right hand literal must be compared (139)" )] + public void Test_and__right_hand_literal_must_be_compared_139() + { + var selector = "$[?true == false && false]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"or, right hand literal must be compared (140)" )] + public void Test_or__right_hand_literal_must_be_compared_140() + { + var selector = "$[?true == false || false]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"and, left hand literal must be compared (141)" )] + public void Test_and__left_hand_literal_must_be_compared_141() + { + var selector = "$[?false && true == false]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"or, left hand literal must be compared (142)" )] + public void Test_or__left_hand_literal_must_be_compared_142() + { + var selector = "$[?false || true == false]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + } +} + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs new file mode 100644 index 00000000..6def80e7 --- /dev/null +++ b/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs @@ -0,0 +1,1772 @@ +// This file was auto generated. + +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Cts.Tests +{ + [TestClass] + public class CtsFunctionsTest + { + + [TestMethod( @"count, count function (1)" )] + public void Test_count__count_function_1() + { + var selector = "$[?count(@..*)>2]"; + var document = JsonNode.Parse( + """ + [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ], + "d": "f" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ], + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"count, single-node arg (2)" )] + public void Test_count__single_node_arg_2() + { + var selector = "$[?count(@.a)>1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ], + "d": "f" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"count, multiple-selector arg (3)" )] + public void Test_count__multiple_selector_arg_3() + { + var selector = "$[?count(@['a','d'])>1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ], + "d": "f" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": [ + 1 + ], + "d": "f" + }, + { + "a": 1, + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"count, non-query arg, number (4)" )] + public void Test_count__non_query_arg__number_4() + { + var selector = "$[?count(1)>2]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"count, non-query arg, string (5)" )] + public void Test_count__non_query_arg__string_5() + { + var selector = "$[?count('string')>2]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"count, non-query arg, true (6)" )] + public void Test_count__non_query_arg__true_6() + { + var selector = "$[?count(true)>2]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"count, non-query arg, false (7)" )] + public void Test_count__non_query_arg__false_7() + { + var selector = "$[?count(false)>2]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"count, non-query arg, null (8)" )] + public void Test_count__non_query_arg__null_8() + { + var selector = "$[?count(null)>2]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"count, result must be compared (9)" )] + public void Test_count__result_must_be_compared_9() + { + var selector = "$[?count(@..*)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"count, no params (10)" )] + public void Test_count__no_params_10() + { + var selector = "$[?count()==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"count, too many params (11)" )] + public void Test_count__too_many_params_11() + { + var selector = "$[?count(@.a,@.b)==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"length, string data (12)" )] + public void Test_length__string_data_12() + { + var selector = "$[?length(@.a)>=2]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "ab" + }, + { + "a": "d" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "ab" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"length, string data, unicode (13)" )] + public void Test_length__string_data__unicode_13() + { + var selector = "$[?length(@)==2]"; + var document = JsonNode.Parse( + """ + [ + "☺", + "☺☺", + "☺☺☺", + "ж", + "жж", + "жжж", + "磨", + "阿美", + "形声字" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "☺☺", + "жж", + "阿美" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"length, array data (14)" )] + public void Test_length__array_data_14() + { + var selector = "$[?length(@.a)>=2]"; + var document = JsonNode.Parse( + """ + [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ] + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": [ + 1, + 2, + 3 + ] + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"length, missing data (15)" )] + public void Test_length__missing_data_15() + { + var selector = "$[?length(@.a)>=2]"; + var document = JsonNode.Parse( + """ + [ + { + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"length, number arg (16)" )] + public void Test_length__number_arg_16() + { + var selector = "$[?length(1)>=2]"; + var document = JsonNode.Parse( + """ + [ + { + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"length, true arg (17)" )] + public void Test_length__true_arg_17() + { + var selector = "$[?length(true)>=2]"; + var document = JsonNode.Parse( + """ + [ + { + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"length, false arg (18)" )] + public void Test_length__false_arg_18() + { + var selector = "$[?length(false)>=2]"; + var document = JsonNode.Parse( + """ + [ + { + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"length, null arg (19)" )] + public void Test_length__null_arg_19() + { + var selector = "$[?length(null)>=2]"; + var document = JsonNode.Parse( + """ + [ + { + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"length, result must be compared (20)" )] + public void Test_length__result_must_be_compared_20() + { + var selector = "$[?length(@.a)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"length, no params (21)" )] + public void Test_length__no_params_21() + { + var selector = "$[?length()==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"length, too many params (22)" )] + public void Test_length__too_many_params_22() + { + var selector = "$[?length(@.a,@.b)==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"length, non-singular query arg (23)" )] + public void Test_length__non_singular_query_arg_23() + { + var selector = "$[?length(@.*)<3]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"length, arg is a function expression (24)" )] + public void Test_length__arg_is_a_function_expression_24() + { + var selector = "$.values[?length(@.a)==length(value($..c))]"; + var document = JsonNode.Parse( + """ + { + "c": "cd", + "values": [ + { + "a": "ab" + }, + { + "a": "d" + } + ] + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "ab" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"length, arg is special nothing (25)" )] + public void Test_length__arg_is_special_nothing_25() + { + var selector = "$[?length(value(@.a))>0]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "ab" + }, + { + "c": "d" + }, + { + "a": null + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "ab" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, found match (26)" )] + public void Test_match__found_match_26() + { + var selector = "$[?match(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "ab" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "ab" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, double quotes (27)" )] + public void Test_match__double_quotes_27() + { + var selector = "$[?match(@.a, \"a.*\")]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "ab" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "ab" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, regex from the document (28)" )] + public void Test_match__regex_from_the_document_28() + { + var selector = "$.values[?match(@, $.regex)]"; + var document = JsonNode.Parse( + """ + { + "regex": "b.?b", + "values": [ + "abc", + "bcd", + "bab", + "bba", + "bbab", + "b", + true, + [], + {} + ] + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "bab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, don't select match (29)" )] + public void Test_match__don_t_select_match_29() + { + var selector = "$[?!match(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "ab" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, not a match (30)" )] + public void Test_match__not_a_match_30() + { + var selector = "$[?match(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, select non-match (31)" )] + public void Test_match__select_non_match_31() + { + var selector = "$[?!match(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, non-string first arg (32)" )] + public void Test_match__non_string_first_arg_32() + { + var selector = "$[?match(1, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, non-string second arg (33)" )] + public void Test_match__non_string_second_arg_33() + { + var selector = "$[?match(@.a, 1)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, filter, match function, unicode char class, uppercase (34)" )] + public void Test_match__filter__match_function__unicode_char_class__uppercase_34() + { + var selector = "$[?match(@, '\\\\p{Lu}')]"; + var document = JsonNode.Parse( + """ + [ + "ж", + "Ж", + "1", + "жЖ", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "Ж" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, filter, match function, unicode char class negated, uppercase (35)" )] + public void Test_match__filter__match_function__unicode_char_class_negated__uppercase_35() + { + var selector = "$[?match(@, '\\\\P{Lu}')]"; + var document = JsonNode.Parse( + """ + [ + "ж", + "Ж", + "1", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ж", + "1" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, filter, match function, unicode, surrogate pair (36)" )] + public void Test_match__filter__match_function__unicode__surrogate_pair_36() + { + var selector = "$[?match(@, 'a.b')]"; + var document = JsonNode.Parse( + """ + [ + "a𐄁b", + "ab", + "1", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "a𐄁b" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, dot matcher on \u2028 (37)" )] + public void Test_match__dot_matcher_on__u2028_37() + { + var selector = "$[?match(@, '.')]"; + var document = JsonNode.Parse( + """ + [ + "\u2028", + "\r", + "\n", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "\u2028" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, dot matcher on \u2029 (38)" )] + public void Test_match__dot_matcher_on__u2029_38() + { + var selector = "$[?match(@, '.')]"; + var document = JsonNode.Parse( + """ + [ + "\u2029", + "\r", + "\n", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "\u2029" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, result cannot be compared (39)" )] + public void Test_match__result_cannot_be_compared_39() + { + var selector = "$[?match(@.a, 'a.*')==true]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"match, too few params (40)" )] + public void Test_match__too_few_params_40() + { + var selector = "$[?match(@.a)==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"match, too many params (41)" )] + public void Test_match__too_many_params_41() + { + var selector = "$[?match(@.a,@.b,@.c)==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"match, arg is a function expression (42)" )] + public void Test_match__arg_is_a_function_expression_42() + { + var selector = "$.values[?match(@.a, value($..['regex']))]"; + var document = JsonNode.Parse( + """ + { + "regex": "a.*", + "values": [ + { + "a": "ab" + }, + { + "a": "ba" + } + ] + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "ab" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, dot in character class (43)" )] + public void Test_match__dot_in_character_class_43() + { + var selector = "$[?match(@, 'a[.b]c')]"; + var document = JsonNode.Parse( + """ + [ + "abc", + "a.c", + "axc" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "abc", + "a.c" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, escaped dot (44)" )] + public void Test_match__escaped_dot_44() + { + var selector = "$[?match(@, 'a\\\\.c')]"; + var document = JsonNode.Parse( + """ + [ + "abc", + "a.c", + "axc" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "a.c" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, escaped backslash before dot (45)" )] + public void Test_match__escaped_backslash_before_dot_45() + { + var selector = "$[?match(@, 'a\\\\\\\\.c')]"; + var document = JsonNode.Parse( + """ + [ + "abc", + "a.c", + "axc", + "a\\\u2028c" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "a\\\u2028c" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, escaped left square bracket (46)" )] + public void Test_match__escaped_left_square_bracket_46() + { + var selector = "$[?match(@, 'a\\\\[.c')]"; + var document = JsonNode.Parse( + """ + [ + "abc", + "a.c", + "a[\u2028c" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "a[\u2028c" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, escaped right square bracket (47)" )] + public void Test_match__escaped_right_square_bracket_47() + { + var selector = "$[?match(@, 'a[\\\\].]c')]"; + var document = JsonNode.Parse( + """ + [ + "abc", + "a.c", + "a\u2028c", + "a]c" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "a.c", + "a]c" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, explicit caret (48)" )] + public void Test_match__explicit_caret_48() + { + var selector = "$[?match(@, '^ab.*')]"; + var document = JsonNode.Parse( + """ + [ + "abc", + "axc", + "ab", + "xab" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "abc", + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"match, explicit dollar (49)" )] + public void Test_match__explicit_dollar_49() + { + var selector = "$[?match(@, '.*bc$')]"; + var document = JsonNode.Parse( + """ + [ + "abc", + "axc", + "ab", + "abcx" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "abc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, at the end (50)" )] + public void Test_search__at_the_end_50() + { + var selector = "$[?search(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "the end is ab" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "the end is ab" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, double quotes (51)" )] + public void Test_search__double_quotes_51() + { + var selector = "$[?search(@.a, \"a.*\")]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "the end is ab" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "the end is ab" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, at the start (52)" )] + public void Test_search__at_the_start_52() + { + var selector = "$[?search(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "ab is at the start" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "ab is at the start" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, in the middle (53)" )] + public void Test_search__in_the_middle_53() + { + var selector = "$[?search(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "contains two matches" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "contains two matches" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, regex from the document (54)" )] + public void Test_search__regex_from_the_document_54() + { + var selector = "$.values[?search(@, $.regex)]"; + var document = JsonNode.Parse( + """ + { + "regex": "b.?b", + "values": [ + "abc", + "bcd", + "bab", + "bba", + "bbab", + "b", + true, + [], + {} + ] + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "bab", + "bba", + "bbab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, don't select match (55)" )] + public void Test_search__don_t_select_match_55() + { + var selector = "$[?!search(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "contains two matches" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, not a match (56)" )] + public void Test_search__not_a_match_56() + { + var selector = "$[?search(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, select non-match (57)" )] + public void Test_search__select_non_match_57() + { + var selector = "$[?!search(@.a, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, non-string first arg (58)" )] + public void Test_search__non_string_first_arg_58() + { + var selector = "$[?search(1, 'a.*')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, non-string second arg (59)" )] + public void Test_search__non_string_second_arg_59() + { + var selector = "$[?search(@.a, 1)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "bc" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, filter, search function, unicode char class, uppercase (60)" )] + public void Test_search__filter__search_function__unicode_char_class__uppercase_60() + { + var selector = "$[?search(@, '\\\\p{Lu}')]"; + var document = JsonNode.Parse( + """ + [ + "ж", + "Ж", + "1", + "жЖ", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "Ж", + "жЖ" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, filter, search function, unicode char class negated, uppercase (61)" )] + public void Test_search__filter__search_function__unicode_char_class_negated__uppercase_61() + { + var selector = "$[?search(@, '\\\\P{Lu}')]"; + var document = JsonNode.Parse( + """ + [ + "ж", + "Ж", + "1", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ж", + "1" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, filter, search function, unicode, surrogate pair (62)" )] + public void Test_search__filter__search_function__unicode__surrogate_pair_62() + { + var selector = "$[?search(@, 'a.b')]"; + var document = JsonNode.Parse( + """ + [ + "a𐄁bc", + "abc", + "1", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "a𐄁bc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, dot matcher on \u2028 (63)" )] + public void Test_search__dot_matcher_on__u2028_63() + { + var selector = "$[?search(@, '.')]"; + var document = JsonNode.Parse( + """ + [ + "\u2028", + "\r\u2028\n", + "\r", + "\n", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "\u2028", + "\r\u2028\n" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, dot matcher on \u2029 (64)" )] + public void Test_search__dot_matcher_on__u2029_64() + { + var selector = "$[?search(@, '.')]"; + var document = JsonNode.Parse( + """ + [ + "\u2029", + "\r\u2029\n", + "\r", + "\n", + true, + [], + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "\u2029", + "\r\u2029\n" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, result cannot be compared (65)" )] + public void Test_search__result_cannot_be_compared_65() + { + var selector = "$[?search(@.a, 'a.*')==true]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"search, too few params (66)" )] + public void Test_search__too_few_params_66() + { + var selector = "$[?search(@.a)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"search, too many params (67)" )] + public void Test_search__too_many_params_67() + { + var selector = "$[?search(@.a,@.b,@.c)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"search, arg is a function expression (68)" )] + public void Test_search__arg_is_a_function_expression_68() + { + var selector = "$.values[?search(@, value($..['regex']))]"; + var document = JsonNode.Parse( + """ + { + "regex": "b.?b", + "values": [ + "abc", + "bcd", + "bab", + "bba", + "bbab", + "b", + true, + [], + {} + ] + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "bab", + "bba", + "bbab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, dot in character class (69)" )] + public void Test_search__dot_in_character_class_69() + { + var selector = "$[?search(@, 'a[.b]c')]"; + var document = JsonNode.Parse( + """ + [ + "x abc y", + "x a.c y", + "x axc y" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "x abc y", + "x a.c y" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, escaped dot (70)" )] + public void Test_search__escaped_dot_70() + { + var selector = "$[?search(@, 'a\\\\.c')]"; + var document = JsonNode.Parse( + """ + [ + "x abc y", + "x a.c y", + "x axc y" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "x a.c y" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, escaped backslash before dot (71)" )] + public void Test_search__escaped_backslash_before_dot_71() + { + var selector = "$[?search(@, 'a\\\\\\\\.c')]"; + var document = JsonNode.Parse( + """ + [ + "x abc y", + "x a.c y", + "x axc y", + "x a\\\u2028c y" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "x a\\\u2028c y" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, escaped left square bracket (72)" )] + public void Test_search__escaped_left_square_bracket_72() + { + var selector = "$[?search(@, 'a\\\\[.c')]"; + var document = JsonNode.Parse( + """ + [ + "x abc y", + "x a.c y", + "x a[\u2028c y" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "x a[\u2028c y" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"search, escaped right square bracket (73)" )] + public void Test_search__escaped_right_square_bracket_73() + { + var selector = "$[?search(@, 'a[\\\\].]c')]"; + var document = JsonNode.Parse( + """ + [ + "x abc y", + "x a.c y", + "x a\u2028c y", + "x a]c y" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "x a.c y", + "x a]c y" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"value, single-value nodelist (74)" )] + public void Test_value__single_value_nodelist_74() + { + var selector = "$[?value(@.*)==4]"; + var document = JsonNode.Parse( + """ + [ + [ + 4 + ], + { + "foo": 4 + }, + [ + 5 + ], + { + "foo": 5 + }, + 4 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + [ + 4 + ], + { + "foo": 4 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"value, multi-value nodelist (75)" )] + public void Test_value__multi_value_nodelist_75() + { + var selector = "$[?value(@.*)==4]"; + var document = JsonNode.Parse( + """ + [ + [ + 4, + 4 + ], + { + "foo": 4, + "bar": 4 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"value, too few params (76)" )] + public void Test_value__too_few_params_76() + { + var selector = "$[?value()==4]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"value, too many params (77)" )] + public void Test_value__too_many_params_77() + { + var selector = "$[?value(@.a,@.b)==4]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"value, result must be compared (78)" )] + public void Test_value__result_must_be_compared_78() + { + var selector = "$[?value(@.a)]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + } +} + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs new file mode 100644 index 00000000..7a63537e --- /dev/null +++ b/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs @@ -0,0 +1,203 @@ +// This file was auto generated. + +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Cts.Tests +{ + [TestClass] + public class CtsIndexSelectorTest + { + + [TestMethod( @"first element (1)" )] + public void Test_first_element_1() + { + var selector = "$[0]"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "first" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"second element (2)" )] + public void Test_second_element_2() + { + var selector = "$[1]"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "second" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"out of bound (3)" )] + public void Test_out_of_bound_3() + { + var selector = "$[2]"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"overflowing index (4)" )] + public void Test_overflowing_index_4() + { + var selector = "$[231584178474632390847141970017375815706539969331281128078915168015826259279872]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"not actually an index, overflowing index leads into general text (5)" )] + public void Test_not_actually_an_index__overflowing_index_leads_into_general_text_5() + { + var selector = "$[231584178474632390847141970017375815706539969331281128078915168SomeRandomText]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"negative (6)" )] + public void Test_negative_6() + { + var selector = "$[-1]"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "second" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"more negative (7)" )] + public void Test_more_negative_7() + { + var selector = "$[-2]"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "first" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative out of bound (8)" )] + public void Test_negative_out_of_bound_8() + { + var selector = "$[-3]"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"on object (9)" )] + public void Test_on_object_9() + { + var selector = "$[0]"; + var document = JsonNode.Parse( + """ + { + "foo": 1 + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"leading 0 (10)" )] + public void Test_leading_0_10() + { + var selector = "$[01]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"leading -0 (11)" )] + public void Test_leading__0_11() + { + var selector = "$[-01]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + } +} + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs new file mode 100644 index 00000000..efca01e3 --- /dev/null +++ b/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs @@ -0,0 +1,1393 @@ +// This file was auto generated. + +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Cts.Tests +{ + [TestClass] + public class CtsNameSelectorTest + { + + [TestMethod( @"double quotes (1)" )] + public void Test_double_quotes_1() + { + var selector = "$[\"a\"]"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, absent data (2)" )] + public void Test_double_quotes__absent_data_2() + { + var selector = "$[\"c\"]"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, array data (3)" )] + public void Test_double_quotes__array_data_3() + { + var selector = "$[\"a\"]"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, embedded U+0000 (4)" )] + public void Test_double_quotes__embedded_U_0000_4() + { + var selector = "$[\"\u0000\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0001 (5)" )] + public void Test_double_quotes__embedded_U_0001_5() + { + var selector = "$[\"\u0001\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0002 (6)" )] + public void Test_double_quotes__embedded_U_0002_6() + { + var selector = "$[\"\u0002\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0003 (7)" )] + public void Test_double_quotes__embedded_U_0003_7() + { + var selector = "$[\"\u0003\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0004 (8)" )] + public void Test_double_quotes__embedded_U_0004_8() + { + var selector = "$[\"\u0004\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0005 (9)" )] + public void Test_double_quotes__embedded_U_0005_9() + { + var selector = "$[\"\u0005\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0006 (10)" )] + public void Test_double_quotes__embedded_U_0006_10() + { + var selector = "$[\"\u0006\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0007 (11)" )] + public void Test_double_quotes__embedded_U_0007_11() + { + var selector = "$[\"\u0007\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0008 (12)" )] + public void Test_double_quotes__embedded_U_0008_12() + { + var selector = "$[\"\b\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0009 (13)" )] + public void Test_double_quotes__embedded_U_0009_13() + { + var selector = "$[\"\t\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+000A (14)" )] + public void Test_double_quotes__embedded_U_000A_14() + { + var selector = "$[\"\n\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+000B (15)" )] + public void Test_double_quotes__embedded_U_000B_15() + { + var selector = "$[\"\u000b\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+000C (16)" )] + public void Test_double_quotes__embedded_U_000C_16() + { + var selector = "$[\"\f\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+000D (17)" )] + public void Test_double_quotes__embedded_U_000D_17() + { + var selector = "$[\"\r\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+000E (18)" )] + public void Test_double_quotes__embedded_U_000E_18() + { + var selector = "$[\"\u000e\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+000F (19)" )] + public void Test_double_quotes__embedded_U_000F_19() + { + var selector = "$[\"\u000f\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0010 (20)" )] + public void Test_double_quotes__embedded_U_0010_20() + { + var selector = "$[\"\u0010\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0011 (21)" )] + public void Test_double_quotes__embedded_U_0011_21() + { + var selector = "$[\"\u0011\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0012 (22)" )] + public void Test_double_quotes__embedded_U_0012_22() + { + var selector = "$[\"\u0012\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0013 (23)" )] + public void Test_double_quotes__embedded_U_0013_23() + { + var selector = "$[\"\u0013\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0014 (24)" )] + public void Test_double_quotes__embedded_U_0014_24() + { + var selector = "$[\"\u0014\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0015 (25)" )] + public void Test_double_quotes__embedded_U_0015_25() + { + var selector = "$[\"\u0015\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0016 (26)" )] + public void Test_double_quotes__embedded_U_0016_26() + { + var selector = "$[\"\u0016\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0017 (27)" )] + public void Test_double_quotes__embedded_U_0017_27() + { + var selector = "$[\"\u0017\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0018 (28)" )] + public void Test_double_quotes__embedded_U_0018_28() + { + var selector = "$[\"\u0018\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0019 (29)" )] + public void Test_double_quotes__embedded_U_0019_29() + { + var selector = "$[\"\u0019\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+001A (30)" )] + public void Test_double_quotes__embedded_U_001A_30() + { + var selector = "$[\"\u001a\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+001B (31)" )] + public void Test_double_quotes__embedded_U_001B_31() + { + var selector = "$[\"\u001b\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+001C (32)" )] + public void Test_double_quotes__embedded_U_001C_32() + { + var selector = "$[\"\u001c\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+001D (33)" )] + public void Test_double_quotes__embedded_U_001D_33() + { + var selector = "$[\"\u001d\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+001E (34)" )] + public void Test_double_quotes__embedded_U_001E_34() + { + var selector = "$[\"\u001e\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+001F (35)" )] + public void Test_double_quotes__embedded_U_001F_35() + { + var selector = "$[\"\u001f\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded U+0020 (36)" )] + public void Test_double_quotes__embedded_U_0020_36() + { + var selector = "$[\" \"]"; + var document = JsonNode.Parse( + """ + { + " ": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped double quote (37)" )] + public void Test_double_quotes__escaped_double_quote_37() + { + var selector = "$[\"\\\"\"]"; + var document = JsonNode.Parse( + """ + { + "\"": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped reverse solidus (38)" )] + public void Test_double_quotes__escaped_reverse_solidus_38() + { + var selector = "$[\"\\\\\"]"; + var document = JsonNode.Parse( + """ + { + "\\": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped solidus (39)" )] + public void Test_double_quotes__escaped_solidus_39() + { + var selector = "$[\"\\/\"]"; + var document = JsonNode.Parse( + """ + { + "/": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped backspace (40)" )] + public void Test_double_quotes__escaped_backspace_40() + { + var selector = "$[\"\\b\"]"; + var document = JsonNode.Parse( + """ + { + "\b": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped form feed (41)" )] + public void Test_double_quotes__escaped_form_feed_41() + { + var selector = "$[\"\\f\"]"; + var document = JsonNode.Parse( + """ + { + "\f": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped line feed (42)" )] + public void Test_double_quotes__escaped_line_feed_42() + { + var selector = "$[\"\\n\"]"; + var document = JsonNode.Parse( + """ + { + "\n": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped carriage return (43)" )] + public void Test_double_quotes__escaped_carriage_return_43() + { + var selector = "$[\"\\r\"]"; + var document = JsonNode.Parse( + """ + { + "\r": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped tab (44)" )] + public void Test_double_quotes__escaped_tab_44() + { + var selector = "$[\"\\t\"]"; + var document = JsonNode.Parse( + """ + { + "\t": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped ☺, upper case hex (45)" )] + public void Test_double_quotes__escaped____upper_case_hex_45() + { + var selector = "$[\"\\u263A\"]"; + var document = JsonNode.Parse( + """ + { + "☺": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, escaped ☺, lower case hex (46)" )] + public void Test_double_quotes__escaped____lower_case_hex_46() + { + var selector = "$[\"\\u263a\"]"; + var document = JsonNode.Parse( + """ + { + "☺": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, surrogate pair 𝄞 (47)" )] + public void Test_double_quotes__surrogate_pair____47() + { + var selector = "$[\"\\uD834\\uDD1E\"]"; + var document = JsonNode.Parse( + """ + { + "𝄞": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, surrogate pair 😀 (48)" )] + public void Test_double_quotes__surrogate_pair____48() + { + var selector = "$[\"\\uD83D\\uDE00\"]"; + var document = JsonNode.Parse( + """ + { + "😀": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"double quotes, invalid escaped single quote (49)" )] + public void Test_double_quotes__invalid_escaped_single_quote_49() + { + var selector = "$[\"\\'\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, embedded double quote (50)" )] + public void Test_double_quotes__embedded_double_quote_50() + { + var selector = "$[\"\"\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, incomplete escape (51)" )] + public void Test_double_quotes__incomplete_escape_51() + { + var selector = "$[\"\\\"]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes (52)" )] + public void Test_single_quotes_52() + { + var selector = "$['a']"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, absent data (53)" )] + public void Test_single_quotes__absent_data_53() + { + var selector = "$['c']"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, array data (54)" )] + public void Test_single_quotes__array_data_54() + { + var selector = "$['a']"; + var document = JsonNode.Parse( + """ + [ + "first", + "second" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, embedded U+0000 (55)" )] + public void Test_single_quotes__embedded_U_0000_55() + { + var selector = "$['\u0000']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0001 (56)" )] + public void Test_single_quotes__embedded_U_0001_56() + { + var selector = "$['\u0001']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0002 (57)" )] + public void Test_single_quotes__embedded_U_0002_57() + { + var selector = "$['\u0002']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0003 (58)" )] + public void Test_single_quotes__embedded_U_0003_58() + { + var selector = "$['\u0003']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0004 (59)" )] + public void Test_single_quotes__embedded_U_0004_59() + { + var selector = "$['\u0004']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0005 (60)" )] + public void Test_single_quotes__embedded_U_0005_60() + { + var selector = "$['\u0005']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0006 (61)" )] + public void Test_single_quotes__embedded_U_0006_61() + { + var selector = "$['\u0006']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0007 (62)" )] + public void Test_single_quotes__embedded_U_0007_62() + { + var selector = "$['\u0007']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0008 (63)" )] + public void Test_single_quotes__embedded_U_0008_63() + { + var selector = "$['\b']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0009 (64)" )] + public void Test_single_quotes__embedded_U_0009_64() + { + var selector = "$['\t']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+000A (65)" )] + public void Test_single_quotes__embedded_U_000A_65() + { + var selector = "$['\n']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+000B (66)" )] + public void Test_single_quotes__embedded_U_000B_66() + { + var selector = "$['\u000b']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+000C (67)" )] + public void Test_single_quotes__embedded_U_000C_67() + { + var selector = "$['\f']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+000D (68)" )] + public void Test_single_quotes__embedded_U_000D_68() + { + var selector = "$['\r']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+000E (69)" )] + public void Test_single_quotes__embedded_U_000E_69() + { + var selector = "$['\u000e']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+000F (70)" )] + public void Test_single_quotes__embedded_U_000F_70() + { + var selector = "$['\u000f']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0010 (71)" )] + public void Test_single_quotes__embedded_U_0010_71() + { + var selector = "$['\u0010']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0011 (72)" )] + public void Test_single_quotes__embedded_U_0011_72() + { + var selector = "$['\u0011']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0012 (73)" )] + public void Test_single_quotes__embedded_U_0012_73() + { + var selector = "$['\u0012']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0013 (74)" )] + public void Test_single_quotes__embedded_U_0013_74() + { + var selector = "$['\u0013']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0014 (75)" )] + public void Test_single_quotes__embedded_U_0014_75() + { + var selector = "$['\u0014']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0015 (76)" )] + public void Test_single_quotes__embedded_U_0015_76() + { + var selector = "$['\u0015']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0016 (77)" )] + public void Test_single_quotes__embedded_U_0016_77() + { + var selector = "$['\u0016']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0017 (78)" )] + public void Test_single_quotes__embedded_U_0017_78() + { + var selector = "$['\u0017']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0018 (79)" )] + public void Test_single_quotes__embedded_U_0018_79() + { + var selector = "$['\u0018']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0019 (80)" )] + public void Test_single_quotes__embedded_U_0019_80() + { + var selector = "$['\u0019']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+001A (81)" )] + public void Test_single_quotes__embedded_U_001A_81() + { + var selector = "$['\u001a']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+001B (82)" )] + public void Test_single_quotes__embedded_U_001B_82() + { + var selector = "$['\u001b']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+001C (83)" )] + public void Test_single_quotes__embedded_U_001C_83() + { + var selector = "$['\u001c']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+001D (84)" )] + public void Test_single_quotes__embedded_U_001D_84() + { + var selector = "$['\u001d']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+001E (85)" )] + public void Test_single_quotes__embedded_U_001E_85() + { + var selector = "$['\u001e']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+001F (86)" )] + public void Test_single_quotes__embedded_U_001F_86() + { + var selector = "$['\u001f']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded U+0020 (87)" )] + public void Test_single_quotes__embedded_U_0020_87() + { + var selector = "$[' ']"; + var document = JsonNode.Parse( + """ + { + " ": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped single quote (88)" )] + public void Test_single_quotes__escaped_single_quote_88() + { + var selector = "$['\\'']"; + var document = JsonNode.Parse( + """ + { + "'": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped reverse solidus (89)" )] + public void Test_single_quotes__escaped_reverse_solidus_89() + { + var selector = "$['\\\\']"; + var document = JsonNode.Parse( + """ + { + "\\": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped solidus (90)" )] + public void Test_single_quotes__escaped_solidus_90() + { + var selector = "$['\\/']"; + var document = JsonNode.Parse( + """ + { + "/": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped backspace (91)" )] + public void Test_single_quotes__escaped_backspace_91() + { + var selector = "$['\\b']"; + var document = JsonNode.Parse( + """ + { + "\b": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped form feed (92)" )] + public void Test_single_quotes__escaped_form_feed_92() + { + var selector = "$['\\f']"; + var document = JsonNode.Parse( + """ + { + "\f": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped line feed (93)" )] + public void Test_single_quotes__escaped_line_feed_93() + { + var selector = "$['\\n']"; + var document = JsonNode.Parse( + """ + { + "\n": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped carriage return (94)" )] + public void Test_single_quotes__escaped_carriage_return_94() + { + var selector = "$['\\r']"; + var document = JsonNode.Parse( + """ + { + "\r": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped tab (95)" )] + public void Test_single_quotes__escaped_tab_95() + { + var selector = "$['\\t']"; + var document = JsonNode.Parse( + """ + { + "\t": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped ☺, upper case hex (96)" )] + public void Test_single_quotes__escaped____upper_case_hex_96() + { + var selector = "$['\\u263A']"; + var document = JsonNode.Parse( + """ + { + "☺": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, escaped ☺, lower case hex (97)" )] + public void Test_single_quotes__escaped____lower_case_hex_97() + { + var selector = "$['\\u263a']"; + var document = JsonNode.Parse( + """ + { + "☺": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, surrogate pair 𝄞 (98)" )] + public void Test_single_quotes__surrogate_pair____98() + { + var selector = "$['\\uD834\\uDD1E']"; + var document = JsonNode.Parse( + """ + { + "𝄞": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, surrogate pair 😀 (99)" )] + public void Test_single_quotes__surrogate_pair____99() + { + var selector = "$['\\uD83D\\uDE00']"; + var document = JsonNode.Parse( + """ + { + "😀": "A" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "A" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, invalid escaped double quote (100)" )] + public void Test_single_quotes__invalid_escaped_double_quote_100() + { + var selector = "$['\\\"']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, embedded single quote (101)" )] + public void Test_single_quotes__embedded_single_quote_101() + { + var selector = "$[''']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"single quotes, incomplete escape (102)" )] + public void Test_single_quotes__incomplete_escape_102() + { + var selector = "$['\\']"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"double quotes, empty (103)" )] + public void Test_double_quotes__empty_103() + { + var selector = "$[\"\"]"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B", + "": "C" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "C" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"single quotes, empty (104)" )] + public void Test_single_quotes__empty_104() + { + var selector = "$['']"; + var document = JsonNode.Parse( + """ + { + "a": "A", + "b": "B", + "": "C" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "C" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + } +} + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs new file mode 100644 index 00000000..8c588d50 --- /dev/null +++ b/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs @@ -0,0 +1,1057 @@ +// This file was auto generated. + +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Cts.Tests +{ + [TestClass] + public class CtsSliceSelectorTest + { + + [TestMethod( @"slice selector (1)" )] + public void Test_slice_selector_1() + { + var selector = "$[1:3]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1, + 2 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice selector with step (2)" )] + public void Test_slice_selector_with_step_2() + { + var selector = "$[1:6:2]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1, + 3, + 5 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice selector with everything omitted, short form (3)" )] + public void Test_slice_selector_with_everything_omitted__short_form_3() + { + var selector = "$[:]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice selector with everything omitted, long form (4)" )] + public void Test_slice_selector_with_everything_omitted__long_form_4() + { + var selector = "$[::]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice selector with start omitted (5)" )] + public void Test_slice_selector_with_start_omitted_5() + { + var selector = "$[:2]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice selector with start and end omitted (6)" )] + public void Test_slice_selector_with_start_and_end_omitted_6() + { + var selector = "$[::2]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 2, + 4, + 6, + 8 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative step with default start and end (7)" )] + public void Test_negative_step_with_default_start_and_end_7() + { + var selector = "$[::-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 3, + 2, + 1, + 0 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative step with default start (8)" )] + public void Test_negative_step_with_default_start_8() + { + var selector = "$[:0:-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 3, + 2, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative step with default end (9)" )] + public void Test_negative_step_with_default_end_9() + { + var selector = "$[2::-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 1, + 0 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"larger negative step (10)" )] + public void Test_larger_negative_step_10() + { + var selector = "$[::-2]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 3, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative range with default step (11)" )] + public void Test_negative_range_with_default_step_11() + { + var selector = "$[-1:-3]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative range with negative step (12)" )] + public void Test_negative_range_with_negative_step_12() + { + var selector = "$[-1:-3:-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 9, + 8 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative range with larger negative step (13)" )] + public void Test_negative_range_with_larger_negative_step_13() + { + var selector = "$[-1:-6:-2]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 9, + 7, + 5 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"larger negative range with larger negative step (14)" )] + public void Test_larger_negative_range_with_larger_negative_step_14() + { + var selector = "$[-1:-7:-2]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 9, + 7, + 5 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative from, positive to (15)" )] + public void Test_negative_from__positive_to_15() + { + var selector = "$[-5:7]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 5, + 6 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative from (16)" )] + public void Test_negative_from_16() + { + var selector = "$[-2:]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 8, + 9 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"positive from, negative to (17)" )] + public void Test_positive_from__negative_to_17() + { + var selector = "$[1:-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative from, positive to, negative step (18)" )] + public void Test_negative_from__positive_to__negative_step_18() + { + var selector = "$[-1:1:-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"positive from, negative to, negative step (19)" )] + public void Test_positive_from__negative_to__negative_step_19() + { + var selector = "$[7:-5:-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 7, + 6 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"too many colons (20)" )] + public void Test_too_many_colons_20() + { + var selector = "$[1:2:3:4]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"non-integer array index (21)" )] + public void Test_non_integer_array_index_21() + { + var selector = "$[1:2:a]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"zero step (22)" )] + public void Test_zero_step_22() + { + var selector = "$[1:2:0]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"empty range (23)" )] + public void Test_empty_range_23() + { + var selector = "$[2:2]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice selector with everything omitted with empty array (24)" )] + public void Test_slice_selector_with_everything_omitted_with_empty_array_24() + { + var selector = "$[:]"; + var document = JsonNode.Parse( + """ + [] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"negative step with empty array (25)" )] + public void Test_negative_step_with_empty_array_25() + { + var selector = "$[::-1]"; + var document = JsonNode.Parse( + """ + [] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"maximal range with positive step (26)" )] + public void Test_maximal_range_with_positive_step_26() + { + var selector = "$[0:10]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"maximal range with negative step (27)" )] + public void Test_maximal_range_with_negative_step_27() + { + var selector = "$[9:0:-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"excessively large to value (28)" )] + public void Test_excessively_large_to_value_28() + { + var selector = "$[2:113667776004]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"excessively small from value (29)" )] + public void Test_excessively_small_from_value_29() + { + var selector = "$[-113667776004:1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 0 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"excessively large from value with negative step (30)" )] + public void Test_excessively_large_from_value_with_negative_step_30() + { + var selector = "$[113667776004:0:-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2, + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"excessively small to value with negative step (31)" )] + public void Test_excessively_small_to_value_with_negative_step_31() + { + var selector = "$[3:-113667776004:-1]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 3, + 2, + 1, + 0 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"excessively large step (32)" )] + public void Test_excessively_large_step_32() + { + var selector = "$[1:10:113667776004]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 1 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"excessively small step (33)" )] + public void Test_excessively_small_step_33() + { + var selector = "$[-1:-10:-113667776004]"; + var document = JsonNode.Parse( + """ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 9 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"overflowing to value (34)" )] + public void Test_overflowing_to_value_34() + { + var selector = "$[2:231584178474632390847141970017375815706539969331281128078915168015826259279872]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"underflowing from value (35)" )] + public void Test_underflowing_from_value_35() + { + var selector = "$[-231584178474632390847141970017375815706539969331281128078915168015826259279872:1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"overflowing from value with negative step (36)" )] + public void Test_overflowing_from_value_with_negative_step_36() + { + var selector = "$[231584178474632390847141970017375815706539969331281128078915168015826259279872:0:-1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"underflowing to value with negative step (37)" )] + public void Test_underflowing_to_value_with_negative_step_37() + { + var selector = "$[3:-231584178474632390847141970017375815706539969331281128078915168015826259279872:-1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"overflowing step (38)" )] + public void Test_overflowing_step_38() + { + var selector = "$[1:10:231584178474632390847141970017375815706539969331281128078915168015826259279872]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"underflowing step (39)" )] + public void Test_underflowing_step_39() + { + var selector = "$[-1:-10:-231584178474632390847141970017375815706539969331281128078915168015826259279872]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + } +} + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs new file mode 100644 index 00000000..c892887d --- /dev/null +++ b/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs @@ -0,0 +1,4929 @@ +// This file was auto generated. + +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Cts.Tests +{ + [TestClass] + public class CtsWhitespaceTest + { + + [TestMethod( @"filter, space between question mark and expression (1)" )] + public void Test_filter__space_between_question_mark_and_expression_1() + { + var selector = "$[? @.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, newline between question mark and expression (2)" )] + public void Test_filter__newline_between_question_mark_and_expression_2() + { + var selector = "$[?\n@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, tab between question mark and expression (3)" )] + public void Test_filter__tab_between_question_mark_and_expression_3() + { + var selector = "$[?\t@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, return between question mark and expression (4)" )] + public void Test_filter__return_between_question_mark_and_expression_4() + { + var selector = "$[?\r@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, space between question mark and parenthesized expression (5)" )] + public void Test_filter__space_between_question_mark_and_parenthesized_expression_5() + { + var selector = "$[? (@.a)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, newline between question mark and parenthesized expression (6)" )] + public void Test_filter__newline_between_question_mark_and_parenthesized_expression_6() + { + var selector = "$[?\n(@.a)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, tab between question mark and parenthesized expression (7)" )] + public void Test_filter__tab_between_question_mark_and_parenthesized_expression_7() + { + var selector = "$[?\t(@.a)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, return between question mark and parenthesized expression (8)" )] + public void Test_filter__return_between_question_mark_and_parenthesized_expression_8() + { + var selector = "$[?\r(@.a)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, space between parenthesized expression and bracket (9)" )] + public void Test_filter__space_between_parenthesized_expression_and_bracket_9() + { + var selector = "$[?(@.a) ]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, newline between parenthesized expression and bracket (10)" )] + public void Test_filter__newline_between_parenthesized_expression_and_bracket_10() + { + var selector = "$[?(@.a)\n]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, tab between parenthesized expression and bracket (11)" )] + public void Test_filter__tab_between_parenthesized_expression_and_bracket_11() + { + var selector = "$[?(@.a)\t]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, return between parenthesized expression and bracket (12)" )] + public void Test_filter__return_between_parenthesized_expression_and_bracket_12() + { + var selector = "$[?(@.a)\r]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, space between bracket and question mark (13)" )] + public void Test_filter__space_between_bracket_and_question_mark_13() + { + var selector = "$[ ?@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, newline between bracket and question mark (14)" )] + public void Test_filter__newline_between_bracket_and_question_mark_14() + { + var selector = "$[\n?@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, tab between bracket and question mark (15)" )] + public void Test_filter__tab_between_bracket_and_question_mark_15() + { + var selector = "$[\t?@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"filter, return between bracket and question mark (16)" )] + public void Test_filter__return_between_bracket_and_question_mark_16() + { + var selector = "$[\r?@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "b", + "d": "e" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, space between function name and parenthesis (17)" )] + public void Test_functions__space_between_function_name_and_parenthesis_17() + { + var selector = "$[?count (@.*)==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"functions, newline between function name and parenthesis (18)" )] + public void Test_functions__newline_between_function_name_and_parenthesis_18() + { + var selector = "$[?count\n(@.*)==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"functions, tab between function name and parenthesis (19)" )] + public void Test_functions__tab_between_function_name_and_parenthesis_19() + { + var selector = "$[?count\t(@.*)==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"functions, return between function name and parenthesis (20)" )] + public void Test_functions__return_between_function_name_and_parenthesis_20() + { + var selector = "$[?count\r(@.*)==1]"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"functions, space between parenthesis and arg (21)" )] + public void Test_functions__space_between_parenthesis_and_arg_21() + { + var selector = "$[?count( @.*)==1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, newline between parenthesis and arg (22)" )] + public void Test_functions__newline_between_parenthesis_and_arg_22() + { + var selector = "$[?count(\n@.*)==1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, tab between parenthesis and arg (23)" )] + public void Test_functions__tab_between_parenthesis_and_arg_23() + { + var selector = "$[?count(\t@.*)==1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, return between parenthesis and arg (24)" )] + public void Test_functions__return_between_parenthesis_and_arg_24() + { + var selector = "$[?count(\r@.*)==1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, space between arg and comma (25)" )] + public void Test_functions__space_between_arg_and_comma_25() + { + var selector = "$[?search(@ ,'[a-z]+')]"; + var document = JsonNode.Parse( + """ + [ + "foo", + "123" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, newline between arg and comma (26)" )] + public void Test_functions__newline_between_arg_and_comma_26() + { + var selector = "$[?search(@\n,'[a-z]+')]"; + var document = JsonNode.Parse( + """ + [ + "foo", + "123" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, tab between arg and comma (27)" )] + public void Test_functions__tab_between_arg_and_comma_27() + { + var selector = "$[?search(@\t,'[a-z]+')]"; + var document = JsonNode.Parse( + """ + [ + "foo", + "123" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, return between arg and comma (28)" )] + public void Test_functions__return_between_arg_and_comma_28() + { + var selector = "$[?search(@\r,'[a-z]+')]"; + var document = JsonNode.Parse( + """ + [ + "foo", + "123" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, space between comma and arg (29)" )] + public void Test_functions__space_between_comma_and_arg_29() + { + var selector = "$[?search(@, '[a-z]+')]"; + var document = JsonNode.Parse( + """ + [ + "foo", + "123" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, newline between comma and arg (30)" )] + public void Test_functions__newline_between_comma_and_arg_30() + { + var selector = "$[?search(@,\n'[a-z]+')]"; + var document = JsonNode.Parse( + """ + [ + "foo", + "123" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, tab between comma and arg (31)" )] + public void Test_functions__tab_between_comma_and_arg_31() + { + var selector = "$[?search(@,\t'[a-z]+')]"; + var document = JsonNode.Parse( + """ + [ + "foo", + "123" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, return between comma and arg (32)" )] + public void Test_functions__return_between_comma_and_arg_32() + { + var selector = "$[?search(@,\r'[a-z]+')]"; + var document = JsonNode.Parse( + """ + [ + "foo", + "123" + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, space between arg and parenthesis (33)" )] + public void Test_functions__space_between_arg_and_parenthesis_33() + { + var selector = "$[?count(@.* )==1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, newline between arg and parenthesis (34)" )] + public void Test_functions__newline_between_arg_and_parenthesis_34() + { + var selector = "$[?count(@.*\n)==1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, tab between arg and parenthesis (35)" )] + public void Test_functions__tab_between_arg_and_parenthesis_35() + { + var selector = "$[?count(@.*\t)==1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, return between arg and parenthesis (36)" )] + public void Test_functions__return_between_arg_and_parenthesis_36() + { + var selector = "$[?count(@.*\r)==1]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, spaces in a relative singular selector (37)" )] + public void Test_functions__spaces_in_a_relative_singular_selector_37() + { + var selector = "$[?length(@ .a .b) == 3]"; + var document = JsonNode.Parse( + """ + [ + { + "a": { + "b": "foo" + } + }, + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": { + "b": "foo" + } + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, newlines in a relative singular selector (38)" )] + public void Test_functions__newlines_in_a_relative_singular_selector_38() + { + var selector = "$[?length(@\n.a\n.b) == 3]"; + var document = JsonNode.Parse( + """ + [ + { + "a": { + "b": "foo" + } + }, + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": { + "b": "foo" + } + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, tabs in a relative singular selector (39)" )] + public void Test_functions__tabs_in_a_relative_singular_selector_39() + { + var selector = "$[?length(@\t.a\t.b) == 3]"; + var document = JsonNode.Parse( + """ + [ + { + "a": { + "b": "foo" + } + }, + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": { + "b": "foo" + } + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, returns in a relative singular selector (40)" )] + public void Test_functions__returns_in_a_relative_singular_selector_40() + { + var selector = "$[?length(@\r.a\r.b) == 3]"; + var document = JsonNode.Parse( + """ + [ + { + "a": { + "b": "foo" + } + }, + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": { + "b": "foo" + } + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, spaces in an absolute singular selector (41)" )] + public void Test_functions__spaces_in_an_absolute_singular_selector_41() + { + var selector = "$..[?length(@)==length($ [0] .a)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "foo" + }, + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, newlines in an absolute singular selector (42)" )] + public void Test_functions__newlines_in_an_absolute_singular_selector_42() + { + var selector = "$..[?length(@)==length($\n[0]\n.a)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "foo" + }, + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, tabs in an absolute singular selector (43)" )] + public void Test_functions__tabs_in_an_absolute_singular_selector_43() + { + var selector = "$..[?length(@)==length($\t[0]\t.a)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "foo" + }, + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"functions, returns in an absolute singular selector (44)" )] + public void Test_functions__returns_in_an_absolute_singular_selector_44() + { + var selector = "$..[?length(@)==length($\r[0]\r.a)]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "foo" + }, + {} + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "foo" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space before || (45)" )] + public void Test_operators__space_before____45() + { + var selector = "$[?@.a ||@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline before || (46)" )] + public void Test_operators__newline_before____46() + { + var selector = "$[?@.a\n||@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab before || (47)" )] + public void Test_operators__tab_before____47() + { + var selector = "$[?@.a\t||@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return before || (48)" )] + public void Test_operators__return_before____48() + { + var selector = "$[?@.a\r||@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space after || (49)" )] + public void Test_operators__space_after____49() + { + var selector = "$[?@.a|| @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline after || (50)" )] + public void Test_operators__newline_after____50() + { + var selector = "$[?@.a||\n@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab after || (51)" )] + public void Test_operators__tab_after____51() + { + var selector = "$[?@.a||\t@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return after || (52)" )] + public void Test_operators__return_after____52() + { + var selector = "$[?@.a||\r@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space before && (53)" )] + public void Test_operators__space_before____53() + { + var selector = "$[?@.a &&@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline before && (54)" )] + public void Test_operators__newline_before____54() + { + var selector = "$[?@.a\n&&@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab before && (55)" )] + public void Test_operators__tab_before____55() + { + var selector = "$[?@.a\t&&@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return before && (56)" )] + public void Test_operators__return_before____56() + { + var selector = "$[?@.a\r&&@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space after && (57)" )] + public void Test_operators__space_after____57() + { + var selector = "$[?@.a&& @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline after && (58)" )] + public void Test_operators__newline_after____58() + { + var selector = "$[?@.a&& @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab after && (59)" )] + public void Test_operators__tab_after____59() + { + var selector = "$[?@.a&& @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return after && (60)" )] + public void Test_operators__return_after____60() + { + var selector = "$[?@.a&& @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space before == (61)" )] + public void Test_operators__space_before____61() + { + var selector = "$[?@.a ==@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline before == (62)" )] + public void Test_operators__newline_before____62() + { + var selector = "$[?@.a\n==@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab before == (63)" )] + public void Test_operators__tab_before____63() + { + var selector = "$[?@.a\t==@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return before == (64)" )] + public void Test_operators__return_before____64() + { + var selector = "$[?@.a\r==@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space after == (65)" )] + public void Test_operators__space_after____65() + { + var selector = "$[?@.a== @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline after == (66)" )] + public void Test_operators__newline_after____66() + { + var selector = "$[?@.a==\n@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab after == (67)" )] + public void Test_operators__tab_after____67() + { + var selector = "$[?@.a==\t@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return after == (68)" )] + public void Test_operators__return_after____68() + { + var selector = "$[?@.a==\r@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space before != (69)" )] + public void Test_operators__space_before____69() + { + var selector = "$[?@.a !=@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline before != (70)" )] + public void Test_operators__newline_before____70() + { + var selector = "$[?@.a\n!=@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab before != (71)" )] + public void Test_operators__tab_before____71() + { + var selector = "$[?@.a\t!=@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return before != (72)" )] + public void Test_operators__return_before____72() + { + var selector = "$[?@.a\r!=@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space after != (73)" )] + public void Test_operators__space_after____73() + { + var selector = "$[?@.a!= @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline after != (74)" )] + public void Test_operators__newline_after____74() + { + var selector = "$[?@.a!=\n@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab after != (75)" )] + public void Test_operators__tab_after____75() + { + var selector = "$[?@.a!=\t@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return after != (76)" )] + public void Test_operators__return_after____76() + { + var selector = "$[?@.a!=\r@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space before < (77)" )] + public void Test_operators__space_before___77() + { + var selector = "$[?@.a <@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline before < (78)" )] + public void Test_operators__newline_before___78() + { + var selector = "$[?@.a\n<@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab before < (79)" )] + public void Test_operators__tab_before___79() + { + var selector = "$[?@.a\t<@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return before < (80)" )] + public void Test_operators__return_before___80() + { + var selector = "$[?@.a\r<@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space after < (81)" )] + public void Test_operators__space_after___81() + { + var selector = "$[?@.a< @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline after < (82)" )] + public void Test_operators__newline_after___82() + { + var selector = "$[?@.a<\n@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab after < (83)" )] + public void Test_operators__tab_after___83() + { + var selector = "$[?@.a<\t@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return after < (84)" )] + public void Test_operators__return_after___84() + { + var selector = "$[?@.a<\r@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space before > (85)" )] + public void Test_operators__space_before___85() + { + var selector = "$[?@.b >@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline before > (86)" )] + public void Test_operators__newline_before___86() + { + var selector = "$[?@.b\n>@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab before > (87)" )] + public void Test_operators__tab_before___87() + { + var selector = "$[?@.b\t>@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return before > (88)" )] + public void Test_operators__return_before___88() + { + var selector = "$[?@.b\r>@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space after > (89)" )] + public void Test_operators__space_after___89() + { + var selector = "$[?@.b> @.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline after > (90)" )] + public void Test_operators__newline_after___90() + { + var selector = "$[?@.b>\n@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab after > (91)" )] + public void Test_operators__tab_after___91() + { + var selector = "$[?@.b>\t@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return after > (92)" )] + public void Test_operators__return_after___92() + { + var selector = "$[?@.b>\r@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space before <= (93)" )] + public void Test_operators__space_before____93() + { + var selector = "$[?@.a <=@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline before <= (94)" )] + public void Test_operators__newline_before____94() + { + var selector = "$[?@.a\n<=@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab before <= (95)" )] + public void Test_operators__tab_before____95() + { + var selector = "$[?@.a\t<=@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return before <= (96)" )] + public void Test_operators__return_before____96() + { + var selector = "$[?@.a\r<=@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space after <= (97)" )] + public void Test_operators__space_after____97() + { + var selector = "$[?@.a<= @.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline after <= (98)" )] + public void Test_operators__newline_after____98() + { + var selector = "$[?@.a<=\n@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab after <= (99)" )] + public void Test_operators__tab_after____99() + { + var selector = "$[?@.a<=\t@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return after <= (100)" )] + public void Test_operators__return_after____100() + { + var selector = "$[?@.a<=\r@.b]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space before >= (101)" )] + public void Test_operators__space_before____101() + { + var selector = "$[?@.b >=@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline before >= (102)" )] + public void Test_operators__newline_before____102() + { + var selector = "$[?@.b\n>=@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab before >= (103)" )] + public void Test_operators__tab_before____103() + { + var selector = "$[?@.b\t>=@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return before >= (104)" )] + public void Test_operators__return_before____104() + { + var selector = "$[?@.b\r>=@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space after >= (105)" )] + public void Test_operators__space_after____105() + { + var selector = "$[?@.b>= @.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline after >= (106)" )] + public void Test_operators__newline_after____106() + { + var selector = "$[?@.b>=\n@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab after >= (107)" )] + public void Test_operators__tab_after____107() + { + var selector = "$[?@.b>=\t@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return after >= (108)" )] + public void Test_operators__return_after____108() + { + var selector = "$[?@.b>=\r@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space between logical not and test expression (109)" )] + public void Test_operators__space_between_logical_not_and_test_expression_109() + { + var selector = "$[?! @.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline between logical not and test expression (110)" )] + public void Test_operators__newline_between_logical_not_and_test_expression_110() + { + var selector = "$[?!\n@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab between logical not and test expression (111)" )] + public void Test_operators__tab_between_logical_not_and_test_expression_111() + { + var selector = "$[?!\t@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return between logical not and test expression (112)" )] + public void Test_operators__return_between_logical_not_and_test_expression_112() + { + var selector = "$[?!\r@.a]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, space between logical not and parenthesized expression (113)" )] + public void Test_operators__space_between_logical_not_and_parenthesized_expression_113() + { + var selector = "$[?! (@.a=='b')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, newline between logical not and parenthesized expression (114)" )] + public void Test_operators__newline_between_logical_not_and_parenthesized_expression_114() + { + var selector = "$[?!\n(@.a=='b')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, tab between logical not and parenthesized expression (115)" )] + public void Test_operators__tab_between_logical_not_and_parenthesized_expression_115() + { + var selector = "$[?!\t(@.a=='b')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"operators, return between logical not and parenthesized expression (116)" )] + public void Test_operators__return_between_logical_not_and_parenthesized_expression_116() + { + var selector = "$[?!\r(@.a=='b')]"; + var document = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + { + "a": "a", + "d": "e" + }, + { + "a": "d", + "d": "f" + } + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, space between root and bracket (117)" )] + public void Test_selectors__space_between_root_and_bracket_117() + { + var selector = "$ ['a']"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, newline between root and bracket (118)" )] + public void Test_selectors__newline_between_root_and_bracket_118() + { + var selector = "$\n['a']"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, tab between root and bracket (119)" )] + public void Test_selectors__tab_between_root_and_bracket_119() + { + var selector = "$\t['a']"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, return between root and bracket (120)" )] + public void Test_selectors__return_between_root_and_bracket_120() + { + var selector = "$\r['a']"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, space between bracket and bracket (121)" )] + public void Test_selectors__space_between_bracket_and_bracket_121() + { + var selector = "$['a'] ['b']"; + var document = JsonNode.Parse( + """ + { + "a": { + "b": "ab" + } + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, newline between root and bracket (122)" )] + public void Test_selectors__newline_between_root_and_bracket_122() + { + var selector = "$['a'] \n['b']"; + var document = JsonNode.Parse( + """ + { + "a": { + "b": "ab" + } + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, tab between root and bracket (123)" )] + public void Test_selectors__tab_between_root_and_bracket_123() + { + var selector = "$['a'] \t['b']"; + var document = JsonNode.Parse( + """ + { + "a": { + "b": "ab" + } + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, return between root and bracket (124)" )] + public void Test_selectors__return_between_root_and_bracket_124() + { + var selector = "$['a'] \r['b']"; + var document = JsonNode.Parse( + """ + { + "a": { + "b": "ab" + } + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, space between root and dot (125)" )] + public void Test_selectors__space_between_root_and_dot_125() + { + var selector = "$ .a"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, newline between root and dot (126)" )] + public void Test_selectors__newline_between_root_and_dot_126() + { + var selector = "$\n.a"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, tab between root and dot (127)" )] + public void Test_selectors__tab_between_root_and_dot_127() + { + var selector = "$\t.a"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, return between root and dot (128)" )] + public void Test_selectors__return_between_root_and_dot_128() + { + var selector = "$\r.a"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, space between dot and name (129)" )] + public void Test_selectors__space_between_dot_and_name_129() + { + var selector = "$. a"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"selectors, newline between dot and name (130)" )] + public void Test_selectors__newline_between_dot_and_name_130() + { + var selector = "$.\na"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"selectors, tab between dot and name (131)" )] + public void Test_selectors__tab_between_dot_and_name_131() + { + var selector = "$.\ta"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"selectors, return between dot and name (132)" )] + public void Test_selectors__return_between_dot_and_name_132() + { + var selector = "$.\ra"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"selectors, space between recursive descent and name (133)" )] + public void Test_selectors__space_between_recursive_descent_and_name_133() + { + var selector = "$.. a"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"selectors, newline between recursive descent and name (134)" )] + public void Test_selectors__newline_between_recursive_descent_and_name_134() + { + var selector = "$..\na"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"selectors, tab between recursive descent and name (135)" )] + public void Test_selectors__tab_between_recursive_descent_and_name_135() + { + var selector = "$..\ta"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"selectors, return between recursive descent and name (136)" )] + public void Test_selectors__return_between_recursive_descent_and_name_136() + { + var selector = "$..\ra"; + var document = JsonNode.Parse( "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [TestMethod( @"selectors, space between bracket and selector (137)" )] + public void Test_selectors__space_between_bracket_and_selector_137() + { + var selector = "$[ 'a']"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, newline between bracket and selector (138)" )] + public void Test_selectors__newline_between_bracket_and_selector_138() + { + var selector = "$[\n'a']"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, tab between bracket and selector (139)" )] + public void Test_selectors__tab_between_bracket_and_selector_139() + { + var selector = "$[\t'a']"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, return between bracket and selector (140)" )] + public void Test_selectors__return_between_bracket_and_selector_140() + { + var selector = "$[\r'a']"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, space between selector and bracket (141)" )] + public void Test_selectors__space_between_selector_and_bracket_141() + { + var selector = "$['a' ]"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, newline between selector and bracket (142)" )] + public void Test_selectors__newline_between_selector_and_bracket_142() + { + var selector = "$['a'\n]"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, tab between selector and bracket (143)" )] + public void Test_selectors__tab_between_selector_and_bracket_143() + { + var selector = "$['a'\t]"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, return between selector and bracket (144)" )] + public void Test_selectors__return_between_selector_and_bracket_144() + { + var selector = "$['a'\r]"; + var document = JsonNode.Parse( + """ + { + "a": "ab" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, space between selector and comma (145)" )] + public void Test_selectors__space_between_selector_and_comma_145() + { + var selector = "$['a' ,'b']"; + var document = JsonNode.Parse( + """ + { + "a": "ab", + "b": "bc" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab", + "bc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, newline between selector and comma (146)" )] + public void Test_selectors__newline_between_selector_and_comma_146() + { + var selector = "$['a'\n,'b']"; + var document = JsonNode.Parse( + """ + { + "a": "ab", + "b": "bc" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab", + "bc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, tab between selector and comma (147)" )] + public void Test_selectors__tab_between_selector_and_comma_147() + { + var selector = "$['a'\t,'b']"; + var document = JsonNode.Parse( + """ + { + "a": "ab", + "b": "bc" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab", + "bc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, return between selector and comma (148)" )] + public void Test_selectors__return_between_selector_and_comma_148() + { + var selector = "$['a'\r,'b']"; + var document = JsonNode.Parse( + """ + { + "a": "ab", + "b": "bc" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab", + "bc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, space between comma and selector (149)" )] + public void Test_selectors__space_between_comma_and_selector_149() + { + var selector = "$['a', 'b']"; + var document = JsonNode.Parse( + """ + { + "a": "ab", + "b": "bc" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab", + "bc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, newline between comma and selector (150)" )] + public void Test_selectors__newline_between_comma_and_selector_150() + { + var selector = "$['a',\n'b']"; + var document = JsonNode.Parse( + """ + { + "a": "ab", + "b": "bc" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab", + "bc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, tab between comma and selector (151)" )] + public void Test_selectors__tab_between_comma_and_selector_151() + { + var selector = "$['a',\t'b']"; + var document = JsonNode.Parse( + """ + { + "a": "ab", + "b": "bc" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab", + "bc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"selectors, return between comma and selector (152)" )] + public void Test_selectors__return_between_comma_and_selector_152() + { + var selector = "$['a',\r'b']"; + var document = JsonNode.Parse( + """ + { + "a": "ab", + "b": "bc" + } + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + "ab", + "bc" + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, space between start and colon (153)" )] + public void Test_slice__space_between_start_and_colon_153() + { + var selector = "$[1 :5:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, newline between start and colon (154)" )] + public void Test_slice__newline_between_start_and_colon_154() + { + var selector = "$[1\n:5:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, tab between start and colon (155)" )] + public void Test_slice__tab_between_start_and_colon_155() + { + var selector = "$[1\t:5:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, return between start and colon (156)" )] + public void Test_slice__return_between_start_and_colon_156() + { + var selector = "$[1\r:5:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, space between colon and end (157)" )] + public void Test_slice__space_between_colon_and_end_157() + { + var selector = "$[1: 5:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, newline between colon and end (158)" )] + public void Test_slice__newline_between_colon_and_end_158() + { + var selector = "$[1:\n5:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, tab between colon and end (159)" )] + public void Test_slice__tab_between_colon_and_end_159() + { + var selector = "$[1:\t5:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, return between colon and end (160)" )] + public void Test_slice__return_between_colon_and_end_160() + { + var selector = "$[1:\r5:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, space between end and colon (161)" )] + public void Test_slice__space_between_end_and_colon_161() + { + var selector = "$[1:5 :2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, newline between end and colon (162)" )] + public void Test_slice__newline_between_end_and_colon_162() + { + var selector = "$[1:5\n:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, tab between end and colon (163)" )] + public void Test_slice__tab_between_end_and_colon_163() + { + var selector = "$[1:5\t:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, return between end and colon (164)" )] + public void Test_slice__return_between_end_and_colon_164() + { + var selector = "$[1:5\r:2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, space between colon and step (165)" )] + public void Test_slice__space_between_colon_and_step_165() + { + var selector = "$[1:5: 2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, newline between colon and step (166)" )] + public void Test_slice__newline_between_colon_and_step_166() + { + var selector = "$[1:5:\n2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, tab between colon and step (167)" )] + public void Test_slice__tab_between_colon_and_step_167() + { + var selector = "$[1:5:\t2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + + [TestMethod( @"slice, return between colon and step (168)" )] + public void Test_slice__return_between_colon_and_step_168() + { + var selector = "$[1:5:\r2]"; + var document = JsonNode.Parse( + """ + [ + 1, + 2, + 3, + 4, + 5, + 6 + ] + """ ); + var results = document.Select( selector ); + var expect = JsonNode.Parse( + """ + [ + 2, + 4 + ] + """ ); + + var match = TestHelper.MatchOne( results, expect! ); + Assert.IsTrue( match ); + } + } +} + diff --git a/test/Hyperbee.Json.Cts/cts.json b/test/Hyperbee.Json.Cts/cts.json new file mode 100644 index 00000000..328bcb80 --- /dev/null +++ b/test/Hyperbee.Json.Cts/cts.json @@ -0,0 +1,8624 @@ +{ + "description": "JSONPath Compliance Test Suite. This file is autogenerated, do not edit.", + "tests": [ + { + "name": "basic, root", + "selector": "$", + "document": [ + "first", + "second" + ], + "result": [ + [ + "first", + "second" + ] + ] + }, + { + "name": "basic, no leading whitespace", + "selector": " $", + "invalid_selector": true + }, + { + "name": "basic, no trailing whitespace", + "selector": "$ ", + "invalid_selector": true + }, + { + "name": "basic, name shorthand", + "selector": "$.a", + "document": { + "a": "A", + "b": "B" + }, + "result": [ + "A" + ] + }, + { + "name": "basic, name shorthand, extended unicode ☺", + "selector": "$.☺", + "document": { + "☺": "A", + "b": "B" + }, + "result": [ + "A" + ] + }, + { + "name": "basic, name shorthand, underscore", + "selector": "$._", + "document": { + "_": "A", + "_foo": "B" + }, + "result": [ + "A" + ] + }, + { + "name": "basic, name shorthand, symbol", + "selector": "$.&", + "invalid_selector": true + }, + { + "name": "basic, name shorthand, number", + "selector": "$.1", + "invalid_selector": true + }, + { + "name": "basic, name shorthand, absent data", + "selector": "$.c", + "document": { + "a": "A", + "b": "B" + }, + "result": [] + }, + { + "name": "basic, name shorthand, array data", + "selector": "$.a", + "document": [ + "first", + "second" + ], + "result": [] + }, + { + "name": "basic, wildcard shorthand, object data", + "selector": "$.*", + "document": { + "a": "A", + "b": "B" + }, + "results": [ + [ + "A", + "B" + ], + [ + "B", + "A" + ] + ] + }, + { + "name": "basic, wildcard shorthand, array data", + "selector": "$.*", + "document": [ + "first", + "second" + ], + "result": [ + "first", + "second" + ] + }, + { + "name": "basic, wildcard selector, array data", + "selector": "$[*]", + "document": [ + "first", + "second" + ], + "result": [ + "first", + "second" + ] + }, + { + "name": "basic, wildcard shorthand, then name shorthand", + "selector": "$.*.a", + "document": { + "x": { + "a": "Ax", + "b": "Bx" + }, + "y": { + "a": "Ay", + "b": "By" + } + }, + "results": [ + [ + "Ax", + "Ay" + ], + [ + "Ay", + "Ax" + ] + ] + }, + { + "name": "basic, multiple selectors", + "selector": "$[0,2]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 0, + 2 + ] + }, + { + "name": "basic, multiple selectors, space instead of comma", + "selector": "$[0 2]", + "invalid_selector": true + }, + { + "name": "basic, multiple selectors, name and index, array data", + "selector": "$['a',1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 1 + ] + }, + { + "name": "basic, multiple selectors, name and index, object data", + "selector": "$['a',1]", + "document": { + "a": 1, + "b": 2 + }, + "result": [ + 1 + ] + }, + { + "name": "basic, multiple selectors, index and slice", + "selector": "$[1,5:7]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 1, + 5, + 6 + ] + }, + { + "name": "basic, multiple selectors, index and slice, overlapping", + "selector": "$[1,0:3]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 1, + 0, + 1, + 2 + ] + }, + { + "name": "basic, multiple selectors, duplicate index", + "selector": "$[1,1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 1, + 1 + ] + }, + { + "name": "basic, multiple selectors, wildcard and index", + "selector": "$[*,1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 1 + ] + }, + { + "name": "basic, multiple selectors, wildcard and name", + "selector": "$[*,'a']", + "document": { + "a": "A", + "b": "B" + }, + "results": [ + [ + "A", + "B", + "A" + ], + [ + "B", + "A", + "A" + ] + ] + }, + { + "name": "basic, multiple selectors, wildcard and slice", + "selector": "$[*,0:2]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 1 + ] + }, + { + "name": "basic, multiple selectors, multiple wildcards", + "selector": "$[*,*]", + "document": [ + 0, + 1, + 2 + ], + "result": [ + 0, + 1, + 2, + 0, + 1, + 2 + ] + }, + { + "name": "basic, empty segment", + "selector": "$[]", + "invalid_selector": true + }, + { + "name": "basic, descendant segment, index", + "selector": "$..[1]", + "document": { + "o": [ + 0, + 1, + [ + 2, + 3 + ] + ] + }, + "result": [ + 1, + 3 + ] + }, + { + "name": "basic, descendant segment, name shorthand", + "selector": "$..a", + "document": { + "o": [ + { + "a": "b" + }, + { + "a": "c" + } + ] + }, + "result": [ + "b", + "c" + ] + }, + { + "name": "basic, descendant segment, wildcard shorthand, array data", + "selector": "$..*", + "document": [ + 0, + 1 + ], + "result": [ + 0, + 1 + ] + }, + { + "name": "basic, descendant segment, wildcard selector, array data", + "selector": "$..[*]", + "document": [ + 0, + 1 + ], + "result": [ + 0, + 1 + ] + }, + { + "name": "basic, descendant segment, wildcard selector, nested arrays", + "selector": "$..[*]", + "document": [ + [ + [ + 1 + ] + ], + [ + 2 + ] + ], + "results": [ + [ + [ + [ + 1 + ] + ], + [ + 2 + ], + [ + 1 + ], + 1, + 2 + ], + [ + [ + [ + 1 + ] + ], + [ + 2 + ], + [ + 1 + ], + 2, + 1 + ] + ] + }, + { + "name": "basic, descendant segment, wildcard selector, nested objects", + "selector": "$..[*]", + "document": { + "a": { + "c": { + "e": 1 + } + }, + "b": { + "d": 2 + } + }, + "results": [ + [ + { + "c": { + "e": 1 + } + }, + { + "d": 2 + }, + { + "e": 1 + }, + 1, + 2 + ], + [ + { + "c": { + "e": 1 + } + }, + { + "d": 2 + }, + { + "e": 1 + }, + 2, + 1 + ], + [ + { + "c": { + "e": 1 + } + }, + { + "d": 2 + }, + 2, + { + "e": 1 + }, + 1 + ], + [ + { + "d": 2 + }, + { + "c": { + "e": 1 + } + }, + { + "e": 1 + }, + 1, + 2 + ], + [ + { + "d": 2 + }, + { + "c": { + "e": 1 + } + }, + { + "e": 1 + }, + 2, + 1 + ], + [ + { + "d": 2 + }, + { + "c": { + "e": 1 + } + }, + 2, + { + "e": 1 + }, + 1 + ] + ] + }, + { + "name": "basic, descendant segment, wildcard shorthand, object data", + "selector": "$..*", + "document": { + "a": "b" + }, + "result": [ + "b" + ] + }, + { + "name": "basic, descendant segment, wildcard shorthand, nested data", + "selector": "$..*", + "document": { + "o": [ + { + "a": "b" + } + ] + }, + "result": [ + [ + { + "a": "b" + } + ], + { + "a": "b" + }, + "b" + ] + }, + { + "name": "basic, descendant segment, multiple selectors", + "selector": "$..['a','d']", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + "b", + "e", + "c", + "f" + ] + }, + { + "name": "basic, descendant segment, object traversal, multiple selectors", + "selector": "$..['a','d']", + "document": { + "x": { + "a": "b", + "d": "e" + }, + "y": { + "a": "c", + "d": "f" + } + }, + "results": [ + [ + "b", + "e", + "c", + "f" + ], + [ + "c", + "f", + "b", + "e" + ] + ] + }, + { + "name": "basic, bald descendant segment", + "selector": "$..", + "invalid_selector": true + }, + { + "name": "filter, existence, without segments", + "selector": "$[?@]", + "document": { + "a": 1, + "b": null + }, + "results": [ + [ + 1, + null + ], + [ + null, + 1 + ] + ] + }, + { + "name": "filter, existence", + "selector": "$[?@.a]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "filter, existence, present with null", + "selector": "$[?@.a]", + "document": [ + { + "a": null, + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": null, + "d": "e" + } + ] + }, + { + "name": "filter, equals string, single quotes", + "selector": "$[?@.a=='b']", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "filter, equals numeric string, single quotes", + "selector": "$[?@.a=='1']", + "document": [ + { + "a": "1", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ], + "result": [ + { + "a": "1", + "d": "e" + } + ] + }, + { + "name": "filter, equals string, double quotes", + "selector": "$[?@.a==\"b\"]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "filter, equals numeric string, double quotes", + "selector": "$[?@.a==\"1\"]", + "document": [ + { + "a": "1", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ], + "result": [ + { + "a": "1", + "d": "e" + } + ] + }, + { + "name": "filter, equals number", + "selector": "$[?@.a==1]", + "document": [ + { + "a": 1, + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": 2, + "d": "f" + }, + { + "a": "1", + "d": "f" + } + ], + "result": [ + { + "a": 1, + "d": "e" + } + ] + }, + { + "name": "filter, equals null", + "selector": "$[?@.a==null]", + "document": [ + { + "a": null, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": null, + "d": "e" + } + ] + }, + { + "name": "filter, equals null, absent from data", + "selector": "$[?@.a==null]", + "document": [ + { + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [] + }, + { + "name": "filter, equals true", + "selector": "$[?@.a==true]", + "document": [ + { + "a": true, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": true, + "d": "e" + } + ] + }, + { + "name": "filter, equals false", + "selector": "$[?@.a==false]", + "document": [ + { + "a": false, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": false, + "d": "e" + } + ] + }, + { + "name": "filter, equals self", + "selector": "$[?@==@]", + "document": [ + 1, + null, + true, + { + "a": "b" + }, + [ + false + ] + ], + "result": [ + 1, + null, + true, + { + "a": "b" + }, + [ + false + ] + ] + }, + { + "name": "filter, deep equality, arrays", + "selector": "$[?@.a==@.b]", + "document": [ + { + "a": false, + "b": [ + 1, + 2 + ] + }, + { + "a": [ + [ + 1, + [ + 2 + ] + ] + ], + "b": [ + [ + 1, + [ + 2 + ] + ] + ] + }, + { + "a": [ + [ + 1, + [ + 2 + ] + ] + ], + "b": [ + [ + [ + 2 + ], + 1 + ] + ] + }, + { + "a": [ + [ + 1, + [ + 2 + ] + ] + ], + "b": [ + [ + 1, + 2 + ] + ] + } + ], + "result": [ + { + "a": [ + [ + 1, + [ + 2 + ] + ] + ], + "b": [ + [ + 1, + [ + 2 + ] + ] + ] + } + ] + }, + { + "name": "filter, deep equality, objects", + "selector": "$[?@.a==@.b]", + "document": [ + { + "a": false, + "b": { + "x": 1, + "y": { + "z": 1 + } + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "x": 1, + "y": { + "z": 1 + } + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "y": { + "z": 1 + }, + "x": 1 + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "x": 1 + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "x": 1, + "y": { + "z": 2 + } + } + } + ], + "result": [ + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "x": 1, + "y": { + "z": 1 + } + } + }, + { + "a": { + "x": 1, + "y": { + "z": 1 + } + }, + "b": { + "y": { + "z": 1 + }, + "x": 1 + } + } + ] + }, + { + "name": "filter, not-equals string, single quotes", + "selector": "$[?@.a!='b']", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "c", + "d": "f" + } + ] + }, + { + "name": "filter, not-equals numeric string, single quotes", + "selector": "$[?@.a!='1']", + "document": [ + { + "a": "1", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ], + "result": [ + { + "a": 1, + "d": "f" + } + ] + }, + { + "name": "filter, not-equals string, single quotes, different type", + "selector": "$[?@.a!='b']", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ], + "result": [ + { + "a": 1, + "d": "f" + } + ] + }, + { + "name": "filter, not-equals string, double quotes", + "selector": "$[?@.a!=\"b\"]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "c", + "d": "f" + } + ] + }, + { + "name": "filter, not-equals numeric string, double quotes", + "selector": "$[?@.a!=\"1\"]", + "document": [ + { + "a": "1", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ], + "result": [ + { + "a": 1, + "d": "f" + } + ] + }, + { + "name": "filter, not-equals string, double quotes, different types", + "selector": "$[?@.a!=\"b\"]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": 1, + "d": "f" + } + ], + "result": [ + { + "a": 1, + "d": "f" + } + ] + }, + { + "name": "filter, not-equals number", + "selector": "$[?@.a!=1]", + "document": [ + { + "a": 1, + "d": "e" + }, + { + "a": 2, + "d": "f" + }, + { + "a": "1", + "d": "f" + } + ], + "result": [ + { + "a": 2, + "d": "f" + }, + { + "a": "1", + "d": "f" + } + ] + }, + { + "name": "filter, not-equals number, different types", + "selector": "$[?@.a!=1]", + "document": [ + { + "a": 1, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "c", + "d": "f" + } + ] + }, + { + "name": "filter, not-equals null", + "selector": "$[?@.a!=null]", + "document": [ + { + "a": null, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "c", + "d": "f" + } + ] + }, + { + "name": "filter, not-equals null, absent from data", + "selector": "$[?@.a!=null]", + "document": [ + { + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ] + }, + { + "name": "filter, not-equals true", + "selector": "$[?@.a!=true]", + "document": [ + { + "a": true, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "c", + "d": "f" + } + ] + }, + { + "name": "filter, not-equals false", + "selector": "$[?@.a!=false]", + "document": [ + { + "a": false, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "c", + "d": "f" + } + ] + }, + { + "name": "filter, less than string, single quotes", + "selector": "$[?@.a<'c']", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "filter, less than string, double quotes", + "selector": "$[?@.a<\"c\"]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "filter, less than number", + "selector": "$[?@.a<10]", + "document": [ + { + "a": 1, + "d": "e" + }, + { + "a": 10, + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": 20, + "d": "f" + } + ], + "result": [ + { + "a": 1, + "d": "e" + } + ] + }, + { + "name": "filter, less than null", + "selector": "$[?@.a'c']", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "filter, greater than string, double quotes", + "selector": "$[?@.a>\"c\"]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "filter, greater than number", + "selector": "$[?@.a>10]", + "document": [ + { + "a": 1, + "d": "e" + }, + { + "a": 10, + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": 20, + "d": "f" + } + ], + "result": [ + { + "a": 20, + "d": "f" + } + ] + }, + { + "name": "filter, greater than null", + "selector": "$[?@.a>null]", + "document": [ + { + "a": null, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [] + }, + { + "name": "filter, greater than true", + "selector": "$[?@.a>true]", + "document": [ + { + "a": true, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [] + }, + { + "name": "filter, greater than false", + "selector": "$[?@.a>false]", + "document": [ + { + "a": false, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [] + }, + { + "name": "filter, greater than or equal to string, single quotes", + "selector": "$[?@.a>='c']", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "c", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "filter, greater than or equal to string, double quotes", + "selector": "$[?@.a>=\"c\"]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "c", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "filter, greater than or equal to number", + "selector": "$[?@.a>=10]", + "document": [ + { + "a": 1, + "d": "e" + }, + { + "a": 10, + "d": "e" + }, + { + "a": "c", + "d": "f" + }, + { + "a": 20, + "d": "f" + } + ], + "result": [ + { + "a": 10, + "d": "e" + }, + { + "a": 20, + "d": "f" + } + ] + }, + { + "name": "filter, greater than or equal to null", + "selector": "$[?@.a>=null]", + "document": [ + { + "a": null, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": null, + "d": "e" + } + ] + }, + { + "name": "filter, greater than or equal to true", + "selector": "$[?@.a>=true]", + "document": [ + { + "a": true, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": true, + "d": "e" + } + ] + }, + { + "name": "filter, greater than or equal to false", + "selector": "$[?@.a>=false]", + "document": [ + { + "a": false, + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": false, + "d": "e" + } + ] + }, + { + "name": "filter, exists and not-equals null, absent from data", + "selector": "$[?@.a&&@.a!=null]", + "document": [ + { + "d": "e" + }, + { + "a": "c", + "d": "f" + } + ], + "result": [ + { + "a": "c", + "d": "f" + } + ] + }, + { + "name": "filter, exists and exists, data false", + "selector": "$[?@.a&&@.b]", + "document": [ + { + "a": false, + "b": false + }, + { + "b": false + }, + { + "c": false + } + ], + "result": [ + { + "a": false, + "b": false + } + ] + }, + { + "name": "filter, exists or exists, data false", + "selector": "$[?@.a||@.b]", + "document": [ + { + "a": false, + "b": false + }, + { + "b": false + }, + { + "c": false + } + ], + "result": [ + { + "a": false, + "b": false + }, + { + "b": false + } + ] + }, + { + "name": "filter, and", + "selector": "$[?@.a>0&&@.a<10]", + "document": [ + { + "a": -10, + "d": "e" + }, + { + "a": 5, + "d": "f" + }, + { + "a": 20, + "d": "f" + } + ], + "result": [ + { + "a": 5, + "d": "f" + } + ] + }, + { + "name": "filter, or", + "selector": "$[?@.a=='b'||@.a=='d']", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "c", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "filter, not expression", + "selector": "$[?!(@.a=='b')]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "a", + "d": "e" + }, + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "filter, not exists", + "selector": "$[?!@.a]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "d": "f" + } + ] + }, + { + "name": "filter, not exists, data null", + "selector": "$[?!@.a]", + "document": [ + { + "a": null, + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "d": "f" + } + ] + }, + { + "name": "filter, non-singular existence, wildcard", + "selector": "$[?@.*]", + "document": [ + 1, + [], + [ + 2 + ], + {}, + { + "a": 3 + } + ], + "result": [ + [ + 2 + ], + { + "a": 3 + } + ] + }, + { + "name": "filter, non-singular existence, multiple", + "selector": "$[?@[0, 0, 'a']]", + "document": [ + 1, + [], + [ + 2 + ], + [ + 2, + 3 + ], + { + "a": 3 + }, + { + "b": 4 + }, + { + "a": 3, + "b": 4 + } + ], + "result": [ + [ + 2 + ], + [ + 2, + 3 + ], + { + "a": 3 + }, + { + "a": 3, + "b": 4 + } + ] + }, + { + "name": "filter, non-singular existence, slice", + "selector": "$[?@[0:2]]", + "document": [ + 1, + [], + [ + 2 + ], + [ + 2, + 3, + 4 + ], + {}, + { + "a": 3 + } + ], + "result": [ + [ + 2 + ], + [ + 2, + 3, + 4 + ] + ] + }, + { + "name": "filter, non-singular existence, negated", + "selector": "$[?!@.*]", + "document": [ + 1, + [], + [ + 2 + ], + {}, + { + "a": 3 + } + ], + "result": [ + 1, + [], + {} + ] + }, + { + "name": "filter, non-singular query in comparison, slice", + "selector": "$[?@[0:0]==0]", + "invalid_selector": true + }, + { + "name": "filter, non-singular query in comparison, all children", + "selector": "$[?@[*]==0]", + "invalid_selector": true + }, + { + "name": "filter, non-singular query in comparison, descendants", + "selector": "$[?@..a==0]", + "invalid_selector": true + }, + { + "name": "filter, non-singular query in comparison, combined", + "selector": "$[?@.a[*].a==0]", + "invalid_selector": true + }, + { + "name": "filter, nested", + "selector": "$[?@[?@>1]]", + "document": [ + [ + 0 + ], + [ + 0, + 1 + ], + [ + 0, + 1, + 2 + ], + [ + 42 + ] + ], + "result": [ + [ + 0, + 1, + 2 + ], + [ + 42 + ] + ] + }, + { + "name": "filter, name segment on primitive, selects nothing", + "selector": "$[?@.a == 1]", + "document": { + "a": 1 + }, + "result": [] + }, + { + "name": "filter, name segment on array, selects nothing", + "selector": "$[?@['0'] == 5]", + "document": [ + [ + 5, + 6 + ] + ], + "result": [] + }, + { + "name": "filter, index segment on object, selects nothing", + "selector": "$[?@[0] == 5]", + "document": [ + { + "0": 5 + } + ], + "result": [] + }, + { + "name": "filter, relative non-singular query, index, equal", + "selector": "$[?(@[0, 0]==42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, index, not equal", + "selector": "$[?(@[0, 0]!=42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, index, less-or-equal", + "selector": "$[?(@[0, 0]<=42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, name, equal", + "selector": "$[?(@['a', 'a']==42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, name, not equal", + "selector": "$[?(@['a', 'a']!=42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, name, less-or-equal", + "selector": "$[?(@['a', 'a']<=42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, combined, equal", + "selector": "$[?(@[0, '0']==42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, combined, not equal", + "selector": "$[?(@[0, '0']!=42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, combined, less-or-equal", + "selector": "$[?(@[0, '0']<=42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, wildcard, equal", + "selector": "$[?(@.*==42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, wildcard, not equal", + "selector": "$[?(@.*!=42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, wildcard, less-or-equal", + "selector": "$[?(@.*<=42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, slice, equal", + "selector": "$[?(@[0:0]==42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, slice, not equal", + "selector": "$[?(@[0:0]!=42)]", + "invalid_selector": true + }, + { + "name": "filter, relative non-singular query, slice, less-or-equal", + "selector": "$[?(@[0:0]<=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, index, equal", + "selector": "$[?($[0, 0]==42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, index, not equal", + "selector": "$[?($[0, 0]!=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, index, less-or-equal", + "selector": "$[?($[0, 0]<=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, name, equal", + "selector": "$[?($['a', 'a']==42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, name, not equal", + "selector": "$[?($['a', 'a']!=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, name, less-or-equal", + "selector": "$[?($['a', 'a']<=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, combined, equal", + "selector": "$[?($[0, '0']==42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, combined, not equal", + "selector": "$[?($[0, '0']!=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, combined, less-or-equal", + "selector": "$[?($[0, '0']<=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, wildcard, equal", + "selector": "$[?($.*==42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, wildcard, not equal", + "selector": "$[?($.*!=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, wildcard, less-or-equal", + "selector": "$[?($.*<=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, slice, equal", + "selector": "$[?($[0:0]==42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, slice, not equal", + "selector": "$[?($[0:0]!=42)]", + "invalid_selector": true + }, + { + "name": "filter, absolute non-singular query, slice, less-or-equal", + "selector": "$[?($[0:0]<=42)]", + "invalid_selector": true + }, + { + "name": "filter, multiple selectors", + "selector": "$[?@.a,?@.b]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + }, + { + "name": "filter, multiple selectors, comparison", + "selector": "$[?@.a=='b',?@.b=='x']", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "filter, multiple selectors, overlapping", + "selector": "$[?@.a,?@.d]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + }, + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + }, + { + "name": "filter, multiple selectors, filter and index", + "selector": "$[?@.a,1]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + }, + { + "name": "filter, multiple selectors, filter and wildcard", + "selector": "$[?@.a,*]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + }, + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + }, + { + "name": "filter, multiple selectors, filter and slice", + "selector": "$[?@.a,1:]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + }, + { + "g": "h" + } + ], + "result": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + }, + { + "g": "h" + } + ] + }, + { + "name": "filter, multiple selectors, comparison filter, index and slice", + "selector": "$[1, ?@.a=='b', 1:]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "b": "c", + "d": "f" + }, + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ] + }, + { + "name": "filter, equals number, zero and negative zero", + "selector": "$[?@.a==-0]", + "document": [ + { + "a": 0, + "d": "e" + }, + { + "a": 0.1, + "d": "f" + }, + { + "a": "0", + "d": "g" + } + ], + "result": [ + { + "a": 0, + "d": "e" + } + ] + }, + { + "name": "filter, equals number, with and without decimal fraction", + "selector": "$[?@.a==1.0]", + "document": [ + { + "a": 1, + "d": "e" + }, + { + "a": 2, + "d": "f" + }, + { + "a": "1", + "d": "g" + } + ], + "result": [ + { + "a": 1, + "d": "e" + } + ] + }, + { + "name": "filter, equals number, exponent", + "selector": "$[?@.a==1e2]", + "document": [ + { + "a": 100, + "d": "e" + }, + { + "a": 100.1, + "d": "f" + }, + { + "a": "100", + "d": "g" + } + ], + "result": [ + { + "a": 100, + "d": "e" + } + ] + }, + { + "name": "filter, equals number, positive exponent", + "selector": "$[?@.a==1e+2]", + "document": [ + { + "a": 100, + "d": "e" + }, + { + "a": 100.1, + "d": "f" + }, + { + "a": "100", + "d": "g" + } + ], + "result": [ + { + "a": 100, + "d": "e" + } + ] + }, + { + "name": "filter, equals number, negative exponent", + "selector": "$[?@.a==1e-2]", + "document": [ + { + "a": 0.01, + "d": "e" + }, + { + "a": 0.02, + "d": "f" + }, + { + "a": "0.01", + "d": "g" + } + ], + "result": [ + { + "a": 0.01, + "d": "e" + } + ] + }, + { + "name": "filter, equals number, decimal fraction", + "selector": "$[?@.a==1.1]", + "document": [ + { + "a": 1.1, + "d": "e" + }, + { + "a": 1, + "d": "f" + }, + { + "a": "1.1", + "d": "g" + } + ], + "result": [ + { + "a": 1.1, + "d": "e" + } + ] + }, + { + "name": "filter, equals number, decimal fraction, no fractional digit", + "selector": "$[?@.a==1.]", + "invalid_selector": true + }, + { + "name": "filter, equals number, decimal fraction, exponent", + "selector": "$[?@.a==1.1e2]", + "document": [ + { + "a": 110, + "d": "e" + }, + { + "a": 110.1, + "d": "f" + }, + { + "a": "110", + "d": "g" + } + ], + "result": [ + { + "a": 110, + "d": "e" + } + ] + }, + { + "name": "filter, equals number, decimal fraction, positive exponent", + "selector": "$[?@.a==1.1e+2]", + "document": [ + { + "a": 110, + "d": "e" + }, + { + "a": 110.1, + "d": "f" + }, + { + "a": "110", + "d": "g" + } + ], + "result": [ + { + "a": 110, + "d": "e" + } + ] + }, + { + "name": "filter, equals number, decimal fraction, negative exponent", + "selector": "$[?@.a==1.1e-2]", + "document": [ + { + "a": 0.011, + "d": "e" + }, + { + "a": 0.012, + "d": "f" + }, + { + "a": "0.011", + "d": "g" + } + ], + "result": [ + { + "a": 0.011, + "d": "e" + } + ] + }, + { + "name": "filter, equals, special nothing", + "selector": "$.values[?length(@.a) == value($..c)]", + "document": { + "c": "cd", + "values": [ + { + "a": "ab" + }, + { + "c": "d" + }, + { + "a": null + } + ] + }, + "result": [ + { + "c": "d" + }, + { + "a": null + } + ] + }, + { + "name": "filter, equals, empty node list and empty node list", + "selector": "$[?@.a == @.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "c": 3 + } + ] + }, + { + "name": "filter, equals, empty node list and special nothing", + "selector": "$[?@.a == length(@.b)]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "b": 2 + }, + { + "c": 3 + } + ] + }, + { + "name": "filter, object data", + "selector": "$[?@<3]", + "document": { + "a": 1, + "b": 2, + "c": 3 + }, + "results": [ + [ + 1, + 2 + ], + [ + 2, + 1 + ] + ] + }, + { + "name": "filter, and binds more tightly than or", + "selector": "$[?@.a || @.b && @.c]", + "document": [ + { + "a": 1 + }, + { + "b": 2, + "c": 3 + }, + { + "c": 3 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2, + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + }, + { + "name": "filter, left to right evaluation", + "selector": "$[?@.a && @.b || @.c]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 3 + }, + { + "b": 1, + "c": 3 + }, + { + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ], + "result": [ + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 3 + }, + { + "b": 1, + "c": 3 + }, + { + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + }, + { + "name": "filter, group terms, left", + "selector": "$[?(@.a || @.b) && @.c]", + "document": [ + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 3 + }, + { + "b": 2, + "c": 3 + }, + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ], + "result": [ + { + "a": 1, + "c": 3 + }, + { + "b": 2, + "c": 3 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + }, + { + "name": "filter, group terms, right", + "selector": "$[?@.a && (@.b || @.c)]", + "document": [ + { + "a": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 2 + }, + { + "b": 2 + }, + { + "c": 2 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ], + "result": [ + { + "a": 1, + "b": 2 + }, + { + "a": 1, + "c": 2 + }, + { + "a": 1, + "b": 2, + "c": 3 + } + ] + }, + { + "name": "filter, string literal, single quote in double quotes", + "selector": "$[?@ == \"quoted' literal\"]", + "document": [ + "quoted' literal", + "a", + "quoted\\' literal" + ], + "result": [ + "quoted' literal" + ] + }, + { + "name": "filter, string literal, double quote in single quotes", + "selector": "$[?@ == 'quoted\" literal']", + "document": [ + "quoted\" literal", + "a", + "quoted\\\" literal", + "'quoted\" literal'" + ], + "result": [ + "quoted\" literal" + ] + }, + { + "name": "filter, string literal, escaped single quote in single quotes", + "selector": "$[?@ == 'quoted\\' literal']", + "document": [ + "quoted' literal", + "a", + "quoted\\' literal", + "'quoted\" literal'" + ], + "result": [ + "quoted' literal" + ] + }, + { + "name": "filter, string literal, escaped double quote in double quotes", + "selector": "$[?@ == \"quoted\\\" literal\"]", + "document": [ + "quoted\" literal", + "a", + "quoted\\\" literal", + "'quoted\" literal'" + ], + "result": [ + "quoted\" literal" + ] + }, + { + "name": "filter, literal true must be compared", + "selector": "$[?true]", + "invalid_selector": true + }, + { + "name": "filter, literal false must be compared", + "selector": "$[?false]", + "invalid_selector": true + }, + { + "name": "filter, literal string must be compared", + "selector": "$[?'abc']", + "invalid_selector": true + }, + { + "name": "filter, literal int must be compared", + "selector": "$[?2]", + "invalid_selector": true + }, + { + "name": "filter, literal float must be compared", + "selector": "$[?2.2]", + "invalid_selector": true + }, + { + "name": "filter, literal null must be compared", + "selector": "$[?null]", + "invalid_selector": true + }, + { + "name": "filter, and, literals must be compared", + "selector": "$[?true && false]", + "invalid_selector": true + }, + { + "name": "filter, or, literals must be compared", + "selector": "$[?true || false]", + "invalid_selector": true + }, + { + "name": "filter, and, right hand literal must be compared", + "selector": "$[?true == false && false]", + "invalid_selector": true + }, + { + "name": "filter, or, right hand literal must be compared", + "selector": "$[?true == false || false]", + "invalid_selector": true + }, + { + "name": "filter, and, left hand literal must be compared", + "selector": "$[?false && true == false]", + "invalid_selector": true + }, + { + "name": "filter, or, left hand literal must be compared", + "selector": "$[?false || true == false]", + "invalid_selector": true + }, + { + "name": "index selector, first element", + "selector": "$[0]", + "document": [ + "first", + "second" + ], + "result": [ + "first" + ] + }, + { + "name": "index selector, second element", + "selector": "$[1]", + "document": [ + "first", + "second" + ], + "result": [ + "second" + ] + }, + { + "name": "index selector, out of bound", + "selector": "$[2]", + "document": [ + "first", + "second" + ], + "result": [] + }, + { + "name": "index selector, overflowing index", + "selector": "$[231584178474632390847141970017375815706539969331281128078915168015826259279872]", + "invalid_selector": true + }, + { + "name": "index selector, not actually an index, overflowing index leads into general text", + "selector": "$[231584178474632390847141970017375815706539969331281128078915168SomeRandomText]", + "invalid_selector": true + }, + { + "name": "index selector, negative", + "selector": "$[-1]", + "document": [ + "first", + "second" + ], + "result": [ + "second" + ] + }, + { + "name": "index selector, more negative", + "selector": "$[-2]", + "document": [ + "first", + "second" + ], + "result": [ + "first" + ] + }, + { + "name": "index selector, negative out of bound", + "selector": "$[-3]", + "document": [ + "first", + "second" + ], + "result": [] + }, + { + "name": "index selector, on object", + "selector": "$[0]", + "document": { + "foo": 1 + }, + "result": [] + }, + { + "name": "index selector, leading 0", + "selector": "$[01]", + "invalid_selector": true + }, + { + "name": "index selector, leading -0", + "selector": "$[-01]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes", + "selector": "$[\"a\"]", + "document": { + "a": "A", + "b": "B" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, absent data", + "selector": "$[\"c\"]", + "document": { + "a": "A", + "b": "B" + }, + "result": [] + }, + { + "name": "name selector, double quotes, array data", + "selector": "$[\"a\"]", + "document": [ + "first", + "second" + ], + "result": [] + }, + { + "name": "name selector, double quotes, embedded U+0000", + "selector": "$[\"\u0000\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0001", + "selector": "$[\"\u0001\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0002", + "selector": "$[\"\u0002\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0003", + "selector": "$[\"\u0003\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0004", + "selector": "$[\"\u0004\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0005", + "selector": "$[\"\u0005\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0006", + "selector": "$[\"\u0006\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0007", + "selector": "$[\"\u0007\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0008", + "selector": "$[\"\b\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0009", + "selector": "$[\"\t\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+000A", + "selector": "$[\"\n\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+000B", + "selector": "$[\"\u000b\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+000C", + "selector": "$[\"\f\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+000D", + "selector": "$[\"\r\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+000E", + "selector": "$[\"\u000e\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+000F", + "selector": "$[\"\u000f\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0010", + "selector": "$[\"\u0010\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0011", + "selector": "$[\"\u0011\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0012", + "selector": "$[\"\u0012\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0013", + "selector": "$[\"\u0013\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0014", + "selector": "$[\"\u0014\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0015", + "selector": "$[\"\u0015\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0016", + "selector": "$[\"\u0016\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0017", + "selector": "$[\"\u0017\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0018", + "selector": "$[\"\u0018\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0019", + "selector": "$[\"\u0019\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+001A", + "selector": "$[\"\u001a\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+001B", + "selector": "$[\"\u001b\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+001C", + "selector": "$[\"\u001c\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+001D", + "selector": "$[\"\u001d\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+001E", + "selector": "$[\"\u001e\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+001F", + "selector": "$[\"\u001f\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded U+0020", + "selector": "$[\" \"]", + "document": { + " ": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped double quote", + "selector": "$[\"\\\"\"]", + "document": { + "\"": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped reverse solidus", + "selector": "$[\"\\\\\"]", + "document": { + "\\": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped solidus", + "selector": "$[\"\\/\"]", + "document": { + "/": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped backspace", + "selector": "$[\"\\b\"]", + "document": { + "\b": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped form feed", + "selector": "$[\"\\f\"]", + "document": { + "\f": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped line feed", + "selector": "$[\"\\n\"]", + "document": { + "\n": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped carriage return", + "selector": "$[\"\\r\"]", + "document": { + "\r": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped tab", + "selector": "$[\"\\t\"]", + "document": { + "\t": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped ☺, upper case hex", + "selector": "$[\"\\u263A\"]", + "document": { + "☺": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, escaped ☺, lower case hex", + "selector": "$[\"\\u263a\"]", + "document": { + "☺": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, surrogate pair 𝄞", + "selector": "$[\"\\uD834\\uDD1E\"]", + "document": { + "𝄞": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, surrogate pair 😀", + "selector": "$[\"\\uD83D\\uDE00\"]", + "document": { + "😀": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, double quotes, invalid escaped single quote", + "selector": "$[\"\\'\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, embedded double quote", + "selector": "$[\"\"\"]", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, incomplete escape", + "selector": "$[\"\\\"]", + "invalid_selector": true + }, + { + "name": "name selector, single quotes", + "selector": "$['a']", + "document": { + "a": "A", + "b": "B" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, absent data", + "selector": "$['c']", + "document": { + "a": "A", + "b": "B" + }, + "result": [] + }, + { + "name": "name selector, single quotes, array data", + "selector": "$['a']", + "document": [ + "first", + "second" + ], + "result": [] + }, + { + "name": "name selector, single quotes, embedded U+0000", + "selector": "$['\u0000']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0001", + "selector": "$['\u0001']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0002", + "selector": "$['\u0002']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0003", + "selector": "$['\u0003']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0004", + "selector": "$['\u0004']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0005", + "selector": "$['\u0005']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0006", + "selector": "$['\u0006']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0007", + "selector": "$['\u0007']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0008", + "selector": "$['\b']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0009", + "selector": "$['\t']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+000A", + "selector": "$['\n']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+000B", + "selector": "$['\u000b']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+000C", + "selector": "$['\f']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+000D", + "selector": "$['\r']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+000E", + "selector": "$['\u000e']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+000F", + "selector": "$['\u000f']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0010", + "selector": "$['\u0010']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0011", + "selector": "$['\u0011']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0012", + "selector": "$['\u0012']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0013", + "selector": "$['\u0013']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0014", + "selector": "$['\u0014']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0015", + "selector": "$['\u0015']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0016", + "selector": "$['\u0016']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0017", + "selector": "$['\u0017']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0018", + "selector": "$['\u0018']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0019", + "selector": "$['\u0019']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+001A", + "selector": "$['\u001a']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+001B", + "selector": "$['\u001b']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+001C", + "selector": "$['\u001c']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+001D", + "selector": "$['\u001d']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+001E", + "selector": "$['\u001e']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+001F", + "selector": "$['\u001f']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded U+0020", + "selector": "$[' ']", + "document": { + " ": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped single quote", + "selector": "$['\\'']", + "document": { + "'": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped reverse solidus", + "selector": "$['\\\\']", + "document": { + "\\": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped solidus", + "selector": "$['\\/']", + "document": { + "/": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped backspace", + "selector": "$['\\b']", + "document": { + "\b": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped form feed", + "selector": "$['\\f']", + "document": { + "\f": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped line feed", + "selector": "$['\\n']", + "document": { + "\n": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped carriage return", + "selector": "$['\\r']", + "document": { + "\r": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped tab", + "selector": "$['\\t']", + "document": { + "\t": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped ☺, upper case hex", + "selector": "$['\\u263A']", + "document": { + "☺": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, escaped ☺, lower case hex", + "selector": "$['\\u263a']", + "document": { + "☺": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, surrogate pair 𝄞", + "selector": "$['\\uD834\\uDD1E']", + "document": { + "𝄞": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, surrogate pair 😀", + "selector": "$['\\uD83D\\uDE00']", + "document": { + "😀": "A" + }, + "result": [ + "A" + ] + }, + { + "name": "name selector, single quotes, invalid escaped double quote", + "selector": "$['\\\"']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, embedded single quote", + "selector": "$[''']", + "invalid_selector": true + }, + { + "name": "name selector, single quotes, incomplete escape", + "selector": "$['\\']", + "invalid_selector": true + }, + { + "name": "name selector, double quotes, empty", + "selector": "$[\"\"]", + "document": { + "a": "A", + "b": "B", + "": "C" + }, + "result": [ + "C" + ] + }, + { + "name": "name selector, single quotes, empty", + "selector": "$['']", + "document": { + "a": "A", + "b": "B", + "": "C" + }, + "result": [ + "C" + ] + }, + { + "name": "slice selector, slice selector", + "selector": "$[1:3]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 1, + 2 + ] + }, + { + "name": "slice selector, slice selector with step", + "selector": "$[1:6:2]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 1, + 3, + 5 + ] + }, + { + "name": "slice selector, slice selector with everything omitted, short form", + "selector": "$[:]", + "document": [ + 0, + 1, + 2, + 3 + ], + "result": [ + 0, + 1, + 2, + 3 + ] + }, + { + "name": "slice selector, slice selector with everything omitted, long form", + "selector": "$[::]", + "document": [ + 0, + 1, + 2, + 3 + ], + "result": [ + 0, + 1, + 2, + 3 + ] + }, + { + "name": "slice selector, slice selector with start omitted", + "selector": "$[:2]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 0, + 1 + ] + }, + { + "name": "slice selector, slice selector with start and end omitted", + "selector": "$[::2]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 0, + 2, + 4, + 6, + 8 + ] + }, + { + "name": "slice selector, negative step with default start and end", + "selector": "$[::-1]", + "document": [ + 0, + 1, + 2, + 3 + ], + "result": [ + 3, + 2, + 1, + 0 + ] + }, + { + "name": "slice selector, negative step with default start", + "selector": "$[:0:-1]", + "document": [ + 0, + 1, + 2, + 3 + ], + "result": [ + 3, + 2, + 1 + ] + }, + { + "name": "slice selector, negative step with default end", + "selector": "$[2::-1]", + "document": [ + 0, + 1, + 2, + 3 + ], + "result": [ + 2, + 1, + 0 + ] + }, + { + "name": "slice selector, larger negative step", + "selector": "$[::-2]", + "document": [ + 0, + 1, + 2, + 3 + ], + "result": [ + 3, + 1 + ] + }, + { + "name": "slice selector, negative range with default step", + "selector": "$[-1:-3]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [] + }, + { + "name": "slice selector, negative range with negative step", + "selector": "$[-1:-3:-1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 9, + 8 + ] + }, + { + "name": "slice selector, negative range with larger negative step", + "selector": "$[-1:-6:-2]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 9, + 7, + 5 + ] + }, + { + "name": "slice selector, larger negative range with larger negative step", + "selector": "$[-1:-7:-2]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 9, + 7, + 5 + ] + }, + { + "name": "slice selector, negative from, positive to", + "selector": "$[-5:7]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 5, + 6 + ] + }, + { + "name": "slice selector, negative from", + "selector": "$[-2:]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 8, + 9 + ] + }, + { + "name": "slice selector, positive from, negative to", + "selector": "$[1:-1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ] + }, + { + "name": "slice selector, negative from, positive to, negative step", + "selector": "$[-1:1:-1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2 + ] + }, + { + "name": "slice selector, positive from, negative to, negative step", + "selector": "$[7:-5:-1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 7, + 6 + ] + }, + { + "name": "slice selector, too many colons", + "selector": "$[1:2:3:4]", + "invalid_selector": true + }, + { + "name": "slice selector, non-integer array index", + "selector": "$[1:2:a]", + "invalid_selector": true + }, + { + "name": "slice selector, zero step", + "selector": "$[1:2:0]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [] + }, + { + "name": "slice selector, empty range", + "selector": "$[2:2]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [] + }, + { + "name": "slice selector, slice selector with everything omitted with empty array", + "selector": "$[:]", + "document": [], + "result": [] + }, + { + "name": "slice selector, negative step with empty array", + "selector": "$[::-1]", + "document": [], + "result": [] + }, + { + "name": "slice selector, maximal range with positive step", + "selector": "$[0:10]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + }, + { + "name": "slice selector, maximal range with negative step", + "selector": "$[9:0:-1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2, + 1 + ] + }, + { + "name": "slice selector, excessively large to value", + "selector": "$[2:113667776004]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ] + }, + { + "name": "slice selector, excessively small from value", + "selector": "$[-113667776004:1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 0 + ] + }, + { + "name": "slice selector, excessively large from value with negative step", + "selector": "$[113667776004:0:-1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2, + 1 + ] + }, + { + "name": "slice selector, excessively small to value with negative step", + "selector": "$[3:-113667776004:-1]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 3, + 2, + 1, + 0 + ] + }, + { + "name": "slice selector, excessively large step", + "selector": "$[1:10:113667776004]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 1 + ] + }, + { + "name": "slice selector, excessively small step", + "selector": "$[-1:-10:-113667776004]", + "document": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "result": [ + 9 + ] + }, + { + "name": "slice selector, overflowing to value", + "selector": "$[2:231584178474632390847141970017375815706539969331281128078915168015826259279872]", + "invalid_selector": true + }, + { + "name": "slice selector, underflowing from value", + "selector": "$[-231584178474632390847141970017375815706539969331281128078915168015826259279872:1]", + "invalid_selector": true + }, + { + "name": "slice selector, overflowing from value with negative step", + "selector": "$[231584178474632390847141970017375815706539969331281128078915168015826259279872:0:-1]", + "invalid_selector": true + }, + { + "name": "slice selector, underflowing to value with negative step", + "selector": "$[3:-231584178474632390847141970017375815706539969331281128078915168015826259279872:-1]", + "invalid_selector": true + }, + { + "name": "slice selector, overflowing step", + "selector": "$[1:10:231584178474632390847141970017375815706539969331281128078915168015826259279872]", + "invalid_selector": true + }, + { + "name": "slice selector, underflowing step", + "selector": "$[-1:-10:-231584178474632390847141970017375815706539969331281128078915168015826259279872]", + "invalid_selector": true + }, + { + "name": "functions, count, count function", + "selector": "$[?count(@..*)>2]", + "document": [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ], + "d": "f" + }, + { + "a": 1, + "d": "f" + } + ], + "result": [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ], + "d": "f" + } + ] + }, + { + "name": "functions, count, single-node arg", + "selector": "$[?count(@.a)>1]", + "document": [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ], + "d": "f" + }, + { + "a": 1, + "d": "f" + } + ], + "result": [] + }, + { + "name": "functions, count, multiple-selector arg", + "selector": "$[?count(@['a','d'])>1]", + "document": [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ], + "d": "f" + }, + { + "a": 1, + "d": "f" + } + ], + "result": [ + { + "a": [ + 1 + ], + "d": "f" + }, + { + "a": 1, + "d": "f" + } + ] + }, + { + "name": "functions, count, non-query arg, number", + "selector": "$[?count(1)>2]", + "invalid_selector": true + }, + { + "name": "functions, count, non-query arg, string", + "selector": "$[?count('string')>2]", + "invalid_selector": true + }, + { + "name": "functions, count, non-query arg, true", + "selector": "$[?count(true)>2]", + "invalid_selector": true + }, + { + "name": "functions, count, non-query arg, false", + "selector": "$[?count(false)>2]", + "invalid_selector": true + }, + { + "name": "functions, count, non-query arg, null", + "selector": "$[?count(null)>2]", + "invalid_selector": true + }, + { + "name": "functions, count, result must be compared", + "selector": "$[?count(@..*)]", + "invalid_selector": true + }, + { + "name": "functions, count, no params", + "selector": "$[?count()==1]", + "invalid_selector": true + }, + { + "name": "functions, count, too many params", + "selector": "$[?count(@.a,@.b)==1]", + "invalid_selector": true + }, + { + "name": "functions, length, string data", + "selector": "$[?length(@.a)>=2]", + "document": [ + { + "a": "ab" + }, + { + "a": "d" + } + ], + "result": [ + { + "a": "ab" + } + ] + }, + { + "name": "functions, length, string data, unicode", + "selector": "$[?length(@)==2]", + "document": [ + "☺", + "☺☺", + "☺☺☺", + "ж", + "жж", + "жжж", + "磨", + "阿美", + "形声字" + ], + "result": [ + "☺☺", + "жж", + "阿美" + ] + }, + { + "name": "functions, length, array data", + "selector": "$[?length(@.a)>=2]", + "document": [ + { + "a": [ + 1, + 2, + 3 + ] + }, + { + "a": [ + 1 + ] + } + ], + "result": [ + { + "a": [ + 1, + 2, + 3 + ] + } + ] + }, + { + "name": "functions, length, missing data", + "selector": "$[?length(@.a)>=2]", + "document": [ + { + "d": "f" + } + ], + "result": [] + }, + { + "name": "functions, length, number arg", + "selector": "$[?length(1)>=2]", + "document": [ + { + "d": "f" + } + ], + "result": [] + }, + { + "name": "functions, length, true arg", + "selector": "$[?length(true)>=2]", + "document": [ + { + "d": "f" + } + ], + "result": [] + }, + { + "name": "functions, length, false arg", + "selector": "$[?length(false)>=2]", + "document": [ + { + "d": "f" + } + ], + "result": [] + }, + { + "name": "functions, length, null arg", + "selector": "$[?length(null)>=2]", + "document": [ + { + "d": "f" + } + ], + "result": [] + }, + { + "name": "functions, length, result must be compared", + "selector": "$[?length(@.a)]", + "invalid_selector": true + }, + { + "name": "functions, length, no params", + "selector": "$[?length()==1]", + "invalid_selector": true + }, + { + "name": "functions, length, too many params", + "selector": "$[?length(@.a,@.b)==1]", + "invalid_selector": true + }, + { + "name": "functions, length, non-singular query arg", + "selector": "$[?length(@.*)<3]", + "invalid_selector": true + }, + { + "name": "functions, length, arg is a function expression", + "selector": "$.values[?length(@.a)==length(value($..c))]", + "document": { + "c": "cd", + "values": [ + { + "a": "ab" + }, + { + "a": "d" + } + ] + }, + "result": [ + { + "a": "ab" + } + ] + }, + { + "name": "functions, length, arg is special nothing", + "selector": "$[?length(value(@.a))>0]", + "document": [ + { + "a": "ab" + }, + { + "c": "d" + }, + { + "a": null + } + ], + "result": [ + { + "a": "ab" + } + ] + }, + { + "name": "functions, match, found match", + "selector": "$[?match(@.a, 'a.*')]", + "document": [ + { + "a": "ab" + } + ], + "result": [ + { + "a": "ab" + } + ] + }, + { + "name": "functions, match, double quotes", + "selector": "$[?match(@.a, \"a.*\")]", + "document": [ + { + "a": "ab" + } + ], + "result": [ + { + "a": "ab" + } + ] + }, + { + "name": "functions, match, regex from the document", + "selector": "$.values[?match(@, $.regex)]", + "document": { + "regex": "b.?b", + "values": [ + "abc", + "bcd", + "bab", + "bba", + "bbab", + "b", + true, + [], + {} + ] + }, + "result": [ + "bab" + ] + }, + { + "name": "functions, match, don't select match", + "selector": "$[?!match(@.a, 'a.*')]", + "document": [ + { + "a": "ab" + } + ], + "result": [] + }, + { + "name": "functions, match, not a match", + "selector": "$[?match(@.a, 'a.*')]", + "document": [ + { + "a": "bc" + } + ], + "result": [] + }, + { + "name": "functions, match, select non-match", + "selector": "$[?!match(@.a, 'a.*')]", + "document": [ + { + "a": "bc" + } + ], + "result": [ + { + "a": "bc" + } + ] + }, + { + "name": "functions, match, non-string first arg", + "selector": "$[?match(1, 'a.*')]", + "document": [ + { + "a": "bc" + } + ], + "result": [] + }, + { + "name": "functions, match, non-string second arg", + "selector": "$[?match(@.a, 1)]", + "document": [ + { + "a": "bc" + } + ], + "result": [] + }, + { + "name": "functions, match, filter, match function, unicode char class, uppercase", + "selector": "$[?match(@, '\\\\p{Lu}')]", + "document": [ + "ж", + "Ж", + "1", + "жЖ", + true, + [], + {} + ], + "result": [ + "Ж" + ] + }, + { + "name": "functions, match, filter, match function, unicode char class negated, uppercase", + "selector": "$[?match(@, '\\\\P{Lu}')]", + "document": [ + "ж", + "Ж", + "1", + true, + [], + {} + ], + "result": [ + "ж", + "1" + ] + }, + { + "name": "functions, match, filter, match function, unicode, surrogate pair", + "selector": "$[?match(@, 'a.b')]", + "document": [ + "a𐄁b", + "ab", + "1", + true, + [], + {} + ], + "result": [ + "a𐄁b" + ] + }, + { + "name": "functions, match, dot matcher on \\u2028", + "selector": "$[?match(@, '.')]", + "document": [ + "\u2028", + "\r", + "\n", + true, + [], + {} + ], + "result": [ + "\u2028" + ] + }, + { + "name": "functions, match, dot matcher on \\u2029", + "selector": "$[?match(@, '.')]", + "document": [ + "\u2029", + "\r", + "\n", + true, + [], + {} + ], + "result": [ + "\u2029" + ] + }, + { + "name": "functions, match, result cannot be compared", + "selector": "$[?match(@.a, 'a.*')==true]", + "invalid_selector": true + }, + { + "name": "functions, match, too few params", + "selector": "$[?match(@.a)==1]", + "invalid_selector": true + }, + { + "name": "functions, match, too many params", + "selector": "$[?match(@.a,@.b,@.c)==1]", + "invalid_selector": true + }, + { + "name": "functions, match, arg is a function expression", + "selector": "$.values[?match(@.a, value($..['regex']))]", + "document": { + "regex": "a.*", + "values": [ + { + "a": "ab" + }, + { + "a": "ba" + } + ] + }, + "result": [ + { + "a": "ab" + } + ] + }, + { + "name": "functions, match, dot in character class", + "selector": "$[?match(@, 'a[.b]c')]", + "document": [ + "abc", + "a.c", + "axc" + ], + "result": [ + "abc", + "a.c" + ] + }, + { + "name": "functions, match, escaped dot", + "selector": "$[?match(@, 'a\\\\.c')]", + "document": [ + "abc", + "a.c", + "axc" + ], + "result": [ + "a.c" + ] + }, + { + "name": "functions, match, escaped backslash before dot", + "selector": "$[?match(@, 'a\\\\\\\\.c')]", + "document": [ + "abc", + "a.c", + "axc", + "a\\\u2028c" + ], + "result": [ + "a\\\u2028c" + ] + }, + { + "name": "functions, match, escaped left square bracket", + "selector": "$[?match(@, 'a\\\\[.c')]", + "document": [ + "abc", + "a.c", + "a[\u2028c" + ], + "result": [ + "a[\u2028c" + ] + }, + { + "name": "functions, match, escaped right square bracket", + "selector": "$[?match(@, 'a[\\\\].]c')]", + "document": [ + "abc", + "a.c", + "a\u2028c", + "a]c" + ], + "result": [ + "a.c", + "a]c" + ] + }, + { + "name": "functions, match, explicit caret", + "selector": "$[?match(@, '^ab.*')]", + "document": [ + "abc", + "axc", + "ab", + "xab" + ], + "result": [ + "abc", + "ab" + ] + }, + { + "name": "functions, match, explicit dollar", + "selector": "$[?match(@, '.*bc$')]", + "document": [ + "abc", + "axc", + "ab", + "abcx" + ], + "result": [ + "abc" + ] + }, + { + "name": "functions, search, at the end", + "selector": "$[?search(@.a, 'a.*')]", + "document": [ + { + "a": "the end is ab" + } + ], + "result": [ + { + "a": "the end is ab" + } + ] + }, + { + "name": "functions, search, double quotes", + "selector": "$[?search(@.a, \"a.*\")]", + "document": [ + { + "a": "the end is ab" + } + ], + "result": [ + { + "a": "the end is ab" + } + ] + }, + { + "name": "functions, search, at the start", + "selector": "$[?search(@.a, 'a.*')]", + "document": [ + { + "a": "ab is at the start" + } + ], + "result": [ + { + "a": "ab is at the start" + } + ] + }, + { + "name": "functions, search, in the middle", + "selector": "$[?search(@.a, 'a.*')]", + "document": [ + { + "a": "contains two matches" + } + ], + "result": [ + { + "a": "contains two matches" + } + ] + }, + { + "name": "functions, search, regex from the document", + "selector": "$.values[?search(@, $.regex)]", + "document": { + "regex": "b.?b", + "values": [ + "abc", + "bcd", + "bab", + "bba", + "bbab", + "b", + true, + [], + {} + ] + }, + "result": [ + "bab", + "bba", + "bbab" + ] + }, + { + "name": "functions, search, don't select match", + "selector": "$[?!search(@.a, 'a.*')]", + "document": [ + { + "a": "contains two matches" + } + ], + "result": [] + }, + { + "name": "functions, search, not a match", + "selector": "$[?search(@.a, 'a.*')]", + "document": [ + { + "a": "bc" + } + ], + "result": [] + }, + { + "name": "functions, search, select non-match", + "selector": "$[?!search(@.a, 'a.*')]", + "document": [ + { + "a": "bc" + } + ], + "result": [ + { + "a": "bc" + } + ] + }, + { + "name": "functions, search, non-string first arg", + "selector": "$[?search(1, 'a.*')]", + "document": [ + { + "a": "bc" + } + ], + "result": [] + }, + { + "name": "functions, search, non-string second arg", + "selector": "$[?search(@.a, 1)]", + "document": [ + { + "a": "bc" + } + ], + "result": [] + }, + { + "name": "functions, search, filter, search function, unicode char class, uppercase", + "selector": "$[?search(@, '\\\\p{Lu}')]", + "document": [ + "ж", + "Ж", + "1", + "жЖ", + true, + [], + {} + ], + "result": [ + "Ж", + "жЖ" + ] + }, + { + "name": "functions, search, filter, search function, unicode char class negated, uppercase", + "selector": "$[?search(@, '\\\\P{Lu}')]", + "document": [ + "ж", + "Ж", + "1", + true, + [], + {} + ], + "result": [ + "ж", + "1" + ] + }, + { + "name": "functions, search, filter, search function, unicode, surrogate pair", + "selector": "$[?search(@, 'a.b')]", + "document": [ + "a𐄁bc", + "abc", + "1", + true, + [], + {} + ], + "result": [ + "a𐄁bc" + ] + }, + { + "name": "functions, search, dot matcher on \\u2028", + "selector": "$[?search(@, '.')]", + "document": [ + "\u2028", + "\r\u2028\n", + "\r", + "\n", + true, + [], + {} + ], + "result": [ + "\u2028", + "\r\u2028\n" + ] + }, + { + "name": "functions, search, dot matcher on \\u2029", + "selector": "$[?search(@, '.')]", + "document": [ + "\u2029", + "\r\u2029\n", + "\r", + "\n", + true, + [], + {} + ], + "result": [ + "\u2029", + "\r\u2029\n" + ] + }, + { + "name": "functions, search, result cannot be compared", + "selector": "$[?search(@.a, 'a.*')==true]", + "invalid_selector": true + }, + { + "name": "functions, search, too few params", + "selector": "$[?search(@.a)]", + "invalid_selector": true + }, + { + "name": "functions, search, too many params", + "selector": "$[?search(@.a,@.b,@.c)]", + "invalid_selector": true + }, + { + "name": "functions, search, arg is a function expression", + "selector": "$.values[?search(@, value($..['regex']))]", + "document": { + "regex": "b.?b", + "values": [ + "abc", + "bcd", + "bab", + "bba", + "bbab", + "b", + true, + [], + {} + ] + }, + "result": [ + "bab", + "bba", + "bbab" + ] + }, + { + "name": "functions, search, dot in character class", + "selector": "$[?search(@, 'a[.b]c')]", + "document": [ + "x abc y", + "x a.c y", + "x axc y" + ], + "result": [ + "x abc y", + "x a.c y" + ] + }, + { + "name": "functions, search, escaped dot", + "selector": "$[?search(@, 'a\\\\.c')]", + "document": [ + "x abc y", + "x a.c y", + "x axc y" + ], + "result": [ + "x a.c y" + ] + }, + { + "name": "functions, search, escaped backslash before dot", + "selector": "$[?search(@, 'a\\\\\\\\.c')]", + "document": [ + "x abc y", + "x a.c y", + "x axc y", + "x a\\\u2028c y" + ], + "result": [ + "x a\\\u2028c y" + ] + }, + { + "name": "functions, search, escaped left square bracket", + "selector": "$[?search(@, 'a\\\\[.c')]", + "document": [ + "x abc y", + "x a.c y", + "x a[\u2028c y" + ], + "result": [ + "x a[\u2028c y" + ] + }, + { + "name": "functions, search, escaped right square bracket", + "selector": "$[?search(@, 'a[\\\\].]c')]", + "document": [ + "x abc y", + "x a.c y", + "x a\u2028c y", + "x a]c y" + ], + "result": [ + "x a.c y", + "x a]c y" + ] + }, + { + "name": "functions, value, single-value nodelist", + "selector": "$[?value(@.*)==4]", + "document": [ + [ + 4 + ], + { + "foo": 4 + }, + [ + 5 + ], + { + "foo": 5 + }, + 4 + ], + "result": [ + [ + 4 + ], + { + "foo": 4 + } + ] + }, + { + "name": "functions, value, multi-value nodelist", + "selector": "$[?value(@.*)==4]", + "document": [ + [ + 4, + 4 + ], + { + "foo": 4, + "bar": 4 + } + ], + "result": [] + }, + { + "name": "functions, value, too few params", + "selector": "$[?value()==4]", + "invalid_selector": true + }, + { + "name": "functions, value, too many params", + "selector": "$[?value(@.a,@.b)==4]", + "invalid_selector": true + }, + { + "name": "functions, value, result must be compared", + "selector": "$[?value(@.a)]", + "invalid_selector": true + }, + { + "name": "whitespace, filter, space between question mark and expression", + "selector": "$[? @.a]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, newline between question mark and expression", + "selector": "$[?\n@.a]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, tab between question mark and expression", + "selector": "$[?\t@.a]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, return between question mark and expression", + "selector": "$[?\r@.a]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, space between question mark and parenthesized expression", + "selector": "$[? (@.a)]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, newline between question mark and parenthesized expression", + "selector": "$[?\n(@.a)]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, tab between question mark and parenthesized expression", + "selector": "$[?\t(@.a)]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, return between question mark and parenthesized expression", + "selector": "$[?\r(@.a)]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, space between parenthesized expression and bracket", + "selector": "$[?(@.a) ]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, newline between parenthesized expression and bracket", + "selector": "$[?(@.a)\n]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, tab between parenthesized expression and bracket", + "selector": "$[?(@.a)\t]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, return between parenthesized expression and bracket", + "selector": "$[?(@.a)\r]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, space between bracket and question mark", + "selector": "$[ ?@.a]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, newline between bracket and question mark", + "selector": "$[\n?@.a]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, tab between bracket and question mark", + "selector": "$[\t?@.a]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, filter, return between bracket and question mark", + "selector": "$[\r?@.a]", + "document": [ + { + "a": "b", + "d": "e" + }, + { + "b": "c", + "d": "f" + } + ], + "result": [ + { + "a": "b", + "d": "e" + } + ] + }, + { + "name": "whitespace, functions, space between function name and parenthesis", + "selector": "$[?count (@.*)==1]", + "invalid_selector": true + }, + { + "name": "whitespace, functions, newline between function name and parenthesis", + "selector": "$[?count\n(@.*)==1]", + "invalid_selector": true + }, + { + "name": "whitespace, functions, tab between function name and parenthesis", + "selector": "$[?count\t(@.*)==1]", + "invalid_selector": true + }, + { + "name": "whitespace, functions, return between function name and parenthesis", + "selector": "$[?count\r(@.*)==1]", + "invalid_selector": true + }, + { + "name": "whitespace, functions, space between parenthesis and arg", + "selector": "$[?count( @.*)==1]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, functions, newline between parenthesis and arg", + "selector": "$[?count(\n@.*)==1]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, functions, tab between parenthesis and arg", + "selector": "$[?count(\t@.*)==1]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, functions, return between parenthesis and arg", + "selector": "$[?count(\r@.*)==1]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, functions, space between arg and comma", + "selector": "$[?search(@ ,'[a-z]+')]", + "document": [ + "foo", + "123" + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, newline between arg and comma", + "selector": "$[?search(@\n,'[a-z]+')]", + "document": [ + "foo", + "123" + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, tab between arg and comma", + "selector": "$[?search(@\t,'[a-z]+')]", + "document": [ + "foo", + "123" + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, return between arg and comma", + "selector": "$[?search(@\r,'[a-z]+')]", + "document": [ + "foo", + "123" + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, space between comma and arg", + "selector": "$[?search(@, '[a-z]+')]", + "document": [ + "foo", + "123" + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, newline between comma and arg", + "selector": "$[?search(@,\n'[a-z]+')]", + "document": [ + "foo", + "123" + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, tab between comma and arg", + "selector": "$[?search(@,\t'[a-z]+')]", + "document": [ + "foo", + "123" + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, return between comma and arg", + "selector": "$[?search(@,\r'[a-z]+')]", + "document": [ + "foo", + "123" + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, space between arg and parenthesis", + "selector": "$[?count(@.* )==1]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, functions, newline between arg and parenthesis", + "selector": "$[?count(@.*\n)==1]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, functions, tab between arg and parenthesis", + "selector": "$[?count(@.*\t)==1]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, functions, return between arg and parenthesis", + "selector": "$[?count(@.*\r)==1]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, functions, spaces in a relative singular selector", + "selector": "$[?length(@ .a .b) == 3]", + "document": [ + { + "a": { + "b": "foo" + } + }, + {} + ], + "result": [ + { + "a": { + "b": "foo" + } + } + ] + }, + { + "name": "whitespace, functions, newlines in a relative singular selector", + "selector": "$[?length(@\n.a\n.b) == 3]", + "document": [ + { + "a": { + "b": "foo" + } + }, + {} + ], + "result": [ + { + "a": { + "b": "foo" + } + } + ] + }, + { + "name": "whitespace, functions, tabs in a relative singular selector", + "selector": "$[?length(@\t.a\t.b) == 3]", + "document": [ + { + "a": { + "b": "foo" + } + }, + {} + ], + "result": [ + { + "a": { + "b": "foo" + } + } + ] + }, + { + "name": "whitespace, functions, returns in a relative singular selector", + "selector": "$[?length(@\r.a\r.b) == 3]", + "document": [ + { + "a": { + "b": "foo" + } + }, + {} + ], + "result": [ + { + "a": { + "b": "foo" + } + } + ] + }, + { + "name": "whitespace, functions, spaces in an absolute singular selector", + "selector": "$..[?length(@)==length($ [0] .a)]", + "document": [ + { + "a": "foo" + }, + {} + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, newlines in an absolute singular selector", + "selector": "$..[?length(@)==length($\n[0]\n.a)]", + "document": [ + { + "a": "foo" + }, + {} + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, tabs in an absolute singular selector", + "selector": "$..[?length(@)==length($\t[0]\t.a)]", + "document": [ + { + "a": "foo" + }, + {} + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, functions, returns in an absolute singular selector", + "selector": "$..[?length(@)==length($\r[0]\r.a)]", + "document": [ + { + "a": "foo" + }, + {} + ], + "result": [ + "foo" + ] + }, + { + "name": "whitespace, operators, space before ||", + "selector": "$[?@.a ||@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline before ||", + "selector": "$[?@.a\n||@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab before ||", + "selector": "$[?@.a\t||@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return before ||", + "selector": "$[?@.a\r||@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space after ||", + "selector": "$[?@.a|| @.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline after ||", + "selector": "$[?@.a||\n@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab after ||", + "selector": "$[?@.a||\t@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return after ||", + "selector": "$[?@.a||\r@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "c": 3 + } + ], + "result": [ + { + "a": 1 + }, + { + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space before &&", + "selector": "$[?@.a &&@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline before &&", + "selector": "$[?@.a\n&&@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab before &&", + "selector": "$[?@.a\t&&@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return before &&", + "selector": "$[?@.a\r&&@.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space after &&", + "selector": "$[?@.a&& @.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline after &&", + "selector": "$[?@.a&& @.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab after &&", + "selector": "$[?@.a&& @.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return after &&", + "selector": "$[?@.a&& @.b]", + "document": [ + { + "a": 1 + }, + { + "b": 2 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space before ==", + "selector": "$[?@.a ==@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 1 + } + ] + }, + { + "name": "whitespace, operators, newline before ==", + "selector": "$[?@.a\n==@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 1 + } + ] + }, + { + "name": "whitespace, operators, tab before ==", + "selector": "$[?@.a\t==@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 1 + } + ] + }, + { + "name": "whitespace, operators, return before ==", + "selector": "$[?@.a\r==@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 1 + } + ] + }, + { + "name": "whitespace, operators, space after ==", + "selector": "$[?@.a== @.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 1 + } + ] + }, + { + "name": "whitespace, operators, newline after ==", + "selector": "$[?@.a==\n@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 1 + } + ] + }, + { + "name": "whitespace, operators, tab after ==", + "selector": "$[?@.a==\t@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 1 + } + ] + }, + { + "name": "whitespace, operators, return after ==", + "selector": "$[?@.a==\r@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 1 + } + ] + }, + { + "name": "whitespace, operators, space before !=", + "selector": "$[?@.a !=@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline before !=", + "selector": "$[?@.a\n!=@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab before !=", + "selector": "$[?@.a\t!=@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return before !=", + "selector": "$[?@.a\r!=@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space after !=", + "selector": "$[?@.a!= @.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline after !=", + "selector": "$[?@.a!=\n@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab after !=", + "selector": "$[?@.a!=\t@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return after !=", + "selector": "$[?@.a!=\r@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space before <", + "selector": "$[?@.a <@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline before <", + "selector": "$[?@.a\n<@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab before <", + "selector": "$[?@.a\t<@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return before <", + "selector": "$[?@.a\r<@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space after <", + "selector": "$[?@.a< @.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline after <", + "selector": "$[?@.a<\n@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab after <", + "selector": "$[?@.a<\t@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return after <", + "selector": "$[?@.a<\r@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space before >", + "selector": "$[?@.b >@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline before >", + "selector": "$[?@.b\n>@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab before >", + "selector": "$[?@.b\t>@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return before >", + "selector": "$[?@.b\r>@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space after >", + "selector": "$[?@.b> @.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline after >", + "selector": "$[?@.b>\n@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab after >", + "selector": "$[?@.b>\t@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return after >", + "selector": "$[?@.b>\r@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ], + "result": [ + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space before <=", + "selector": "$[?@.a <=@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline before <=", + "selector": "$[?@.a\n<=@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab before <=", + "selector": "$[?@.a\t<=@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return before <=", + "selector": "$[?@.a\r<=@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space after <=", + "selector": "$[?@.a<= @.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline after <=", + "selector": "$[?@.a<=\n@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab after <=", + "selector": "$[?@.a<=\t@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return after <=", + "selector": "$[?@.a<=\r@.b]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space before >=", + "selector": "$[?@.b >=@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline before >=", + "selector": "$[?@.b\n>=@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab before >=", + "selector": "$[?@.b\t>=@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return before >=", + "selector": "$[?@.b\r>=@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space after >=", + "selector": "$[?@.b>= @.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, newline after >=", + "selector": "$[?@.b>=\n@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, tab after >=", + "selector": "$[?@.b>=\t@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, return after >=", + "selector": "$[?@.b>=\r@.a]", + "document": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + }, + { + "a": 2, + "b": 1 + } + ], + "result": [ + { + "a": 1, + "b": 1 + }, + { + "a": 1, + "b": 2 + } + ] + }, + { + "name": "whitespace, operators, space between logical not and test expression", + "selector": "$[?! @.a]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "d": "f" + } + ] + }, + { + "name": "whitespace, operators, newline between logical not and test expression", + "selector": "$[?!\n@.a]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "d": "f" + } + ] + }, + { + "name": "whitespace, operators, tab between logical not and test expression", + "selector": "$[?!\t@.a]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "d": "f" + } + ] + }, + { + "name": "whitespace, operators, return between logical not and test expression", + "selector": "$[?!\r@.a]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "d": "f" + } + ] + }, + { + "name": "whitespace, operators, space between logical not and parenthesized expression", + "selector": "$[?! (@.a=='b')]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "a", + "d": "e" + }, + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "whitespace, operators, newline between logical not and parenthesized expression", + "selector": "$[?!\n(@.a=='b')]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "a", + "d": "e" + }, + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "whitespace, operators, tab between logical not and parenthesized expression", + "selector": "$[?!\t(@.a=='b')]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "a", + "d": "e" + }, + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "whitespace, operators, return between logical not and parenthesized expression", + "selector": "$[?!\r(@.a=='b')]", + "document": [ + { + "a": "a", + "d": "e" + }, + { + "a": "b", + "d": "f" + }, + { + "a": "d", + "d": "f" + } + ], + "result": [ + { + "a": "a", + "d": "e" + }, + { + "a": "d", + "d": "f" + } + ] + }, + { + "name": "whitespace, selectors, space between root and bracket", + "selector": "$ ['a']", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, newline between root and bracket", + "selector": "$\n['a']", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, tab between root and bracket", + "selector": "$\t['a']", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, return between root and bracket", + "selector": "$\r['a']", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, space between bracket and bracket", + "selector": "$['a'] ['b']", + "document": { + "a": { + "b": "ab" + } + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, newline between root and bracket", + "selector": "$['a'] \n['b']", + "document": { + "a": { + "b": "ab" + } + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, tab between root and bracket", + "selector": "$['a'] \t['b']", + "document": { + "a": { + "b": "ab" + } + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, return between root and bracket", + "selector": "$['a'] \r['b']", + "document": { + "a": { + "b": "ab" + } + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, space between root and dot", + "selector": "$ .a", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, newline between root and dot", + "selector": "$\n.a", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, tab between root and dot", + "selector": "$\t.a", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, return between root and dot", + "selector": "$\r.a", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, space between dot and name", + "selector": "$. a", + "invalid_selector": true + }, + { + "name": "whitespace, selectors, newline between dot and name", + "selector": "$.\na", + "invalid_selector": true + }, + { + "name": "whitespace, selectors, tab between dot and name", + "selector": "$.\ta", + "invalid_selector": true + }, + { + "name": "whitespace, selectors, return between dot and name", + "selector": "$.\ra", + "invalid_selector": true + }, + { + "name": "whitespace, selectors, space between recursive descent and name", + "selector": "$.. a", + "invalid_selector": true + }, + { + "name": "whitespace, selectors, newline between recursive descent and name", + "selector": "$..\na", + "invalid_selector": true + }, + { + "name": "whitespace, selectors, tab between recursive descent and name", + "selector": "$..\ta", + "invalid_selector": true + }, + { + "name": "whitespace, selectors, return between recursive descent and name", + "selector": "$..\ra", + "invalid_selector": true + }, + { + "name": "whitespace, selectors, space between bracket and selector", + "selector": "$[ 'a']", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, newline between bracket and selector", + "selector": "$[\n'a']", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, tab between bracket and selector", + "selector": "$[\t'a']", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, return between bracket and selector", + "selector": "$[\r'a']", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, space between selector and bracket", + "selector": "$['a' ]", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, newline between selector and bracket", + "selector": "$['a'\n]", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, tab between selector and bracket", + "selector": "$['a'\t]", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, return between selector and bracket", + "selector": "$['a'\r]", + "document": { + "a": "ab" + }, + "result": [ + "ab" + ] + }, + { + "name": "whitespace, selectors, space between selector and comma", + "selector": "$['a' ,'b']", + "document": { + "a": "ab", + "b": "bc" + }, + "result": [ + "ab", + "bc" + ] + }, + { + "name": "whitespace, selectors, newline between selector and comma", + "selector": "$['a'\n,'b']", + "document": { + "a": "ab", + "b": "bc" + }, + "result": [ + "ab", + "bc" + ] + }, + { + "name": "whitespace, selectors, tab between selector and comma", + "selector": "$['a'\t,'b']", + "document": { + "a": "ab", + "b": "bc" + }, + "result": [ + "ab", + "bc" + ] + }, + { + "name": "whitespace, selectors, return between selector and comma", + "selector": "$['a'\r,'b']", + "document": { + "a": "ab", + "b": "bc" + }, + "result": [ + "ab", + "bc" + ] + }, + { + "name": "whitespace, selectors, space between comma and selector", + "selector": "$['a', 'b']", + "document": { + "a": "ab", + "b": "bc" + }, + "result": [ + "ab", + "bc" + ] + }, + { + "name": "whitespace, selectors, newline between comma and selector", + "selector": "$['a',\n'b']", + "document": { + "a": "ab", + "b": "bc" + }, + "result": [ + "ab", + "bc" + ] + }, + { + "name": "whitespace, selectors, tab between comma and selector", + "selector": "$['a',\t'b']", + "document": { + "a": "ab", + "b": "bc" + }, + "result": [ + "ab", + "bc" + ] + }, + { + "name": "whitespace, selectors, return between comma and selector", + "selector": "$['a',\r'b']", + "document": { + "a": "ab", + "b": "bc" + }, + "result": [ + "ab", + "bc" + ] + }, + { + "name": "whitespace, slice, space between start and colon", + "selector": "$[1 :5:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, newline between start and colon", + "selector": "$[1\n:5:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, tab between start and colon", + "selector": "$[1\t:5:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, return between start and colon", + "selector": "$[1\r:5:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, space between colon and end", + "selector": "$[1: 5:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, newline between colon and end", + "selector": "$[1:\n5:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, tab between colon and end", + "selector": "$[1:\t5:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, return between colon and end", + "selector": "$[1:\r5:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, space between end and colon", + "selector": "$[1:5 :2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, newline between end and colon", + "selector": "$[1:5\n:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, tab between end and colon", + "selector": "$[1:5\t:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, return between end and colon", + "selector": "$[1:5\r:2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, space between colon and step", + "selector": "$[1:5: 2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, newline between colon and step", + "selector": "$[1:5:\n2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, tab between colon and step", + "selector": "$[1:5:\t2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + }, + { + "name": "whitespace, slice, return between colon and step", + "selector": "$[1:5:\r2]", + "document": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "result": [ + 2, + 4 + ] + } + ] +} diff --git a/test/Hyperbee.Json.Cts/generate_tests.ps1 b/test/Hyperbee.Json.Cts/generate_tests.ps1 new file mode 100644 index 00000000..9db4fdff --- /dev/null +++ b/test/Hyperbee.Json.Cts/generate_tests.ps1 @@ -0,0 +1,296 @@ +function Invoke-WebRequestWithRetry { + param ( + [Parameter(Mandatory=$true)] + [string]$Url, + [int]$MaxRetries = 5, + [int]$RetryDelay = 3 # seconds + ) + + Write-Host "Downloading $Url" + + $attempt = 0 + while ($attempt -lt $MaxRetries) { + try { + $response = Invoke-WebRequest -Uri $Url + return $response + } + catch { + $attempt++ + Write-Host "Attempt $attempt failed: $_" + if ($attempt -ge $MaxRetries) { + throw "Failed to retrieve the content after $MaxRetries attempts. Error: $_" + } + Start-Sleep -Seconds $RetryDelay + } + } + + Write-Host "Download complete." +} + +function Get-JsonContent { + param ( + [Parameter(Mandatory=$true)] + [string]$Url, + [string]$LocalPath + ) + + if (Test-Path -Path $LocalPath) { + # Read from file location + $jsonContent = Get-Content -Path $ctsPath -Raw + Write-Host "JSON content read from '$LocalPath'." + } else { + # Fetch the JSON content as a string + $response = Invoke-WebRequestWithRetry -Url $Url + $jsonContent = $response.Content + + # Save the JSON content to a file in a pretty formatted way if SavePath is provided + if ($PSBoundParameters.ContainsKey('LocalPath')) { + $prettyJson = $jsonContent | ConvertFrom-Json -AsHashtable | ConvertTo-Json -Depth 10 + Set-Content -Path $LocalPath -Value $prettyJson + Write-Host "JSON content saved to '$LocalPath'." + } + } + + # Convert the raw JSON string to a PowerShell hashtable to access properties + $jsonObject = $jsonContent | ConvertFrom-Json -AsHashtable + + # Use regex to extract all selector properties + $pattern = '"selector"\s*:\s*"(.*?[^\\])"' + $match = [regex]::Matches($jsonContent, $pattern) + + # Iterate through all tests and collect the properties + $output = @() + for ($i = 0; $i -lt $jsonObject.tests.Count; $i++) { + $test = $jsonObject.tests[$i] + + # Split the name into category (group) and name parts + $fullName = $test['name'] + $splitName = $fullName -split ',', 2 + $group = $splitName[0].Trim() + $name = if ($splitName.Length -gt 1) { $splitName[1].Trim() } else { $null } + + # Ignore empty groups + if ([string]::IsNullOrWhiteSpace($group)) { + continue + } + + # Convert JSON to strings BEFORE adding to PSObject to prevent unwanted conversions + $document = ConvertTo-Json -InputObject $test['document'] -Depth 10 + $result = if ($test.ContainsKey('result')) { ConvertTo-Json -InputObject $test['result'] -Depth 10 } else { $null } + $results = if ($test.ContainsKey('results')) { ConvertTo-Json -InputObject $test['results'] -Depth 10 } else { $null } + $invalid_selector = if ($test.ContainsKey('invalid_selector')) { $test['invalid_selector'] } else { $null } + + $rawJsonSelector = $match[$i].Groups[1].Value + + $item = [PSCustomObject]@{ + name = $name + group = $group + document = $document + result = $result + results = $results + selector = $rawJsonSelector + invalid_selector = $invalid_selector + } + + $output += $item + } + + return $output +} + +# Helper function to convert test names to C# method names +function Convert-ToCSharpMethodName { + param ( + [string]$name + ) + return $name -replace '[^a-zA-Z0-9]', '_' +} + +function FormatJson { + param ( + [string]$json, + [int]$indent + ) + + # Ignore empty groups + if ([string]::IsNullOrWhiteSpace($json)) { + return $null + } + + # Detect the line break format + $lineBreak = if ($json -contains "`r`n") { "`r`n" } else { "`n" } + + # Split the JSON string into lines + $lines = $json -split $lineBreak + + # Create the indentation string + $indentation = " " * $indent + + # Add indentation to each line except the first + $formattedLines = $lines | ForEach-Object { $indentation + $_ } + + # Join the lines back into a single string with the detected line break format + $formattedJson = $lineBreak + ($formattedLines -join $lineBreak) + $lineBreak + $indentation + + return $formattedJson +} + +function Convert-ToPascalCase { + param ( + [string]$value + ) + + if ([string]::IsNullOrWhiteSpace($value)) { + return $value + } + + $words = $value -split '\s+' # Split the string by whitespace + $pascalCaseWords = $words | ForEach-Object { + if ([string]::IsNullOrEmpty($_)) { + continue + } + + $firstLetter = $_[0].ToString().ToUpper() + $restOfString = $_.Substring(1).ToLower() + $firstLetter + $restOfString + } + + return $pascalCaseWords -join '' +} + +function Get-UnitTestContent { + param ( + [Parameter(Mandatory=$true)] + [array]$JsonTests, + [Parameter(Mandatory=$true)] + [string]$group + ) + + # Give the class a unique name + $uniquePart = Convert-ToPascalCase -value $group + $className = "Cts$($uniquePart)Test" + + # Prepare the content for the C# unit test file + $unitTestContent = @" +// This file was auto generated. + +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Cts.Tests +{ + [TestClass] + public class $className + {`r`n +"@ + + $testNumber = 0 + + # Loop through each test case in the JSON and generate a TestMethod + foreach ($test in $JsonTests) { + $name = $test.name + $methodName = Convert-ToCSharpMethodName $name # Convert $test.name to C# method name + + if ($null -eq $name -or $name -eq "") { + continue + } + + $testNumber++ + $selector = $test.selector + + if ($selector.EndsWith('\')) { + $selector += '\' + } + + $invalidSelector = if ($test.invalid_selector) { $true } else { $false } + + $document = FormatJson -json $test.document -indent 16 + $result = FormatJson -json $test.result -indent 16 + $results = FormatJson -json $test.results -indent 16 + + # Replace placeholders in the template with actual test case data + $unitTestContent += @" + + [TestMethod( @`"$name ($testNumber)`" )] + public void Test`_$methodName`_$testNumber() + { + var selector = `"$selector`";`r`n +"@ + + if ($invalidSelector) { + $unitTestContent += @" + var document = JsonNode.Parse( `"[0]`" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + }`r`n +"@ + } else { + $unitTestContent += @" + var document = JsonNode.Parse( + `"`"`"$document`"`"`"); + var results = document.Select(selector);`r`n +"@ + if ($null -ne $result) { + $unitTestContent += @" + var expect = JsonNode.Parse( + `"`"`"$result`"`"`"); + + var match = TestHelper.MatchOne(results, expect!); + Assert.IsTrue(match); + }`r`n +"@ + } elseif ($null -ne $results) { + $unitTestContent += @" + var expectOneOf = JsonNode.Parse( + `"`"`"$results`"`"`"); + + var match = TestHelper.MatchAny(results, expectOneOf!); + Assert.IsTrue(match); + }`r`n +"@ + } else { + $unitTestContent += @" + Assert.Fail(`"missing results`"); + }`r`n +"@ + } + } + } + + # Close the class and namespace + $unitTestContent += @" + } +}`r`n +"@ + + return $unitTestContent +} + +# Generate unit-tests by category +$ctsPath = Join-Path -Path $PSScriptRoot -ChildPath "cts.json" + +$jsonUrl = "https://raw.githubusercontent.com/jsonpath-standard/jsonpath-compliance-test-suite/main/cts.json" +$jsonContent = Get-JsonContent -Url $jsonUrl -LocalPath $ctsPath + +# Group tests by category +$groupedTests = $jsonContent | Group-Object -Property { $_.group } + +# Ensure the Tests subfolder exists +$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "Tests" +if (-not (Test-Path -Path $testsFolderPath)) { + New-Item -Path $testsFolderPath -ItemType Directory | Out-Null +} + +foreach ($group in $groupedTests) { + $category = $group.Name + $categoryTests = $group.Group + + $unitTestContent = Get-UnitTestContent -JsonTests $categoryTests -group $category + + # Replace spaces with hyphens in the category for the filename + $sanitizedCategory = $category -replace ' ', '-' + $unitTestPath = Join-Path -Path $testsFolderPath -ChildPath ("cts-" + $sanitizedCategory + "-tests.cs") + Set-Content -Path $unitTestPath -Value $unitTestContent + + Write-Host "C# unit test file 'cts-$sanitizedCategory-tests.cs' generated successfully at '$unitTestPath'." +} diff --git a/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs b/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs index 9b5402c4..7f6da371 100644 --- a/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs +++ b/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs @@ -14,26 +14,6 @@ public struct TestItem public string B { get; set; } } - [TestMethod] - public void Should_SerializeJsonElement_ToObject() - { - // arrange - var source = new TestItem - { - A = "a", - B = "b" - }; - - var json = JsonSerializer.Serialize( source ); - var document = JsonDocument.Parse( json ); - - // act - var result = JsonHelper.ConvertToObject( document.RootElement ); - - // assert - Assert.AreEqual( source, result ); - } - [TestMethod] public void Should_ReturnPropertyValue_ForJsonPathPointer() { diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs index 1f3fc32c..0f6a058e 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; +using System.Linq; +using System.Reflection; using System.Text.Json.Nodes; using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; using Hyperbee.Json.Tests.TestSupport; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -32,20 +31,20 @@ public void Should_CallCustomFunction() Assert.AreEqual( "$.store.book[2].title", results[0].GetPath() ); } - private class PathNodeFunction() : FilterExtensionFunction( argumentCount: 1 ) + private class PathNodeFunction() : FilterExtensionFunction( PathMethodInfo, FilterExtensionInfo.MustCompare ) { public const string Name = "path"; - private static readonly Expression PathExpression = Expression.Constant( (Func, string>) Path ); + private static readonly MethodInfo PathMethodInfo = GetMethod( nameof( Path ) ); - protected override Expression GetExtensionExpression( Expression[] arguments ) + private static INodeType Path( INodeType arg ) { - return Expression.Invoke( PathExpression, arguments[0] ); - } + if ( arg is NodesType nodes ) + { + var node = nodes.FirstOrDefault(); + return new ValueType( node?.GetPath() ); + } - private static string Path( IEnumerable nodes ) - { - var node = nodes.FirstOrDefault(); - return node?.GetPath(); + return Constants.Null; } } } diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs index 8f2ded55..e4b2e46e 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs @@ -16,25 +16,18 @@ namespace Hyperbee.Json.Tests.Parsers; public class FilterParserTests : JsonTestBase { [DataTestMethod] + [DataRow( "((1 == 1))", true, typeof( JsonElement ) )] [DataRow( "((\"world\" == 'world') && (1 == 1))", true, typeof( JsonElement ) )] - [DataRow( "true", true, typeof( JsonElement ) )] - [DataRow( "false", false, typeof( JsonElement ) )] [DataRow( "1 == 1", true, typeof( JsonElement ) )] [DataRow( "(1 == 1)", true, typeof( JsonElement ) )] [DataRow( "(1 != 2)", true, typeof( JsonElement ) )] [DataRow( "!(1 == 2)", true, typeof( JsonElement ) )] - [DataRow( "(\"world\" == 'world') && (true || false)", true, typeof( JsonElement ) )] - [DataRow( "(\"world\" == 'world') || true", true, typeof( JsonElement ) )] [DataRow( "(\"world\" == 'world') || 1 == 1", true, typeof( JsonElement ) )] [DataRow( "!('World' != 'World') && !(1 == 2 || 1 == 3)", true, typeof( JsonElement ) )] - [DataRow( "true", true, typeof( JsonNode ) )] - [DataRow( "false", false, typeof( JsonNode ) )] [DataRow( "1 == 1", true, typeof( JsonNode ) )] [DataRow( "(1 == 1)", true, typeof( JsonNode ) )] [DataRow( "(1 != 2)", true, typeof( JsonNode ) )] [DataRow( "!(1 == 2)", true, typeof( JsonNode ) )] - [DataRow( "(\"world\" == 'world') && (true || false)", true, typeof( JsonNode ) )] - [DataRow( "(\"world\" == 'world') || true", true, typeof( JsonNode ) )] [DataRow( "(\"world\" == 'world') || 1 == 1", true, typeof( JsonNode ) )] [DataRow( "!('World' != 'World') && !(1 == 2 || 1 == 3)", true, typeof( JsonNode ) )] public void Should_MatchExpectedResult_WhenUsingConstants( string filter, bool expected, Type sourceType ) @@ -49,6 +42,23 @@ public void Should_MatchExpectedResult_WhenUsingConstants( string filter, bool e Assert.AreEqual( expected, result ); } + [DataTestMethod] + [DataRow( "true", typeof( JsonElement ) )] + [DataRow( "false", typeof( JsonElement ) )] + [DataRow( "true", typeof( JsonNode ) )] + [DataRow( "false", typeof( JsonNode ) )] + public void Should_Fail_WhenNotComparingLiterals( string filter, Type sourceType ) + { + // arrange + + // act & assert + Assert.ThrowsException( () => + { + var (expression, param) = GetExpression( filter, sourceType ); + return Execute( expression, param, sourceType ); + } ); + } + [DataTestMethod] [DataRow( "@.store.bicycle.price < 10", false, typeof( JsonElement ) )] [DataRow( "@.store.bicycle.price <= 10", false, typeof( JsonElement ) )] @@ -83,7 +93,7 @@ public void Should_MatchExpectedResult_WhenUsingConstants( string filter, bool e public void Should_MatchExpectedResult_WhenUsingJsonPath( string filter, bool expected, Type sourceType ) { // arrange & act - var result = CompileAndExecute( filter, sourceType ); + var result = CompileAndExecuteFilter( filter, sourceType ); // assert Assert.AreEqual( expected, result ); @@ -93,12 +103,12 @@ public void Should_MatchExpectedResult_WhenUsingJsonPath( string filter, bool ex [DataRow( "$.store.book[?(@.price > 20)].price", 22.99F, typeof( JsonElement ) )] [DataRow( "$.store.book[?(@.category == 'reference')].price", 8.95F, typeof( JsonElement ) )] [DataRow( "$.store.book[?(@.price < 9.00 && @.category == 'reference')].price", 8.95F, typeof( JsonElement ) )] - [DataRow( "$.store.book[?(match(@.title, \"Sayings*\" ))].price", 8.95F, typeof( JsonElement ) )] + [DataRow( "$.store.book[?(match(@.title, \"Sayings.*\" ))].price", 8.95F, typeof( JsonElement ) )] [DataRow( "$.store.book[?(@.category == $.store.book[0].category)].price", 8.95F, typeof( JsonElement ) )] [DataRow( "$.store.book[?(@.price > 20)].price", 22.99F, typeof( JsonNode ) )] [DataRow( "$.store.book[?(@.category == 'reference')].price", 8.95F, typeof( JsonNode ) )] [DataRow( "$.store.book[?(@.price < 9.00 && @.category == 'reference')].price", 8.95F, typeof( JsonNode ) )] - [DataRow( "$.store.book[?(match(@.title, \"Sayings*\" ))].price", 8.95F, typeof( JsonNode ) )] + [DataRow( "$.store.book[?(match(@.title, \"Sayings.*\" ))].price", 8.95F, typeof( JsonNode ) )] [DataRow( "$.store.book[?(@.category == $.store.book[0].category)].price", 8.95F, typeof( JsonNode ) )] public void Should_ReturnExpectedResult_WhenUsingExpressionEvaluator( string filter, float expected, Type sourceType ) { @@ -114,20 +124,20 @@ public void Should_ReturnExpectedResult_WhenUsingExpressionEvaluator( string fil [DataRow( "count(@.store.book.*) == 4", true, typeof( JsonElement ) )] [DataRow( "length(@.store.book) == 4", true, typeof( JsonElement ) )] [DataRow( "length(@.store.book[0].category) == 9", true, typeof( JsonElement ) )] - [DataRow( "match(@.store.book[0].title, \"Sayings*\" )", true, typeof( JsonElement ) )] + [DataRow( "match(@.store.book[0].title, \"Sayings.*\" )", true, typeof( JsonElement ) )] [DataRow( "search(@.store.book[0].author, \"[Nn]igel Rees\" )", true, typeof( JsonElement ) )] [DataRow( "value(@.store.book[0].author) == \"Nigel Rees\"", true, typeof( JsonElement ) )] [DataRow( "count(@.store.book) == 1", true, typeof( JsonNode ) )] [DataRow( "count(@.store.book.*) == 4", true, typeof( JsonNode ) )] [DataRow( "length(@.store.book) == 4", true, typeof( JsonNode ) )] [DataRow( "length(@.store.book[0].category) == 9", true, typeof( JsonNode ) )] - [DataRow( "match(@.store.book[0].title, \"Sayings*\" )", true, typeof( JsonNode ) )] + [DataRow( "match(@.store.book[0].title, \"Sayings.*\" )", true, typeof( JsonNode ) )] [DataRow( "search(@.store.book[0].author, \"[Nn]igel Rees\" )", true, typeof( JsonNode ) )] [DataRow( "value(@.store.book[0].author) == \"Nigel Rees\"", true, typeof( JsonNode ) )] public void Should_MatchExpectedResult_WhenUsingFunctions( string filter, bool expected, Type sourceType ) { // arrange & act - var result = CompileAndExecute( filter, sourceType ); + var result = CompileAndExecuteFilter( filter, sourceType ); // assert Assert.AreEqual( expected, result ); @@ -135,30 +145,36 @@ public void Should_MatchExpectedResult_WhenUsingFunctions( string filter, bool e [DataTestMethod] [DataRow( "length(@.store.book) == 4 ", true, typeof( JsonElement ) )] - [DataRow( "length (@.store.book) == 4 ", true, typeof( JsonElement ) )] [DataRow( " length(@.store.book) == 4", true, typeof( JsonElement ) )] [DataRow( " length(@.store.book) == 4 ", true, typeof( JsonElement ) )] [DataRow( " length( @.store.book ) == 4 ", true, typeof( JsonElement ) )] [DataRow( "4 == length( @.store.book ) ", true, typeof( JsonElement ) )] - [DataRow( "4 == length ( @.store.book ) ", true, typeof( JsonElement ) )] [DataRow( " 4 == length(@.store.book)", true, typeof( JsonElement ) )] [DataRow( " 4 == length(@.store.book) ", true, typeof( JsonElement ) )] [DataRow( " 4 == length( @.store.book ) ", true, typeof( JsonElement ) )] public void Should_MatchExpectedResult_WhenHasExtraSpaces( string filter, bool expected, Type sourceType ) { // arrange & act - var result = CompileAndExecute( filter, sourceType ); + var result = CompileAndExecuteFilter( filter, sourceType ); // assert Assert.AreEqual( expected, result ); } + [DataTestMethod] + [DataRow( "4 == length ( @.store.book )", typeof( JsonElement ) )] + [DataRow( "length (@.store.book) == 4", typeof( JsonElement ) )] + public void Should_Fail_WhenHasInvalidWhitespace( string filter, Type sourceType ) + { + Assert.ThrowsException( () => CompileAndExecuteFilter( filter, sourceType ) ); + } + [DataTestMethod] [DataRow( "unknown_literal", typeof( JsonElement ) )] [DataRow( "'unbalanced string\"", typeof( JsonElement ) )] [DataRow( " \t ", typeof( JsonElement ) )] [DataRow( "1 === 1", typeof( JsonElement ) )] - //[DataRow( "(1 == 1(", typeof( JsonElement ) )] + [DataRow( "(1 == 1(", typeof( JsonElement ) )] [DataRow( "(1 == 1)(", typeof( JsonElement ) )] [DataRow( "(1 == ", typeof( JsonElement ) )] [DataRow( "== 1", typeof( JsonElement ) )] @@ -182,12 +198,12 @@ private static (Expression, ParameterExpression) GetExpression( string filter, T { if ( sourceType == typeof( JsonElement ) ) { - var elementContext = new FilterContext( new ElementTypeDescriptor() ); - return (FilterParser.Parse( filter, elementContext ), elementContext.Root); + var elementContext = new FilterParserContext( new ElementTypeDescriptor() ); + return (FilterParser.Parse( filter, elementContext ), elementContext.RuntimeContext); } - var nodeContext = new FilterContext( new NodeTypeDescriptor() ); - return (FilterParser.Parse( filter, nodeContext ), nodeContext.Root); + var nodeContext = new FilterParserContext( new NodeTypeDescriptor() ); + return (FilterParser.Parse( filter, nodeContext ), nodeContext.RuntimeContext); } private static bool Execute( Expression expression, ParameterExpression param, Type sourceType ) @@ -195,41 +211,43 @@ private static bool Execute( Expression expression, ParameterExpression param, T if ( sourceType == typeof( JsonElement ) ) { var func = Expression - .Lambda>( expression, param ) + .Lambda, bool>>( expression, param ) .Compile(); - - return func( new JsonElement() ); + var descriptor = new ElementTypeDescriptor(); + return func( new FilterRuntimeContext( new JsonElement(), new JsonElement(), descriptor ) ); } if ( sourceType == typeof( JsonNode ) ) { var func = Expression - .Lambda>( expression, param ) + .Lambda, bool>>( expression, param ) .Compile(); - - return func( JsonNode.Parse( "{}" ) ); + var descriptor = new NodeTypeDescriptor(); + return func( new FilterRuntimeContext( new JsonObject(), new JsonObject(), descriptor ) ); } throw new NotImplementedException(); } - private static bool CompileAndExecute( string filter, Type sourceType ) + private static bool CompileAndExecuteFilter( string filter, Type sourceType ) { if ( sourceType == typeof( JsonElement ) ) { var source = GetDocument(); - var func = FilterParser.Compile( filter, new ElementTypeDescriptor() ); + var descriptor = new ElementTypeDescriptor(); + var func = FilterParser.Compile( filter, descriptor ); - return func( source.RootElement, source.RootElement ); + return func( new FilterRuntimeContext( source.RootElement, source.RootElement, descriptor ) ); } else { // arrange var source = GetDocument(); - var func = FilterParser.Compile( filter, new NodeTypeDescriptor() ); + var descriptor = new NodeTypeDescriptor(); + var func = FilterParser.Compile( filter, descriptor ); // act - return func( source, source ); + return func( new FilterRuntimeContext( source, source, descriptor ) ); } } diff --git a/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs index 7c6d345e..d0b905c1 100644 --- a/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs @@ -34,10 +34,10 @@ public class JsonPathQueryParserTests public void Should_TokenizeJsonPath( string jsonPath, string expected ) { // act - var pathSegment = JsonPathQueryParser.Parse( jsonPath ); + var compiledQuery = JsonPathQueryParser.Parse( jsonPath ); // arrange - var result = SegmentsToString( pathSegment ); + var result = SegmentsToString( compiledQuery.Segments ); // assert Assert.AreEqual( expected, result ); diff --git a/test/Hyperbee.Json.Tests/Query/JsonComparerComparandTests.cs b/test/Hyperbee.Json.Tests/Query/JsonComparerComparandTests.cs deleted file mode 100644 index b9eccdd6..00000000 --- a/test/Hyperbee.Json.Tests/Query/JsonComparerComparandTests.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Nodes; -using Hyperbee.Json.Descriptors.Node; -using Hyperbee.Json.Filters.Parser.Expressions; -using Hyperbee.Json.Tests.TestSupport; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Hyperbee.Json.Tests.Query; - -[TestClass] -public class JsonComparerComparandTests : JsonTestBase -{ - [DataTestMethod] - [DataRow( true, true, true )] - [DataRow( false, false, true )] - [DataRow( false, true, false )] - [DataRow( true, false, false )] - [DataRow( "hello", "hello", true )] - [DataRow( 10F, 10F, true )] - [DataRow( "hello", "world", false )] - [DataRow( 99F, 11F, false )] - [DataRow( "hello", 11F, false )] - [DataRow( false, 11F, false )] - [DataRow( true, 11F, false )] - public void ComparandWithEqualResults( object left, object right, bool areEqual ) - { - var accessor = new NodeValueAccessor(); - - var a = new ComparerExpressionFactory.Comparand( accessor, left ); - var b = new ComparerExpressionFactory.Comparand( accessor, right ); - - var result = a == b; - - Assert.AreEqual( areEqual, result ); - } - - [DataTestMethod] - [DataRow( true, true, true )] - [DataRow( false, false, true )] - [DataRow( false, true, false )] - [DataRow( true, false, true )] - [DataRow( "hello", "hello", true )] - [DataRow( 10F, 10F, true )] - [DataRow( 14F, 10F, true )] - [DataRow( 1F, 14F, false )] - - public void ComparandWithGreaterResults( object left, object right, bool areEqual ) - { - var accessor = new NodeValueAccessor(); - - var a = new ComparerExpressionFactory.Comparand( accessor, left ); - var b = new ComparerExpressionFactory.Comparand( accessor, right ); - - var result = a >= b; - - Assert.AreEqual( areEqual, result ); - } - - [DataTestMethod] - [DataRow( """{ "value": 1 }""", 99F, false )] - [DataRow( """{ "value": 99 }""", 99F, true )] - [DataRow( """{ "value": "hello" }""", "world", false )] - [DataRow( """{ "value": "hello" }""", "hello", true )] - [DataRow( """{ "value": { "child": 5 } }""", "hello", false )] - public void ComparandWithJsonObjectResults( string left, object right, bool areEqual ) - { - var accessor = new NodeValueAccessor(); - var node = new List { JsonNode.Parse( left )!["value"] }; - - var a = new ComparerExpressionFactory.Comparand( accessor, node ); - var b = new ComparerExpressionFactory.Comparand( accessor, right ); - - var result = a == b; - - Assert.AreEqual( areEqual, result ); - } - - - [DataTestMethod] - [DataRow( """[1,2,3]""", 2F, true )] - [DataRow( """["hello","hi","world" ]""", "hi", true )] - [DataRow( """[1,2,3]""", 99F, false )] - [DataRow( """["hello","world" ]""", "hi", false )] - public void ComparandWithLeftJsonArray( string left, object right, bool areEqual ) - { - var accessor = new NodeValueAccessor(); - - var a = new ComparerExpressionFactory.Comparand( accessor, JsonNode.Parse( left ) ); - var b = new ComparerExpressionFactory.Comparand( accessor, right ); - - var result = a == b; - - Assert.AreEqual( areEqual, result ); - } - - [DataTestMethod] - [DataRow( 2F, """[1,2,3]""", true )] - [DataRow( "hi", """["hello","hi","world" ]""", true )] - [DataRow( 99F, """[1,2,3]""", false )] - [DataRow( "hi", """["hello","world" ]""", false )] - public void ComparandWithRightJsonArray( object left, string right, bool areEqual ) - { - var accessor = new NodeValueAccessor(); - - var a = new ComparerExpressionFactory.Comparand( accessor, left ); - var b = new ComparerExpressionFactory.Comparand( accessor, JsonNode.Parse( right ) ); - - var result = a == b; - - Assert.AreEqual( areEqual, result ); - } - - [TestMethod] - public void ComparandWithEmpty() - { - var accessor = new NodeValueAccessor(); - - var a = new ComparerExpressionFactory.Comparand( accessor, new List() ); - var b = new ComparerExpressionFactory.Comparand( accessor, 1 ); - - Assert.IsFalse( a < b ); - Assert.IsFalse( a <= b ); - - Assert.IsFalse( a > b ); - Assert.IsFalse( a >= b ); - - Assert.IsFalse( a == b ); - Assert.IsTrue( a != b ); - } -} - diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathArrayTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathArrayTests.cs index b4bc9b87..7e87894b 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathArrayTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathArrayTests.cs @@ -1000,26 +1000,6 @@ public void ArraySliceWithStep1( string query, Type sourceType ) Assert.IsTrue( expected.SequenceEqual( matches ) ); } - [DataTestMethod] - [DataRow( "$[010:024:010]", typeof( JsonDocument ) )] - [DataRow( "$[010:024:010]", typeof( JsonNode ) )] - public void ArraySliceWithStepAndLeadingZeros( string query, Type sourceType ) - { - // consensus: [10, 20] - - const string json = "[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]"; - var source = GetDocumentFromSource( sourceType, json ); - - var matches = source.Select( query ); - var expected = new[] - { - source.FromJsonPathPointer("$[10]"), - source.FromJsonPathPointer("$[20]") - }; - - Assert.IsTrue( expected.SequenceEqual( matches ) ); - } - [DataTestMethod] [DataRow( "$[0:4:2]", typeof( JsonDocument ) )] [DataRow( "$[0:4:2]", typeof( JsonNode ) )] diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathBracketNotationTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathBracketNotationTests.cs index 1951d70a..eee78768 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathBracketNotationTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathBracketNotationTests.cs @@ -235,8 +235,7 @@ public void BracketNotationWithEmptyStringDoubleQuoted( string query, Type sourc [DataRow( "$[-2]", typeof( JsonNode ) )] public void BracketNotationWithNegativeNumberOnShortArray( string query, Type sourceType ) { - // rfc: ["one element] // (-2 => -2:1:1 => -1:1:1 [0]) - // consensus: [] + // rfc: ["one element] // out of bounds should return empty list const string json = """ [ @@ -247,8 +246,7 @@ public void BracketNotationWithNegativeNumberOnShortArray( string query, Type so var matches = source.Select( query ).ToList(); - Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "one element" ); + Assert.IsTrue( matches.Count == 0 ); } [DataTestMethod] @@ -401,20 +399,6 @@ public void BracketNotationWithNumberOnShortArray( string query, Type sourceType Assert.IsTrue( expected.SequenceEqual( matches ) ); } - /* - [DataTestMethod] - [DataRow("$[0]", typeof(JsonDocument))] - [DataRow("$[0]", typeof(JsonNode))] - public void BracketNotationWithNumberOnString(string query, Type sourceType) - { - // rfc: NOT_SUPPORTED // JsonDocument can't parse - // consensus: [] - - const string json = "Hello World"; - var source = GetDocumentFromSource(sourceType, json); - } - */ - [DataTestMethod] [DataRow( "$[':']", typeof( JsonDocument ) )] [DataRow( "$[':']", typeof( JsonNode ) )] @@ -526,20 +510,6 @@ public void BracketNotationWithQuotedDotWildcard( string query, Type sourceType Assert.IsTrue( expected.SequenceEqual( matches ) ); } - /* - [DataTestMethod] - [DataRow("$['\"']", typeof(JsonDocument))] - [DataRow("$['\"']", typeof(JsonNode))] - public void BracketNotationWithQuotedDoubleQuoteLiteral(string query, Type sourceType) - { - // rfc: NOT_SUPPORTED // JsonDocument can't parse - // consensus: ["value"] - - const string json = "{ \"\"\": \"value\", \"another\": \"entry\"}"; - var source = GetDocumentFromSource(sourceType, json); - } - */ - [DataTestMethod] [DataRow( @"$['\\']", typeof( JsonDocument ) )] [DataRow( @"$['\\']", typeof( JsonNode ) )] @@ -558,7 +528,7 @@ public void BracketNotationWithQuotedEscapedBackslash( string query, Type source var matches = source.Select( query ); var expected = new[] { - source.FromJsonPathPointer(@"$['\']") + source.FromJsonPathPointer(@"$['\\']") }; Assert.IsTrue( expected.SequenceEqual( matches ) ); @@ -633,8 +603,8 @@ public void BracketNotationWithQuotedRootLiteral( string query, Type sourceType } [DataTestMethod] - [DataRow( """$[':@.\"$,*\'\\']""", typeof( JsonDocument ) )] - [DataRow( """$[':@.\"$,*\'\\']""", typeof( JsonNode ) )] + [DataRow( """$[':@."$,*\'\\']""", typeof( JsonDocument ) )] + [DataRow( """$[':@."$,*\'\\']""", typeof( JsonNode ) )] public void BracketNotationWithQuotedSpecialCharactersCombined( string query, Type sourceType ) { // rfc: 42 diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs index f5d9de69..0b01f3e1 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs @@ -84,32 +84,6 @@ public void FilterExpressionWithLessThan( string query, Type sourceType ) Assert.IsTrue( expected.SequenceEqual( matches ) ); } - [DataTestMethod] - [DataRow( "$[?($..key)==2]", typeof( JsonDocument ) )] - [DataRow( "$[?($..key)==2]", typeof( JsonNode ) )] - public void FilterExpressionWithContainsArray( string query, Type sourceType ) - { - const string json = - """ - { - "values": [ - { "key": 1, "value": 10 }, - { "key": 2, "value": 20 } - ] - } - """; - - var source = GetDocumentFromSource( sourceType, json ); - - var matches = source.Select( query ); - var expected = new[] - { - source.FromJsonPathPointer( "$['values']" ) - }; - - Assert.IsTrue( expected.SequenceEqual( matches ) ); - } - [DataTestMethod] [DataRow( "$..*[?(@.id>2)]", typeof( JsonDocument ) )] [DataRow( "$..*[?(@.id>2)]", typeof( JsonNode ) )] @@ -206,7 +180,10 @@ public void FilterExpressionWithDifferentGroupedOperators( string query, Type so var source = GetDocumentFromSource( sourceType, json ); var matches = source.Select( query ); - var expected = new[] { source.FromJsonPathPointer( "$[1]" ), source.FromJsonPathPointer( "$[2]" ), source.FromJsonPathPointer( "$[4]" ) }; + var expected = new[] { + source.FromJsonPathPointer( "$[1]" ), + source.FromJsonPathPointer( "$[2]" ), + source.FromJsonPathPointer( "$[4]" ) }; Assert.IsTrue( expected.SequenceEqual( matches ) ); } @@ -335,10 +312,10 @@ public void FilterExpressionWithEqualsArray( string query, Type sourceType ) [DataTestMethod] [DataRow( "$[?(@[0:1]==[1])]", typeof( JsonDocument ) )] [DataRow( "$[?(@[0:1]==[1])]", typeof( JsonNode ) )] + [ExpectedException( typeof( NotSupportedException ) )] public void FilterExpressionWithEqualsArrayForSliceWithRange1( string query, Type sourceType ) { // consensus: NOT_SUPPORTED - // deviation: [] ??? should return [1]? var json = """ @@ -352,24 +329,16 @@ public void FilterExpressionWithEqualsArrayForSliceWithRange1( string query, Typ """; var source = GetDocumentFromSource( sourceType, json ); - - var matches = source.Select( query ); - var expected = Enumerable.Empty(); - // var expected = new[] - // { - // source.FromJsonPathPointer( "$[1]" ) - // }; - - Assert.IsTrue( expected.SequenceEqual( matches ) ); + _ = source.Select( query ).ToArray(); } [DataTestMethod] [DataRow( "$[?(@.*==[1,2])]", typeof( JsonDocument ) )] [DataRow( "$[?(@.*==[1,2])]", typeof( JsonNode ) )] + [ExpectedException( typeof( NotSupportedException ) )] public void FilterExpressionWithEqualsArrayForDotNotationWithStart( string query, Type sourceType ) { // consensus: NOT_SUPPORTED - // deviation: [] var json = """ @@ -387,10 +356,7 @@ public void FilterExpressionWithEqualsArrayForDotNotationWithStart( string query var source = GetDocumentFromSource( sourceType, json ); - var matches = source.Select( query ); - var expected = Enumerable.Empty(); - - Assert.IsTrue( expected.SequenceEqual( matches ) ); + _ = source.Select( query ).ToArray(); } [DataTestMethod] @@ -489,44 +455,6 @@ public void FilterExpressionWithEqualsArrayWithSingleQuotes( string query, Type _ = source.Select( query ).ToArray(); } - [DataTestMethod] - [DataRow( "$[?(@.a[?(@.price>10)])]", typeof( JsonDocument ) )] - [DataRow( "$[?(@.a[?(@.price>10)])]", typeof( JsonNode ) )] - [ExpectedException( typeof( NotSupportedException ) )] - public void FilterExpressionWithSubFilter( string query, Type sourceType ) - { - // consensus: NOT_SUPPORTED - - var json = - """ - [ - { - "a": [{"price": 1}, {"price": 3}] - }, - { - "a": [{"price": 11}] - }, - { - "a": [{"price": 8}, {"price": 12}, {"price": 3}] - }, - { - "a": [] - } - ] - """; - - var source = GetDocumentFromSource( sourceType, json ); - - var matches = source.Select( query ); - var expected = new[] - { - source.FromJsonPathPointer( "$[1]" ), - source.FromJsonPathPointer( "$[2]" ) - }; - - Assert.IsTrue( expected.SequenceEqual( matches ) ); - } - [DataTestMethod] [DataRow( "$[?((@.key<44)==false)]", typeof( JsonDocument ) )] [DataRow( "$[?((@.key<44)==false)]", typeof( JsonNode ) )] @@ -661,7 +589,7 @@ public void FilterExpressionWithEqualsNull( string query, Type sourceType ) var source = GetDocumentFromSource( sourceType, json ); - var matches = source.Select( query ); + var matches = source.Select( query ).ToArray(); var expected = new[] { source.FromJsonPathPointer( "$[3]" ) }; Assert.IsTrue( expected.SequenceEqual( matches ) ); diff --git a/test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs b/test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs new file mode 100644 index 00000000..9bfe5c8d --- /dev/null +++ b/test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using System.Text.Json.Nodes; +using Hyperbee.Json.Descriptors.Node; +using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; +using Hyperbee.Json.Tests.TestSupport; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Hyperbee.Json.Tests.Query; + +[TestClass] +public class NodeTypeComparerTests : JsonTestBase +{ + [DataTestMethod] + [DataRow( true, true, true )] + [DataRow( false, false, true )] + [DataRow( false, true, false )] + [DataRow( true, false, false )] + [DataRow( "hello", "hello", true )] + [DataRow( 10F, 10F, true )] + [DataRow( "hello", "world", false )] + [DataRow( 99F, 11F, false )] + [DataRow( "hello", 11F, false )] + [DataRow( false, 11F, false )] + [DataRow( true, 11F, false )] + public void NodeTypeComparer_ShouldCompare_WithEqualResults( object left, object right, bool areEqual ) + { + // Arrange + var comparer = GetComparer(); + var a = GetNodeType( left ); + var b = GetNodeType( right ); + + // Act + var result = comparer.Compare( a, b, Operator.Equals ) == 0; + + // Assert + Assert.AreEqual( areEqual, result ); + } + + [DataTestMethod] + [DataRow( true, true, true )] + [DataRow( false, false, true )] + [DataRow( false, true, false )] + [DataRow( true, false, true )] + [DataRow( "hello", "hello", true )] + [DataRow( 10F, 10F, true )] + [DataRow( 14F, 10F, true )] + [DataRow( 1F, 14F, false )] + public void NodeTypeComparer_ShouldCompare_WithGreaterResults( object left, object right, bool areEqual ) + { + // Arrange + var comparer = GetComparer(); + var a = GetNodeType( left ); + var b = GetNodeType( right ); + + // Act + var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) >= 0; + + // Assert + Assert.AreEqual( areEqual, result ); + } + + [DataTestMethod] + [DataRow( """{ "value": 1 }""", 99F, false )] + [DataRow( """{ "value": 99 }""", 99F, true )] + [DataRow( """{ "value": "hello" }""", "world", false )] + [DataRow( """{ "value": "hello" }""", "hello", true )] + [DataRow( """{ "value": { "child": 5 } }""", "hello", false )] + public void NodeTypeComparer_ShouldCompare_WithJsonObjectResults( string left, object right, bool areEqual ) + { + // Arrange + var comparer = GetComparer(); + var a = GetNodeType( new List { JsonNode.Parse( left )!["value"] } ); + var b = GetNodeType( right ); + + // Act + var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) == 0; + + // Assert + Assert.AreEqual( areEqual, result ); + } + + [DataTestMethod] + [DataRow( """[1,2,3]""", 2F, true )] + [DataRow( """["hello","hi","world" ]""", "hi", true )] + [DataRow( """[1,2,3]""", 99F, false )] + [DataRow( """["hello","world" ]""", "hi", false )] + public void NodeTypeComparer_ShouldCompare_WithLeftJsonArray( string left, object right, bool areEqual ) + { + // Arrange + var comparer = GetComparer(); + var a = GetNodeType( JsonNode.Parse( left )!.AsArray() ); + var b = GetNodeType( right ); + + // Act + var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) == 0; + + // Assert + Assert.AreEqual( areEqual, result ); + } + + [DataTestMethod] + [DataRow( 2F, """[1,2,3]""", true )] + [DataRow( "hi", """["hello","hi","world" ]""", true )] + [DataRow( 99F, """[1,2,3]""", false )] + [DataRow( "hi", """["hello","world" ]""", false )] + public void NodeTypeComparer_ShouldCompare_WithRightJsonArray( object left, string right, bool areEqual ) + { + // Arrange + var comparer = GetComparer(); + var a = GetNodeType( left ); + var b = GetNodeType( JsonNode.Parse( right )!.AsArray() ); + + // Act + var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) == 0; + + // Assert + Assert.AreEqual( areEqual, result ); + } + + [TestMethod] + public void NodeTypeComparer_ShouldCompare_WithEmpty() + { + var comparer = GetComparer(); + var a = new NodesType( [], true ); + var b = new ValueType( 1F ); + + Assert.IsFalse( comparer.Compare( a, b, Operator.LessThan ) < 0 ); + Assert.IsFalse( comparer.Compare( a, b, Operator.LessThanOrEqual ) <= 0 ); + + Assert.IsFalse( comparer.Compare( a, b, Operator.GreaterThan ) > 0 ); + Assert.IsFalse( comparer.Compare( a, b, Operator.GreaterThanOrEqual ) >= 0 ); + + Assert.IsFalse( comparer.Compare( a, b, Operator.Equals ) == 0 ); + Assert.IsTrue( comparer.Compare( a, b, Operator.NotEquals ) != 0 ); + } + + private static NodeTypeComparer GetComparer() => new( new NodeValueAccessor() ); + + private static INodeType GetNodeType( object item ) => + item switch + { + string itemString => new ValueType( itemString ), + float itemFloat => new ValueType( itemFloat ), + bool itemBool => new ValueType( itemBool ), + IEnumerable nodes => new NodesType( nodes, true ), + _ => Constants.Nothing + }; +} From b2b2aa94b865ae579d28cf30a856caa006a92e11 Mon Sep 17 00:00:00 2001 From: MattEdwardsWaggleBee Date: Fri, 12 Jul 2024 21:32:02 +0000 Subject: [PATCH 04/18] Previous version was 'v1.2.1'. Version now 'v1.3.0'. --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index d5bdfe6f..983787c5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,8 +2,8 @@ 1 - 2 - 1 + 3 + 0 From 75b1cfc5cb69df187cab838931d4a9e6a42149f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 07:56:21 -0700 Subject: [PATCH 05/18] [FEATURE]: Improve filter extension method argument handling (#38) * Added argument helpers * Refactored value system * Modified type system to support int * Modified type system to allow covariant returns from extension functions * Replaced usages of Expression Invokes with Expression Call, and eliminate usages of MakeGenericMethod --------- Co-authored-by: Brenton Farmer --- README.md | 15 +-- .../Element/ElementTypeDescriptor.cs | 10 +- .../Element/ElementValueAccessor.cs | 18 ++- .../Element/Functions/CountElementFunction.cs | 24 ++-- .../Functions/LengthElementFunction.cs | 42 +++---- .../Element/Functions/MatchElementFunction.cs | 32 ++--- .../Functions/SearchElementFunction.cs | 31 ++--- .../Element/Functions/ValueElementFunction.cs | 42 +++---- .../Element/ValueTypeExtensions.cs | 84 +++++++++++++ .../Descriptors/FunctionRegistry.cs | 6 +- .../Descriptors/ITypeDescriptor.cs | 5 +- .../Node/Functions/CountNodeFunction.cs | 16 +-- .../Node/Functions/LengthNodeFunction.cs | 42 +++---- .../Node/Functions/MatchNodeFunction.cs | 33 ++---- .../Node/Functions/SearchNodeFunction.cs | 31 ++--- .../Node/Functions/ValueNodeFunction.cs | 42 +++---- .../Descriptors/Node/NodeTypeDescriptor.cs | 10 +- .../Descriptors/Node/NodeValueAccessor.cs | 22 +++- .../Descriptors/Node/ValueTypeExtensions.cs | 89 ++++++++++++++ .../Extensions/EnumerableExtensions.cs | 17 +++ .../Extensions/JsonPathPointerExtensions.cs | 6 +- src/Hyperbee.Json/Filters/IRegexp.cs | 16 +-- .../Parser/Expressions/CompareExpression.cs | 61 ++++++++++ .../Expressions/FilterTruthyExpression.cs | 33 ++++++ .../Expressions/FunctionExpressionFactory.cs | 4 +- .../Expressions/JsonExpressionFactory.cs | 2 +- .../Expressions/LiteralExpressionFactory.cs | 13 ++- .../Expressions/SelectExpressionFactory.cs | 22 ++-- .../Filters/Parser/FilterExtensionFunction.cs | 110 ++++++++++-------- .../Filters/Parser/FilterParser.cs | 71 +++++++++-- .../Filters/Parser/FilterTruthyExpression.cs | 41 ------- .../NodeTypeComparerBinderExpression.cs | 25 ---- .../Filters/Parser/NodeTypeExpression.cs | 64 ---------- .../ValueTypeComparer.cs} | 82 ++++++++----- src/Hyperbee.Json/Filters/Values/Constants.cs | 10 -- src/Hyperbee.Json/Filters/Values/INodeType.cs | 8 -- .../Filters/Values/IValueType.cs | 21 ++++ .../Values/{NodesType.cs => NodeList.cs} | 7 +- src/Hyperbee.Json/Filters/Values/Nothing.cs | 7 -- src/Hyperbee.Json/Filters/Values/Null.cs | 7 -- .../Filters/Values/ScalarValue.cs | 29 +++++ .../Values/{NodeTypeKind.cs => ValueKind.cs} | 5 +- src/Hyperbee.Json/Filters/Values/ValueType.cs | 10 -- src/Hyperbee.Json/JsonPathQueryParser.cs | 10 +- src/Hyperbee.Json/JsonPathSegment.cs | 1 - .../Tests/cts-filter-tests.cs | 4 +- test/Hyperbee.Json.Cts/generate_tests.ps1 | 2 +- .../Parsers/FilterExtensionFunctionTests.cs | 11 +- .../Parsers/FilterParserTests.cs | 4 +- .../ValueTypeComparerTests.cs} | 18 +-- 50 files changed, 736 insertions(+), 579 deletions(-) create mode 100644 src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs create mode 100644 src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs create mode 100644 src/Hyperbee.Json/Extensions/EnumerableExtensions.cs create mode 100644 src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs create mode 100644 src/Hyperbee.Json/Filters/Parser/Expressions/FilterTruthyExpression.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs rename src/Hyperbee.Json/Filters/{Values/NodeTypeComparer.cs => Parser/ValueTypeComparer.cs} (68%) delete mode 100644 src/Hyperbee.Json/Filters/Values/Constants.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/INodeType.cs create mode 100644 src/Hyperbee.Json/Filters/Values/IValueType.cs rename src/Hyperbee.Json/Filters/Values/{NodesType.cs => NodeList.cs} (56%) delete mode 100644 src/Hyperbee.Json/Filters/Values/Nothing.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/Null.cs create mode 100644 src/Hyperbee.Json/Filters/Values/ScalarValue.cs rename src/Hyperbee.Json/Filters/Values/{NodeTypeKind.cs => ValueKind.cs} (64%) delete mode 100644 src/Hyperbee.Json/Filters/Values/ValueType.cs rename test/Hyperbee.Json.Tests/{Query/NodeTypeComparerTests.cs => Parsers/ValueTypeComparerTests.cs} (89%) diff --git a/README.md b/README.md index f40a3e5e..6bb250c8 100644 --- a/README.md +++ b/README.md @@ -193,18 +193,15 @@ You can also extend the supported function set by registering your own functions **Step 1:** Create a custom function that returns the path of a `JsonNode`. ```csharp -public class PathNodeFunction() : FilterExtensionFunction( PathMethodInfo, FilterExtensionInfo.MustCompare ) +private class PathNodeFunction() : FilterExtensionFunction( PathMethodInfo, FilterExtensionInfo.MustCompare ) { public const string Name = "path"; private static readonly MethodInfo PathMethodInfo = GetMethod( nameof( Path ) ); - private static INodeType Path( INodeType arg ) + private static ScalarValue Path( IValueType argument ) { - if ( arg is not NodesType nodes ) - return Constants.Null; - - var node = nodes.FirstOrDefault(); - return new ValueType( node?.GetPath() ); + return argument.TryGetNode( out var node ) ? node?.GetPath() : null; + } } ``` @@ -231,7 +228,6 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. - Comprehensive feature set. - Deferred execution queries with `IEnumerable`. - Strong community support. - - .NET Foundation Project. - **Cons:** - No support for `JsonElement`. @@ -253,7 +249,6 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. - Comprehensive feature set. - Documentation and examples. - Strong community support. - - .NET Foundation Project. - **Cons:** - No support for `JsonElement`, or `JsonNode`. @@ -344,7 +339,7 @@ Here is a performance comparison of various queries on the standard book store d ``` -## Additioal Documentation +## Additional Documentation Additional documentation can be found in the project's `/docs` folder. diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs index 09687c88..2d132809 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs @@ -1,15 +1,15 @@ using System.Text.Json; using Hyperbee.Json.Descriptors.Element.Functions; using Hyperbee.Json.Filters; -using Hyperbee.Json.Filters.Values; +using Hyperbee.Json.Filters.Parser; namespace Hyperbee.Json.Descriptors.Element; public class ElementTypeDescriptor : ITypeDescriptor { - private FilterEvaluator _evaluator; private ElementValueAccessor _accessor; - private NodeTypeComparer _comparer; + private FilterEvaluator _evaluator; + private ValueTypeComparer _comparer; public FunctionRegistry Functions { get; } = new(); @@ -19,8 +19,8 @@ public class ElementTypeDescriptor : ITypeDescriptor public IFilterEvaluator FilterEvaluator => _evaluator ??= new FilterEvaluator( this ); - public INodeTypeComparer Comparer => - _comparer ??= new NodeTypeComparer( Accessor ); + public IValueTypeComparer Comparer => + _comparer ??= new ValueTypeComparer( Accessor ); public bool CanUsePointer => true; diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs index 3dac8a6c..8028a058 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs @@ -162,8 +162,21 @@ public bool TryGetValueFromNode( JsonElement element, out object value ) value = element.GetString(); break; case JsonValueKind.Number: - value = element.GetSingle(); - break; + if ( element.TryGetInt32( out int intValue ) ) + { + value = intValue; + break; + } + + if ( element.TryGetSingle( out float floatValue ) ) + { + value = floatValue; + break; + } + + value = false; + return false; + case JsonValueKind.True: value = true; break; @@ -181,6 +194,7 @@ public bool TryGetValueFromNode( JsonElement element, out object value ) return true; } + public bool TryGetFromPointer( in JsonElement element, JsonPathSegment segment, out JsonElement childValue ) { return element.TryGetFromJsonPathPointer( segment, out childValue ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs index 30c22efe..349f983c 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs @@ -5,21 +5,21 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class CountElementFunction() : FilterExtensionFunction( CountMethodInfo, FilterExtensionInfo.MustCompare ) +public class CountElementFunction() : FilterExtensionFunction( CountMethod, FilterExtensionInfo.MustCompare ) { public const string Name = "count"; - private static readonly MethodInfo CountMethodInfo = GetMethod( nameof( Count ) ); + private static readonly MethodInfo CountMethod = GetMethod( nameof( Count ) ); - public static INodeType Count( INodeType input ) + public static ScalarValue Count( IValueType argument ) { - switch ( input ) - { - case NodesType nodes: - if ( nodes.IsNormalized && !nodes.Any() ) - return new ValueType( 1F ); - return new ValueType( nodes.Count() ); - default: - return new ValueType( 1F ); - } + if ( argument.ValueKind != ValueKind.NodeList ) + throw new NotSupportedException( $"Function `{Name}` must be a node list." ); + + var nodes = (NodeList) argument; + + if ( nodes.IsNormalized && !nodes.Any() ) + return 1; + + return nodes.Count(); } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs index fc2372ce..ded983bc 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs @@ -1,42 +1,34 @@ using System.Reflection; using System.Text.Json; +using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Element.Functions; -public class LengthElementFunction() : FilterExtensionFunction( LengthMethodInfo, FilterExtensionInfo.MustCompare | FilterExtensionInfo.ExpectNormalized ) +public class LengthElementFunction() : FilterExtensionFunction( LengthMethod, FilterExtensionInfo.MustCompare | FilterExtensionInfo.ExpectNormalized ) { public const string Name = "length"; - private static readonly MethodInfo LengthMethodInfo = GetMethod( nameof( Length ) ); + private static readonly MethodInfo LengthMethod = GetMethod( nameof( Length ) ); - public static INodeType Length( INodeType input ) + public static IValueType Length( IValueType argument ) { - return input switch + if ( argument.TryGetValue( out var stringValue ) ) { - NodesType nodes => LengthImpl( nodes.FirstOrDefault() ), - ValueType valueString => new ValueType( valueString.Value.Length ), - Null or Nothing => input, - _ => Constants.Nothing - }; - } + return new ScalarValue( stringValue.Length ); + } - public static INodeType LengthImpl( object value ) - { - return value switch + if ( argument.TryGetNode( out var node ) ) { - string str => new ValueType( str.Length ), - Array array => new ValueType( array.Length ), - System.Collections.ICollection collection => new ValueType( collection.Count ), - System.Collections.IEnumerable enumerable => new ValueType( enumerable.Cast().Count() ), - JsonElement node => node.ValueKind switch + return node.ValueKind switch { - JsonValueKind.String => new ValueType( node.GetString()?.Length ?? 0 ), - JsonValueKind.Array => new ValueType( node.EnumerateArray().Count() ), - JsonValueKind.Object => new ValueType( node.EnumerateObject().Count() ), - _ => Constants.Null - }, - _ => Constants.Null - }; + JsonValueKind.String => new ScalarValue( node.GetString()?.Length ?? 0 ), + JsonValueKind.Array => new ScalarValue( node.GetArrayLength() ), + JsonValueKind.Object => new ScalarValue( node.EnumerateObject().Count() ), + _ => Scalar.Nothing + }; + } + + return Scalar.Nothing; } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs index 652c9c5f..e1148332 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Text.Json; using System.Text.RegularExpressions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; @@ -7,33 +6,20 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class MatchElementFunction() : FilterExtensionFunction( MatchMethodInfo, FilterExtensionInfo.MustNotCompare ) +public class MatchElementFunction() : FilterExtensionFunction( MatchMethod, FilterExtensionInfo.MustNotCompare ) { public const string Name = "match"; - private static readonly MethodInfo MatchMethodInfo = GetMethod( nameof( Match ) ); + private static readonly MethodInfo MatchMethod = GetMethod( nameof( Match ) ); - public static INodeType Match( INodeType input, INodeType regex ) + public static ScalarValue Match( IValueType input, IValueType pattern ) { - return input switch - { - NodesType nodes when regex is ValueType stringValue => - MatchImpl( nodes, stringValue.Value ), - NodesType nodes when regex is NodesType stringValue => - MatchImpl( nodes, stringValue.Value.FirstOrDefault().GetString() ), - _ => Constants.False - }; - } - - public static INodeType MatchImpl( NodesType nodes, string regex ) - { - var value = nodes.FirstOrDefault(); - - if ( value.ValueKind != JsonValueKind.String ) - return Constants.False; + if ( !input.TryGetValue( out var value ) || value == null ) + return false; - var stringValue = value.GetString() ?? string.Empty; + if ( !pattern.TryGetValue( out var patternValue ) || patternValue == null ) + return false; - var regexPattern = new Regex( $"^{IRegexp.ConvertToIRegexp( regex )}$" ); - return new ValueType( regexPattern.IsMatch( stringValue ) ); + var regex = new Regex( $"^{IRegexp.ConvertToIRegexp( patternValue )}$" ); + return regex.IsMatch( value ); } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs index 28d4a40b..1b812cfd 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs @@ -7,33 +7,20 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class SearchElementFunction() : FilterExtensionFunction( SearchMethodInfo, FilterExtensionInfo.MustNotCompare ) +public class SearchElementFunction() : FilterExtensionFunction( SearchMethod, FilterExtensionInfo.MustNotCompare ) { public const string Name = "search"; - private static readonly MethodInfo SearchMethodInfo = GetMethod( nameof( Search ) ); + private static readonly MethodInfo SearchMethod = GetMethod( nameof( Search ) ); - public static INodeType Search( INodeType input, INodeType regex ) + public static ScalarValue Search( IValueType input, IValueType pattern ) { - return input switch - { - NodesType nodes when regex is ValueType stringValue => - SearchImpl( nodes, stringValue.Value ), - NodesType nodes when regex is NodesType stringValue => - SearchImpl( nodes, stringValue.Value.FirstOrDefault().GetString() ), - _ => Constants.False - }; - } - - public static INodeType SearchImpl( NodesType nodes, string regex ) - { - var value = nodes.FirstOrDefault(); - - if ( value.ValueKind != JsonValueKind.String ) - return Constants.False; + if ( !input.TryGetValue( out var value ) || value == null ) + return false; - var stringValue = value.GetString() ?? string.Empty; + if ( !pattern.TryGetValue( out var patternValue ) || patternValue == null ) + return false; - var regexPattern = new Regex( IRegexp.ConvertToIRegexp( regex ) ); - return new ValueType( regexPattern.IsMatch( stringValue ) ); + var regex = new Regex( IRegexp.ConvertToIRegexp( patternValue ) ); + return regex.IsMatch( value ); } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs index 8c04d5c4..f41e7fc3 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs @@ -1,45 +1,33 @@ using System.Reflection; using System.Text.Json; +using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Element.Functions; -public class ValueElementFunction() : FilterExtensionFunction( ValueMethodInfo, FilterExtensionInfo.MustCompare ) +public class ValueElementFunction() : FilterExtensionFunction( ValueMethod, FilterExtensionInfo.MustCompare ) { public const string Name = "value"; - private static readonly MethodInfo ValueMethodInfo = GetMethod( nameof( Value ) ); + private static readonly MethodInfo ValueMethod = GetMethod( nameof( Value ) ); - public static INodeType Value( INodeType arg ) + public static IValueType Value( IValueType argument ) { - if ( arg.Kind != NodeTypeKind.NodeList ) - throw new NotSupportedException( $"Function {Name} does not support kind {arg.Kind}" ); + if ( argument is not NodeList nodes ) + throw new NotSupportedException( $"Function `{Name}` does not support kind {argument.ValueKind}" ); - var nodeArray = ((NodesType) arg).ToArray(); - - if ( nodeArray.Length != 1 ) - return Constants.Nothing; - - var node = nodeArray.FirstOrDefault(); + var node = nodes.OneOrDefault(); return node.ValueKind switch { - JsonValueKind.Number => new ValueType( node.GetSingle() ), - JsonValueKind.String => new ValueType( node.GetString() ), - JsonValueKind.Object or JsonValueKind.Array => new ValueType( IsNotEmpty( node ) ), - JsonValueKind.True => Constants.True, - JsonValueKind.False or JsonValueKind.Null or JsonValueKind.Undefined => Constants.False, - _ => Constants.False + JsonValueKind.Number when node.TryGetInt32( out var intValue ) => Scalar.Value( intValue ), + JsonValueKind.Number => Scalar.Value( node.GetSingle() ), + JsonValueKind.String => Scalar.Value( node.GetString() ), + JsonValueKind.Object => Scalar.Value( node.EnumerateObject().Any() ), + JsonValueKind.Array => Scalar.Value( node.GetArrayLength() != 0 ), + JsonValueKind.True => Scalar.True, + JsonValueKind.False => Scalar.False, + _ => Scalar.Nothing }; - - static bool IsNotEmpty( JsonElement node ) - { - return node.ValueKind switch - { - JsonValueKind.Array => node.EnumerateArray().Any(), - JsonValueKind.Object => node.EnumerateObject().Any(), - _ => false - }; - } } } diff --git a/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs b/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs new file mode 100644 index 00000000..718a93b1 --- /dev/null +++ b/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs @@ -0,0 +1,84 @@ +using System.Text.Json; +using Hyperbee.Json.Extensions; +using Hyperbee.Json.Filters.Values; + +namespace Hyperbee.Json.Descriptors.Element; + +public static class ValueTypeExtensions +{ + public static bool TryGetValue( this IValueType input, out T value ) where T : IConvertible + { + switch ( input ) + { + case ScalarValue valueType: + value = valueType.Value; + return true; + + case NodeList nodesType: + var element = nodesType.FirstOrDefault(); + if ( element.TryConvertTo( out value ) ) + return true; + break; + } + + value = default; + return false; + } + + public static bool TryGetNode( this IValueType input, out T value ) + { + if ( input is NodeList nodes ) + { + value = nodes.OneOrDefault(); + return true; + } + + value = default; + return false; + } + + private static bool TryConvertTo( this JsonElement element, out T value ) where T : IConvertible + { + value = default; + try + { + if ( typeof( T ) == typeof( string ) && element.ValueKind == JsonValueKind.String ) + { + value = (T) (IConvertible) element.GetString(); + return true; + } + + if ( typeof( T ) == typeof( int ) && element.ValueKind == JsonValueKind.Number && element.TryGetInt32( out var intValue ) ) + { + value = (T) (IConvertible) intValue; + return true; + } + + if ( typeof( T ) == typeof( float ) && element.ValueKind == JsonValueKind.Number && element.TryGetSingle( out var floatValue ) ) + { + value = (T) (IConvertible) floatValue; + return true; + } + + if ( typeof( T ) == typeof( bool ) ) + { + if ( element.ValueKind == JsonValueKind.True ) + { + value = (T) (IConvertible) true; + return true; + } + if ( element.ValueKind == JsonValueKind.False ) + { + value = (T) (IConvertible) false; + return true; + } + } + } + catch + { + // Ignore conversion errors + } + + return false; + } +} diff --git a/src/Hyperbee.Json/Descriptors/FunctionRegistry.cs b/src/Hyperbee.Json/Descriptors/FunctionRegistry.cs index 2d342441..df8fcfc4 100644 --- a/src/Hyperbee.Json/Descriptors/FunctionRegistry.cs +++ b/src/Hyperbee.Json/Descriptors/FunctionRegistry.cs @@ -4,7 +4,7 @@ namespace Hyperbee.Json.Descriptors; public sealed class FunctionRegistry { - private Dictionary Functions { get; } = []; + private Dictionary Functions { get; } = []; public void Register( string name, Func factory ) where TFunction : FilterExtensionFunction @@ -12,8 +12,8 @@ public void Register( string name, Func factory ) Functions[name] = () => factory(); } - internal bool TryGetCreator( string name, out FunctionCreator functionCreator ) + internal bool TryGetActivator( string name, out FunctionActivator functionActivator ) { - return Functions.TryGetValue( name, out functionCreator ); + return Functions.TryGetValue( name, out functionActivator ); } } diff --git a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs index 6ce777d2..7f21175e 100644 --- a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs @@ -1,10 +1,9 @@ using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; -using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors; -public delegate FilterExtensionFunction FunctionCreator(); +public delegate FilterExtensionFunction FunctionActivator(); public interface ITypeDescriptor { @@ -16,7 +15,7 @@ public interface ITypeDescriptor : ITypeDescriptor public IValueAccessor Accessor { get; } public IFilterEvaluator FilterEvaluator { get; } - public INodeTypeComparer Comparer { get; } + public IValueTypeComparer Comparer { get; } bool CanUsePointer { get; } public void Deconstruct( out IValueAccessor valueAccessor, out IFilterEvaluator filterEvaluator ) diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs index 38480824..5bd3f212 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs @@ -5,21 +5,21 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class CountNodeFunction() : FilterExtensionFunction( CountMethodInfo, FilterExtensionInfo.MustCompare ) +public class CountNodeFunction() : FilterExtensionFunction( CountMethod, FilterExtensionInfo.MustCompare ) { public const string Name = "count"; - private static readonly MethodInfo CountMethodInfo = GetMethod( nameof( Count ) ); + private static readonly MethodInfo CountMethod = GetMethod( nameof( Count ) ); - public static INodeType Count( INodeType arg ) + public static ScalarValue Count( IValueType argument ) { - if ( arg.Kind != NodeTypeKind.NodeList ) - throw new NotSupportedException( $"Function {Name} must be a node list." ); + if ( argument.ValueKind != ValueKind.NodeList ) + throw new NotSupportedException( $"Function `{Name}` must be a node list." ); - var nodes = (NodesType) arg; + var nodes = (NodeList) argument; if ( nodes.IsNormalized && !nodes.Any() ) - return new ValueType( 1 ); + return 1; - return new ValueType( nodes.Count() ); + return nodes.Count(); } } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs index 0f5792d7..f924a23c 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs @@ -6,39 +6,27 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class LengthNodeFunction() : FilterExtensionFunction( LengthMethodInfo, FilterExtensionInfo.MustCompare | FilterExtensionInfo.ExpectNormalized ) +public class LengthNodeFunction() : FilterExtensionFunction( LengthMethod, FilterExtensionInfo.MustCompare | FilterExtensionInfo.ExpectNormalized ) { public const string Name = "length"; - private static readonly MethodInfo LengthMethodInfo = GetMethod( nameof( Length ) ); + private static readonly MethodInfo LengthMethod = GetMethod( nameof( Length ) ); - public static INodeType Length( INodeType input ) + public static IValueType Length( IValueType input ) { - return input switch - { - NodesType nodes => LengthImpl( nodes.FirstOrDefault() ), - ValueType valueString => new ValueType( valueString.Value.Length ), - Null or Nothing => input, - _ => Constants.Nothing - }; - } + if ( input.TryGetValue( out var stringValue ) ) + return Scalar.Value( stringValue.Length ); - public static INodeType LengthImpl( object value ) - { - return value switch + if ( input.TryGetNode( out var node ) ) { - string str => new ValueType( str.Length ), - Array array => new ValueType( array.Length ), - System.Collections.ICollection collection => new ValueType( collection.Count ), - System.Collections.IEnumerable enumerable => new ValueType( enumerable.Cast().Count() ), - JsonNode node => node.GetValueKind() switch + return node?.GetValueKind() switch { - JsonValueKind.String => new ValueType( node.GetValue()?.Length ?? 0 ), - JsonValueKind.Array => new ValueType( node.AsArray().Count ), - JsonValueKind.Object => new ValueType( node.AsObject().Count ), - _ => Constants.Nothing - }, - _ => Constants.Nothing - }; - } + JsonValueKind.String => Scalar.Value( node.GetValue()?.Length ?? 0 ), + JsonValueKind.Array => Scalar.Value( node.AsArray().Count ), + JsonValueKind.Object => Scalar.Value( node.AsObject().Count ), + _ => Scalar.Nothing + }; + } + return Scalar.Nothing; + } } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs index 443d9fcb..5612611f 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs @@ -1,6 +1,4 @@ using System.Reflection; -using System.Text.Json; -using System.Text.Json.Nodes; using System.Text.RegularExpressions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; @@ -8,34 +6,21 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class MatchNodeFunction() : FilterExtensionFunction( MatchMethodInfo, FilterExtensionInfo.MustNotCompare ) +public class MatchNodeFunction() : FilterExtensionFunction( MatchMethod, FilterExtensionInfo.MustNotCompare ) { public const string Name = "match"; - private static readonly MethodInfo MatchMethodInfo = GetMethod( nameof( Match ) ); + private static readonly MethodInfo MatchMethod = GetMethod( nameof( Match ) ); - public static INodeType Match( INodeType input, INodeType regex ) + public static ScalarValue Match( IValueType input, IValueType pattern ) { - return input switch - { - NodesType nodes when regex is ValueType stringValue => - MatchImpl( nodes, stringValue.Value ), - NodesType nodes when regex is NodesType stringValue => - MatchImpl( nodes, stringValue.Value.FirstOrDefault()?.GetValue() ), - _ => Constants.False - }; - } - - private static INodeType MatchImpl( NodesType nodes, string regex ) - { - var value = nodes.FirstOrDefault(); - - if ( value?.GetValueKind() != JsonValueKind.String ) - return Constants.False; + if ( !input.TryGetValue( out var value ) || value == null ) + return false; - var stringValue = value.GetValue(); + if ( !pattern.TryGetValue( out var patternValue ) || patternValue == null ) + return false; - var regexPattern = new Regex( $"^{IRegexp.ConvertToIRegexp( regex )}$" ); - return new ValueType( regexPattern.IsMatch( stringValue ) ); + var regex = new Regex( $"^{IRegexp.ConvertToIRegexp( patternValue )}$" ); + return regex.IsMatch( value ); } } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs index 40f26776..632c05a8 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs @@ -1,6 +1,4 @@ using System.Reflection; -using System.Text.Json; -using System.Text.Json.Nodes; using System.Text.RegularExpressions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; @@ -8,31 +6,20 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class SearchNodeFunction() : FilterExtensionFunction( SearchMethodInfo, FilterExtensionInfo.MustNotCompare ) +public class SearchNodeFunction() : FilterExtensionFunction( SearchMethod, FilterExtensionInfo.MustNotCompare ) { public const string Name = "search"; - private static readonly MethodInfo SearchMethodInfo = GetMethod( nameof( Search ) ); + private static readonly MethodInfo SearchMethod = GetMethod( nameof( Search ) ); - public static INodeType Search( INodeType input, INodeType regex ) + public static ScalarValue Search( IValueType input, IValueType pattern ) { - return input switch - { - NodesType nodes when regex is ValueType stringValue => - SearchImpl( nodes, stringValue.Value ), - NodesType nodes when regex is NodesType stringValue => - SearchImpl( nodes, stringValue.Value.FirstOrDefault()?.GetValue() ), - _ => Constants.False - }; - } - - public static INodeType SearchImpl( NodesType nodes, string regex ) - { - var value = nodes.FirstOrDefault(); + if ( !input.TryGetValue( out var value ) || value == null ) + return false; - if ( value?.GetValueKind() != JsonValueKind.String ) - return Constants.False; + if ( !pattern.TryGetValue( out var patternValue ) || patternValue == null ) + return false; - var regexPattern = new Regex( IRegexp.ConvertToIRegexp( regex ) ); - return new ValueType( regexPattern.IsMatch( value.GetValue() ) ); + var regex = new Regex( IRegexp.ConvertToIRegexp( patternValue ) ); + return regex.IsMatch( value ); } } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs index 36ae42f6..05571780 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs @@ -1,46 +1,34 @@ using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Descriptors.Node.Functions; -public class ValueNodeFunction() : FilterExtensionFunction( ValueMethodInfo, FilterExtensionInfo.MustCompare ) +public class ValueNodeFunction() : FilterExtensionFunction( ValueMethod, FilterExtensionInfo.MustCompare ) { public const string Name = "value"; - private static readonly MethodInfo ValueMethodInfo = GetMethod( nameof( Value ) ); + private static readonly MethodInfo ValueMethod = GetMethod( nameof( Value ) ); - public static INodeType Value( INodeType arg ) + public static IValueType Value( IValueType argument ) { - if ( arg.Kind != NodeTypeKind.NodeList ) - throw new NotSupportedException( $"Function {Name} does not support kind {arg.Kind}" ); + if ( argument is not NodeList nodes ) + throw new NotSupportedException( $"Function `{Name}` does not support kind {argument.ValueKind}" ); - var nodeArray = ((NodesType) arg).ToArray(); - - if ( nodeArray.Length != 1 ) - return Constants.Nothing; - - var node = nodeArray.FirstOrDefault(); + var node = nodes.OneOrDefault(); return node?.GetValueKind() switch { - JsonValueKind.Number => new ValueType( node.GetValue() ), - JsonValueKind.String => new ValueType( node.GetValue() ), - JsonValueKind.Object or JsonValueKind.Array => new ValueType( IsNotEmpty( node ) ), - JsonValueKind.True => Constants.True, - JsonValueKind.False or JsonValueKind.Null or JsonValueKind.Undefined => Constants.False, - _ => Constants.False + JsonValueKind.Number when node.AsValue().TryGetValue( out var intValue ) => Scalar.Value( intValue ), + JsonValueKind.Number => Scalar.Value( node.GetValue() ), + JsonValueKind.String => Scalar.Value( node.GetValue() ), + JsonValueKind.Object => Scalar.Value( node.AsObject().Count != 0 ), + JsonValueKind.Array => Scalar.Value( node.AsArray().Count != 0 ), + JsonValueKind.True => Scalar.True, + JsonValueKind.False => Scalar.False, + _ => Scalar.Nothing }; - - static bool IsNotEmpty( JsonNode node ) - { - return node.GetValueKind() switch - { - JsonValueKind.Array => node.AsArray().Count != 0, - JsonValueKind.Object => node.AsObject().Count != 0, - _ => false - }; - } } } diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs index bdfd97b4..c23336db 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs @@ -1,15 +1,15 @@ using System.Text.Json.Nodes; using Hyperbee.Json.Descriptors.Node.Functions; using Hyperbee.Json.Filters; -using Hyperbee.Json.Filters.Values; +using Hyperbee.Json.Filters.Parser; namespace Hyperbee.Json.Descriptors.Node; public class NodeTypeDescriptor : ITypeDescriptor { - private FilterEvaluator _evaluator; private NodeValueAccessor _accessor; - private NodeTypeComparer _comparer; + private FilterEvaluator _evaluator; + private ValueTypeComparer _comparer; public FunctionRegistry Functions { get; } = new(); @@ -19,8 +19,8 @@ public class NodeTypeDescriptor : ITypeDescriptor public IFilterEvaluator FilterEvaluator => _evaluator ??= new FilterEvaluator( this ); - public INodeTypeComparer Comparer => - _comparer ??= new NodeTypeComparer( Accessor ); + public IValueTypeComparer Comparer => + _comparer ??= new ValueTypeComparer( Accessor ); public bool CanUsePointer => true; diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs index 852b0163..c9695505 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs @@ -157,9 +157,26 @@ public bool TryGetValueFromNode( JsonNode node, out object value ) case JsonValueKind.String: value = node.GetValue(); break; + case JsonValueKind.Number: - value = node.GetValue(); - break; + if ( node is JsonValue jsonValue ) + { + if ( jsonValue.TryGetValue( out int intValue ) ) + { + value = intValue; + break; + } + + if ( jsonValue.TryGetValue( out float floatValue ) ) + { + value = floatValue; + break; + } + } + + value = false; + return false; + case JsonValueKind.True: value = true; break; @@ -178,6 +195,7 @@ public bool TryGetValueFromNode( JsonNode node, out object value ) return true; } + public bool TryGetFromPointer( in JsonNode node, JsonPathSegment segment, out JsonNode childValue ) { return node.TryGetFromJsonPathPointer( segment, out childValue ); diff --git a/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs b/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs new file mode 100644 index 00000000..e5c43746 --- /dev/null +++ b/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs @@ -0,0 +1,89 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; +using Hyperbee.Json.Filters.Values; + +namespace Hyperbee.Json.Descriptors.Node; + +public static class ValueTypeExtensions +{ + public static bool TryGetValue( this IValueType input, out T value ) where T : IConvertible + { + switch ( input ) + { + case ScalarValue valueType: + value = valueType.Value; + return true; + + case NodeList nodesType: + var node = nodesType.FirstOrDefault(); + if ( node.TryConvertTo( out value ) ) + return true; + break; + } + + value = default; + return false; + } + + public static bool TryGetNode( this IValueType input, out T value ) + { + if ( input is NodeList nodes ) + { + value = nodes.OneOrDefault(); + return true; + } + + value = default; + return false; + } + + private static bool TryConvertTo( this JsonNode node, out T value ) where T : IConvertible + { + value = default; + try + { + if ( typeof( T ) == typeof( string ) && node is JsonValue jsonValue && jsonValue.TryGetValue( out string stringValue ) ) + { + value = (T) (IConvertible) stringValue; + return true; + } + + if ( typeof( T ) == typeof( int ) && node is JsonValue jsonInt && jsonInt.TryGetValue( out int intValue ) ) + { + value = (T) (IConvertible) intValue; + return true; + } + + if ( typeof( T ) == typeof( float ) && node is JsonValue jsonFloat && jsonFloat.TryGetValue( out float floatValue ) ) + { + value = (T) (IConvertible) floatValue; + return true; + } + + if ( typeof( T ) == typeof( float ) && node is JsonArray jsonArray ) + { + value = (T) (IConvertible) jsonArray.Count; + return true; + } + + if ( typeof( T ) == typeof( float ) && node is JsonObject jsonObject ) + { + value = (T) (IConvertible) jsonObject.Count; + return true; + } + + if ( typeof( T ) == typeof( bool ) && node is JsonValue jsonBool && jsonBool.TryGetValue( out bool boolValue ) ) + { + value = (T) (IConvertible) boolValue; + return true; + } + } + catch + { + // Ignore conversion errors + } + + return false; + } +} diff --git a/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs b/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..566a337f --- /dev/null +++ b/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs @@ -0,0 +1,17 @@ +namespace Hyperbee.Json.Extensions; + +public static class EnumerableExtensions +{ + public static T OneOrDefault( this IEnumerable source ) + { + ArgumentNullException.ThrowIfNull( source, nameof( source ) ); + + using var iterator = source.GetEnumerator(); + + if ( !iterator.MoveNext() ) // No elements + return default; + + var singleItem = iterator.Current; + return iterator.MoveNext() ? default : singleItem; + } +} diff --git a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs index a088c5a3..600eb9d4 100644 --- a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs +++ b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs @@ -3,9 +3,9 @@ namespace Hyperbee.Json.Extensions; -// DISTINCT from JsonPath these extensions are intended to facilitate 'diving' for Json Properties using -// normalized paths. a normalized path is an absolute path that references a single element. -// similar to JsonPointer but using JsonPath notation. +// DISTINCT from JsonPath these extensions are intended to facilitate 'diving' for Json Properties +// using normalized paths. a normalized path is an absolute path that references a single element. +// Similar to JsonPointer but using JsonPath notation. // // syntax supports absolute paths; dotted notation, quoted names, and simple bracketed array accessors only. // diff --git a/src/Hyperbee.Json/Filters/IRegexp.cs b/src/Hyperbee.Json/Filters/IRegexp.cs index 26c66468..b3c7bdbc 100644 --- a/src/Hyperbee.Json/Filters/IRegexp.cs +++ b/src/Hyperbee.Json/Filters/IRegexp.cs @@ -27,9 +27,7 @@ public static string ConvertToIRegexp( ReadOnlySpan pattern ) return string.Empty; var patternSize = pattern.Length; - Span dotPositions = patternSize > 256 - ? new bool[patternSize] - : stackalloc bool[patternSize]; + Span dotPositions = patternSize > 256 ? new bool[patternSize] : stackalloc bool[patternSize]; var inCharacterClass = false; var dotCount = 0; @@ -66,8 +64,8 @@ public static string ConvertToIRegexp( ReadOnlySpan pattern ) * - The entire pattern is wrapped in a non-capturing group to group the regex parts together * without capturing the matched text. * - * 2. Negative Lookahead `(?! ... )` - * - `(?[\r\n])`: Match any character that is not a carriage return (`\r`) or newline (`\n`). + * 2. Negative Character Class `[^ ...]` + * - `[^\r\n]`: Match any character that is not a carriage return (`\r`) or newline (`\n`). * * 3. Surrogate Pair `\p{Cs}\p{Cs}` * - `\p{Cs}`: Matches any character in the "Cs" (surrogate) Unicode category. @@ -88,12 +86,10 @@ public static string ConvertToIRegexp( ReadOnlySpan pattern ) * \p{Cs}\p{Cs} # Match a surrogate pair (two surrogates in sequence) * ) */ - var replacement = @"(?:[^\r\n]|\p{Cs}\p{Cs})".AsSpan(); // (?:(?![\r\n])\P{Cs}|\p{Cs}\p{Cs}) + var replacement = @"(?:[^\r\n]|\p{Cs}\p{Cs})".AsSpan(); - var newSize = pattern.Length + dotCount * (replacement.Length - 1); // '.' is 1 char, so extra (pattern-length - 1) chars per '.' - Span buffer = newSize > 512 - ? new char[newSize] - : stackalloc char[newSize]; + var newSize = pattern.Length + dotCount * (replacement.Length - 1); + Span buffer = newSize > 512 ? new char[newSize] : stackalloc char[newSize]; var bufferIndex = 0; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs new file mode 100644 index 00000000..0e76ca43 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs @@ -0,0 +1,61 @@ +using System.Linq.Expressions; +using System.Reflection; +using Hyperbee.Json.Filters.Values; + +namespace Hyperbee.Json.Filters.Parser.Expressions; + +public static class CompareExpression +{ + private static readonly MethodInfo AreEqualMethod = typeof( CompareExpression ).GetMethod( nameof( AreEqual ) ); + private static readonly MethodInfo AreNotEqualMethod = typeof( CompareExpression ).GetMethod( nameof( AreNotEqual ) ); + private static readonly MethodInfo IsLessThanMethod = typeof( CompareExpression ).GetMethod( nameof( IsLessThan ) ); + private static readonly MethodInfo IsLessThanOrEqualMethod = typeof( CompareExpression ).GetMethod( nameof( IsLessThanOrEqual ) ); + private static readonly MethodInfo IsGreaterThanMethod = typeof( CompareExpression ).GetMethod( nameof( IsGreaterThan ) ); + private static readonly MethodInfo IsGreaterThanOrEqualMethod = typeof( CompareExpression ).GetMethod( nameof( IsGreaterThanOrEqual ) ); + + private static readonly MethodInfo AndAlsoMethod = typeof( CompareExpression ).GetMethod( nameof( AndAlso ) ); + private static readonly MethodInfo OrElseMethod = typeof( CompareExpression ).GetMethod( nameof( OrElse ) ); + private static readonly MethodInfo NotMethod = typeof( CompareExpression ).GetMethod( nameof( NotBoolean ) ); + + public static Expression Equal( Expression left, Expression right ) => Expression.Call( AreEqualMethod, left, right ); + public static Expression NotEqual( Expression left, Expression right ) => Expression.Call( AreNotEqualMethod, left, right ); + public static Expression LessThan( Expression left, Expression right ) => Expression.Call( IsLessThanMethod, left, right ); + public static Expression LessThanOrEqual( Expression left, Expression right ) => Expression.Call( IsLessThanOrEqualMethod, left, right ); + public static Expression GreaterThan( Expression left, Expression right ) => Expression.Call( IsGreaterThanMethod, left, right ); + public static Expression GreaterThanOrEqual( Expression left, Expression right ) => Expression.Call( IsGreaterThanOrEqualMethod, left, right ); + + public static Expression And( Expression left, Expression right ) => Expression.Call( AndAlsoMethod, left, right ); + public static Expression Or( Expression left, Expression right ) => Expression.Call( OrElseMethod, left, right ); + public static Expression Not( Expression expression ) => Expression.Call( NotMethod, expression ); + + public static bool AreEqual( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.Equals ) == 0; + public static bool AreNotEqual( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.NotEquals ) != 0; + public static bool IsLessThan( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.LessThan ) < 0; + public static bool IsLessThanOrEqual( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.LessThanOrEqual ) <= 0; + public static bool IsGreaterThan( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.GreaterThan ) > 0; + public static bool IsGreaterThanOrEqual( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.GreaterThanOrEqual ) >= 0; + + public static bool AndAlso( IValueType left, IValueType right ) + { + if ( left is ScalarValue leftBoolValue && right is ScalarValue rightBoolValue ) + return leftBoolValue.Value && rightBoolValue.Value; + + return left.Comparer.Exists( left ) && right.Comparer.Exists( right ); + } + + public static bool OrElse( IValueType left, IValueType right ) + { + if ( left is ScalarValue leftBoolValue && right is ScalarValue rightBoolValue ) + return leftBoolValue.Value || rightBoolValue.Value; + + return left.Comparer.Exists( left ) || right.Comparer.Exists( right ); + } + + public static bool NotBoolean( IValueType value ) + { + if ( value is ScalarValue { Value: false } ) + return true; + + return !value.Comparer.Exists( value ); + } +} diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/FilterTruthyExpression.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/FilterTruthyExpression.cs new file mode 100644 index 00000000..02592781 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/FilterTruthyExpression.cs @@ -0,0 +1,33 @@ +using System.Collections; +using System.Linq.Expressions; +using System.Reflection; +using Hyperbee.Json.Filters.Values; + +namespace Hyperbee.Json.Filters.Parser.Expressions; + +public static class FilterTruthyExpression +{ + private static readonly MethodInfo IsTruthyMethod = typeof( FilterTruthyExpression ).GetMethod( nameof( IsTruthy ), BindingFlags.NonPublic | BindingFlags.Static ); + + public static Expression IsTruthyExpression( Expression expression ) => + expression.Type == typeof( bool ) + ? expression + : Expression.Call( IsTruthyMethod, expression ); + + private static bool IsTruthy( object value ) + { + var truthy = value switch + { + Nothing => false, + Null => false, + ScalarValue valueBool => valueBool.Value, + ScalarValue intValue => intValue.Value != 0, + ScalarValue floatValue => floatValue.Value != 0, + ScalarValue valueString => !string.IsNullOrEmpty( valueString.Value ) && !valueString.Value.Equals( "false", StringComparison.OrdinalIgnoreCase ), + IEnumerable enumerable => enumerable.Cast().Any(), // NodeList + _ => true + }; + + return truthy; + } +} diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs index 8d8cfc62..0d8220dd 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs @@ -6,12 +6,12 @@ internal class FunctionExpressionFactory : IExpressionFactory { public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) { - if ( parserContext.Descriptor.Functions.TryGetCreator( state.Item.ToString(), out var functionCreator ) ) + if ( parserContext.Descriptor.Functions.TryGetActivator( state.Item.ToString(), out var functionActivator ) ) { if ( state.TrailingWhitespace ) throw new NotSupportedException( "Whitespace is not allowed after a function name." ); - var function = functionCreator(); + var function = functionActivator(); expression = function .GetExpression( ref state, parserContext ); // will recurse for each function argument. diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs index 846587e0..6c0c90ee 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs @@ -9,7 +9,7 @@ public static bool TryGetExpression( ref ParserState state, out Expressio { if ( parserContext.Descriptor.Accessor.TryParseNode( state.Item.ToString(), out var node ) ) { - expression = Expression.Constant( new NodesType( [node], isNormalized: true ) ); + expression = Expression.Constant( new NodeList( [node], isNormalized: true ) ); expressionInfo.Kind = ExpressionKind.Json; return true; } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs index b3f1c538..fe71e3e4 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs @@ -22,29 +22,32 @@ private static ConstantExpression GetLiteralExpression( ReadOnlySpan item // Check for known literals (true, false, null) first if ( item.Equals( "true", StringComparison.OrdinalIgnoreCase ) ) - return Expression.Constant( Constants.True ); + return Expression.Constant( Scalar.True ); if ( item.Equals( "false", StringComparison.OrdinalIgnoreCase ) ) - return Expression.Constant( Constants.False ); + return Expression.Constant( Scalar.False ); if ( item.Equals( "null", StringComparison.OrdinalIgnoreCase ) ) - return Expression.Constant( Constants.Null ); + return Expression.Constant( Scalar.Null ); // Check for quoted strings if ( item.Length >= 2 && (item[0] == '"' && item[^1] == '"' || item[0] == '\'' && item[^1] == '\'') ) - return Expression.Constant( new ValueType( item[1..^1].ToString() ) ); // remove quotes + return Expression.Constant( Scalar.Value( item[1..^1].ToString() ) ); // remove quotes // Check for numbers // // The current design treats all numbers are floats since we don't // know what's in the data or the other side of the operator yet. + if ( int.TryParse( item, out int intResult ) ) + return Expression.Constant( Scalar.Value( intResult ) ); + if ( item.Length > 0 && item[^1] == '.' ) // incomplete floating-point number. we can parse it but the RFC doesn't like it. throw new NotSupportedException( $"Incomplete floating-point number `{item.ToString()}`" ); return float.TryParse( item, out float result ) - ? Expression.Constant( new ValueType( result ) ) + ? Expression.Constant( Scalar.Value( result ) ) : null; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs index 7220ee9a..fba81be2 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using System.Reflection; using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser.Expressions; @@ -16,11 +17,12 @@ public static bool TryGetExpression( ref ParserState state, out Expressio return true; } - static class ExpressionHelper + private static class ExpressionHelper { - private static readonly Expression SelectExpression = Expression.Constant( (Func, INodeType>) Select ); + private static readonly MethodInfo SelectMethod = typeof( ExpressionHelper ) + .GetMethod( nameof( Select ), BindingFlags.NonPublic | BindingFlags.Static ); - public static Expression GetExpression( ReadOnlySpan item, bool allowDotWhitespace, FilterParserContext parserContext ) + public static MethodCallExpression GetExpression( ReadOnlySpan item, bool allowDotWhitespace, FilterParserContext parserContext ) { if ( item.IsEmpty ) return null; @@ -28,22 +30,20 @@ public static Expression GetExpression( ReadOnlySpan item, bool allowDotWh if ( item[0] != '$' && item[0] != '@' ) return null; - return Expression.Invoke( - SelectExpression, + return Expression.Call( + SelectMethod, Expression.Constant( item.ToString() ), Expression.Constant( allowDotWhitespace ), parserContext.RuntimeContext ); } - private static INodeType Select( string query, bool allowDotWhitespace, FilterRuntimeContext runtimeContext ) + private static IValueType Select( string query, bool allowDotWhitespace, FilterRuntimeContext runtimeContext ) { var compileQuery = JsonPathQueryParser.Parse( query, allowDotWhitespace ); - // Current becomes root - return query[0] == '$' - ? new NodesType( JsonPath.SelectInternal( runtimeContext.Root, runtimeContext.Root, compileQuery ), compileQuery.Normalized ) - : new NodesType( JsonPath.SelectInternal( runtimeContext.Current, runtimeContext.Root, compileQuery ), compileQuery.Normalized ); - } + var value = query[0] == '$' ? runtimeContext.Root : runtimeContext.Current; + return new NodeList( JsonPath.SelectInternal( value, runtimeContext.Root, compileQuery ), compileQuery.Normalized ); + } } } diff --git a/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs b/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs index 51efee57..fbc9aaf1 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs @@ -2,71 +2,83 @@ using System.Reflection; using Hyperbee.Json.Filters.Values; -namespace Hyperbee.Json.Filters.Parser; - -public abstract class FilterExtensionFunction +namespace Hyperbee.Json.Filters.Parser { - private readonly int _argumentCount; - private readonly MethodInfo _methodInfo; - private MethodInfo _throwIfNotNormalizedMethodInfo; - - public FilterExtensionInfo FunctionInfo { get; } - - protected FilterExtensionFunction( MethodInfo methodInfo, FilterExtensionInfo filterInfo ) + public abstract class FilterExtensionFunction { - _argumentCount = methodInfo.GetParameters().Length; - _methodInfo = methodInfo; + private readonly int _argumentCount; + private readonly MethodInfo _methodInfo; - FunctionInfo = filterInfo; - } + public FilterExtensionInfo FunctionInfo { get; } - internal Expression GetExpression( ref ParserState state, FilterParserContext parserContext ) - { - var arguments = new Expression[_argumentCount]; - var expectNormalized = FunctionInfo.HasFlag( FilterExtensionInfo.ExpectNormalized ); - - for ( var i = 0; i < _argumentCount; i++ ) + protected FilterExtensionFunction( MethodInfo methodInfo, FilterExtensionInfo filterInfo ) { - var localState = state with - { - Item = [], - Terminal = i == _argumentCount - 1 - ? FilterParser.ArgClose - : FilterParser.ArgComma, - IsArgument = true - }; + _argumentCount = methodInfo.GetParameters().Length; + _methodInfo = methodInfo; - if ( localState.EndOfBuffer ) - throw new NotSupportedException( $"Invalid arguments for filter: \"{state.Buffer}\"." ); + FunctionInfo = filterInfo; + } - var argument = FilterParser.Parse( ref localState, parserContext ); + internal Expression GetExpression( ref ParserState state, FilterParserContext parserContext ) + { + var arguments = new Expression[_argumentCount]; + var expectNormalized = FunctionInfo.HasFlag( FilterExtensionInfo.ExpectNormalized ); - // Create expression that throws if not normalized. - if ( expectNormalized ) + for ( var i = 0; i < _argumentCount; i++ ) { - _throwIfNotNormalizedMethodInfo ??= GetMethod( nameof( ThrowIfNotNormalized ) ) - .MakeGenericMethod( typeof( TNode ) ); + var localState = state with + { + Item = [], + IsArgument = true, + Terminal = i == _argumentCount - 1 + ? FilterParser.ArgClose + : FilterParser.ArgComma + }; - arguments[i] = Expression.Call( _throwIfNotNormalizedMethodInfo, - Expression.Constant( _methodInfo.Name ), - Expression.Convert( argument, typeof( INodeType ) ) ); + if ( localState.EndOfBuffer ) + throw new NotSupportedException( $"Invalid arguments for filter: \"{state.Buffer}\"." ); + + var argument = FilterParser.Parse( ref localState, parserContext ); + arguments[i] = ArgumentExpression( expectNormalized, argument ); } - else + + // Call the method and cast the result to INodeType + var callExpression = Expression.Call( _methodInfo, arguments ); + var castExpression = Expression.Convert( callExpression, typeof( IValueType ) ); + + return castExpression; + } + + private Expression ArgumentExpression( bool expectNormalized, Expression argument ) + { + if ( expectNormalized ) { - arguments[i] = Expression.Convert( argument, typeof( INodeType ) ); + // Create expression that throws if not normalized. + return Expression.Call( + ExpressionHelper.ValidateArgumentMethod, + Expression.Constant( _methodInfo.Name ), + Expression.Convert( argument, typeof( IValueType ) ) + ); } + + return Expression.Convert( argument, typeof( IValueType ) ); } - return Expression.Call( _methodInfo, arguments ); - } + protected static MethodInfo GetMethod( string methodName ) => + typeof( T ).GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ); - public static INodeType ThrowIfNotNormalized( string methodName, INodeType node ) - { - if ( node is NodesType { IsNormalized: false } ) - throw new NotSupportedException( $"Function {methodName} does not support non-singular arguments." ); + private static class ExpressionHelper // this helper removes need for MakeGenericMethod + { + public static readonly MethodInfo ValidateArgumentMethod = + typeof( ExpressionHelper ).GetMethod( nameof( ValidateArgument ), BindingFlags.NonPublic | BindingFlags.Static ); - return node; - } + static IValueType ValidateArgument( string methodName, IValueType argument ) + { + if ( argument is NodeList { IsNormalized: false } ) + throw new NotSupportedException( $"Function {methodName} does not support non-singular arguments." ); - protected static MethodInfo GetMethod( string methodName ) => typeof( T ).GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ); + return argument; + } + } + } } diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index 4e0c0ba8..c8dd9d6b 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -13,6 +13,7 @@ using System.Linq.Expressions; using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Parser.Expressions; +using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser; @@ -333,27 +334,73 @@ Operator.LessThan or private static void MergeItems( ExprItem left, ExprItem right, FilterParserContext parserContext ) { - left.Expression = NodeTypeComparerBinderExpression.BindComparerExpression( parserContext, left.Expression ); - right.Expression = NodeTypeComparerBinderExpression.BindComparerExpression( parserContext, right.Expression ); + left.Expression = BindComparerExpression( parserContext, left.Expression ); + right.Expression = BindComparerExpression( parserContext, right.Expression ); left.Expression = left.Operator switch { - Operator.Equals => NodeTypeExpression.Equal( left.Expression, right.Expression ), - Operator.NotEquals => NodeTypeExpression.NotEqual( left.Expression, right.Expression ), - Operator.GreaterThan => NodeTypeExpression.GreaterThan( left.Expression, right.Expression ), - Operator.GreaterThanOrEqual => NodeTypeExpression.GreaterThanOrEqual( left.Expression, right.Expression ), - Operator.LessThan => NodeTypeExpression.LessThan( left.Expression, right.Expression ), - Operator.LessThanOrEqual => NodeTypeExpression.LessThanOrEqual( left.Expression, right.Expression ), - Operator.And => NodeTypeExpression.And( left.Expression, right.Expression ), - Operator.Or => NodeTypeExpression.Or( left.Expression, right.Expression ), - Operator.Not => NodeTypeExpression.Not( right.Expression ), + Operator.Equals => CompareExpression.Equal( left.Expression, right.Expression ), + Operator.NotEquals => CompareExpression.NotEqual( left.Expression, right.Expression ), + Operator.GreaterThan => CompareExpression.GreaterThan( left.Expression, right.Expression ), + Operator.GreaterThanOrEqual => CompareExpression.GreaterThanOrEqual( left.Expression, right.Expression ), + Operator.LessThan => CompareExpression.LessThan( left.Expression, right.Expression ), + Operator.LessThanOrEqual => CompareExpression.LessThanOrEqual( left.Expression, right.Expression ), + Operator.And => CompareExpression.And( left.Expression, right.Expression ), + Operator.Or => CompareExpression.Or( left.Expression, right.Expression ), + Operator.Not => CompareExpression.Not( right.Expression ), _ => throw new InvalidOperationException( $"Invalid operator {left.Operator}" ) }; - left.Expression = FilterTruthyExpression.ConvertBoolToValueTypeExpression( left.Expression ); + left.Expression = ConvertBoolToScalarExpression( left.Expression ); left.Operator = right.Operator; left.ExpressionInfo.Kind = ExpressionKind.Merged; + + return; + + static Expression ConvertBoolToScalarExpression( Expression leftExpression ) + { + // convert bool result to Scalar.True or Scalar.False + Expression conditionalExpression = Expression.Condition( + leftExpression, + Expression.Constant( Scalar.True, typeof( IValueType ) ), + Expression.Constant( Scalar.False, typeof( IValueType ) ) + ); + + return conditionalExpression; + } + + static Expression BindComparerExpression( FilterParserContext parserContext, Expression expression ) + { + // Create an Expression that does: + // + // static IValueType BindComparerExpression(FilterParserContext parserContext, IValueType value) + // { + // value.Comparer = parserContext.Descriptor.Comparer; + // return value; + // } + + if ( expression == null ) + return null; + + var valueVariable = Expression.Variable( typeof( IValueType ), "value" ); + + var valueAssign = Expression.Assign( + valueVariable, + Expression.Convert( expression, typeof( IValueType ) ) ); + + var comparerAssign = Expression.Assign( + Expression.PropertyOrField( valueVariable, "Comparer" ), + Expression.Constant( parserContext.Descriptor.Comparer, typeof( IValueTypeComparer ) ) + ); + + return Expression.Block( + [valueVariable], + valueAssign, + comparerAssign, + valueVariable + ); + } } // Throw helpers diff --git a/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs b/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs deleted file mode 100644 index 1e2820d7..00000000 --- a/src/Hyperbee.Json/Filters/Parser/FilterTruthyExpression.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections; -using System.Linq.Expressions; -using System.Reflection; -using Hyperbee.Json.Filters.Values; - -namespace Hyperbee.Json.Filters.Parser; - -public static class FilterTruthyExpression -{ - private static readonly MethodInfo IsTruthyMethodInfo = typeof( FilterTruthyExpression ).GetMethod( nameof( IsTruthy ) ); - private static readonly MethodInfo ConvertBoolToValueTypeMethodInfo = typeof( FilterTruthyExpression ).GetMethod( nameof( ConvertBoolToValueType ) ); - - public static Expression IsTruthyExpression( Expression expression ) => - expression.Type == typeof( bool ) - ? expression - : Expression.Call( IsTruthyMethodInfo, expression ); - - public static Expression ConvertBoolToValueTypeExpression( Expression expression ) => - Expression.Call( ConvertBoolToValueTypeMethodInfo, expression ); - - public static bool IsTruthy( object value ) - { - var truthy = value switch - { - Nothing => false, - Null => false, - ValueType valueBool => valueBool.Value, - ValueType floatValue => floatValue.Value != 0, - ValueType valueString => !string.IsNullOrEmpty( valueString.Value ) && !valueString.Value.Equals( "false", StringComparison.OrdinalIgnoreCase ), - IEnumerable enumerable => enumerable.Cast().Any(), // NodesType - _ => true - }; - - return truthy; - } - - public static INodeType ConvertBoolToValueType( bool value ) - { - return value ? Constants.True : Constants.False; - } -} diff --git a/src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs b/src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs deleted file mode 100644 index b6ca6328..00000000 --- a/src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Json.Filters.Values; - -namespace Hyperbee.Json.Filters.Parser; - -public static class NodeTypeComparerBinderExpression -{ - private static readonly Expression BindComparerExpressionConst = Expression.Constant( (Func, INodeType, INodeType>) BindComparer ); - internal static Expression BindComparerExpression( FilterParserContext parserContext, Expression expression ) - { - if ( expression == null ) - return null; - - var parserContextExp = Expression.Constant( parserContext ); - - return Expression.Invoke( BindComparerExpressionConst, parserContextExp, - Expression.Convert( expression, typeof( INodeType ) ) ); - } - - internal static INodeType BindComparer( FilterParserContext parserContext, INodeType item ) - { - item.Comparer = parserContext.Descriptor.Comparer; - return item; - } -} diff --git a/src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs b/src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs deleted file mode 100644 index 8b969121..00000000 --- a/src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Linq.Expressions; -using System.Reflection; -using Hyperbee.Json.Filters.Values; - -namespace Hyperbee.Json.Filters.Parser; - -public static class NodeTypeExpression -{ - private static readonly MethodInfo AreEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( AreEqual ) ); - private static readonly MethodInfo AreNotEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( AreNotEqual ) ); - private static readonly MethodInfo IsLessThanMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsLessThan ) ); - private static readonly MethodInfo IsLessThanOrEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsLessThanOrEqual ) ); - private static readonly MethodInfo IsGreaterThanMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsGreaterThan ) ); - private static readonly MethodInfo IsGreaterThanOrEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsGreaterThanOrEqual ) ); - - private static readonly MethodInfo AndAlsoMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( AndAlso ) ); - private static readonly MethodInfo OrElseMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( OrElse ) ); - private static readonly MethodInfo NotMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( NotBoolean ) ); - - public static Expression Equal( Expression left, Expression right ) => Expression.Call( AreEqualMethodInfo, left, right ); - public static Expression NotEqual( Expression left, Expression right ) => Expression.Call( AreNotEqualMethodInfo, left, right ); - public static Expression LessThan( Expression left, Expression right ) => Expression.Call( IsLessThanMethodInfo, left, right ); - public static Expression LessThanOrEqual( Expression left, Expression right ) => Expression.Call( IsLessThanOrEqualMethodInfo, left, right ); - public static Expression GreaterThan( Expression left, Expression right ) => Expression.Call( IsGreaterThanMethodInfo, left, right ); - public static Expression GreaterThanOrEqual( Expression left, Expression right ) => Expression.Call( IsGreaterThanOrEqualMethodInfo, left, right ); - - // Binary operators - public static Expression And( Expression left, Expression right ) => Expression.Call( AndAlsoMethodInfo, left, right ); - public static Expression Or( Expression left, Expression right ) => Expression.Call( OrElseMethodInfo, left, right ); - public static Expression Not( Expression expression ) => Expression.Call( NotMethodInfo, expression ); - - public static bool AreEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.Equals ) == 0; - public static bool AreNotEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.NotEquals ) != 0; - public static bool IsLessThan( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.LessThan ) < 0; - public static bool IsLessThanOrEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.LessThanOrEqual ) <= 0; - public static bool IsGreaterThan( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.GreaterThan ) > 0; - public static bool IsGreaterThanOrEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.GreaterThanOrEqual ) >= 0; - - public static bool AndAlso( INodeType left, INodeType right ) - { - if ( left is ValueType leftBoolValue && right is ValueType rightBoolValue ) - return leftBoolValue.Value && rightBoolValue.Value; - - return left.Comparer.Exists( left ) && - right.Comparer.Exists( right ); - } - - public static bool OrElse( INodeType left, INodeType right ) - { - if ( left is ValueType leftBoolValue && right is ValueType rightBoolValue ) - return leftBoolValue.Value || rightBoolValue.Value; - - return left.Comparer.Exists( left ) || - right.Comparer.Exists( right ); - } - - public static bool NotBoolean( INodeType value ) - { - if ( value is ValueType { Value: false } ) - return true; - - return !value.Comparer.Exists( value ); - } -} diff --git a/src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs b/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs similarity index 68% rename from src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs rename to src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs index 47103f4b..45abfa42 100644 --- a/src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs +++ b/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs @@ -1,16 +1,16 @@ using Hyperbee.Json.Descriptors; -using Hyperbee.Json.Filters.Parser; +using Hyperbee.Json.Filters.Values; -namespace Hyperbee.Json.Filters.Values; +namespace Hyperbee.Json.Filters.Parser; -public interface INodeTypeComparer +public interface IValueTypeComparer { - public int Compare( INodeType left, INodeType right, Operator operation ); + public int Compare( IValueType left, IValueType right, Operator operation ); - public bool Exists( INodeType node ); + public bool Exists( IValueType node ); } -public class NodeTypeComparer( IValueAccessor accessor ) : INodeTypeComparer +public class ValueTypeComparer( IValueAccessor accessor ) : IValueTypeComparer { private const float Tolerance = 1e-6F; // Define a tolerance for float comparisons @@ -56,23 +56,23 @@ public class NodeTypeComparer( IValueAccessor accessor ) : INodeTy * - Check if one is a NodeList and the other is a Value. * - Compare directly if both are Values. */ - public int Compare( INodeType left, INodeType right, Operator operation ) + public int Compare( IValueType left, IValueType right, Operator operation ) { ThrowIfNotNormalized( left ); ThrowIfNotNormalized( right ); - if ( left is NodesType leftEnumerable && right is NodesType rightEnumerable ) + if ( left is NodeList leftEnumerable && right is NodeList rightEnumerable ) { return CompareEnumerables( leftEnumerable, rightEnumerable ); } - if ( left is NodesType leftEnumerable1 ) + if ( left is NodeList leftEnumerable1 ) { var compare = CompareEnumerableToValue( leftEnumerable1, right, out var typeMismatch, out var nodeCount ); return AdjustResult( compare, nodeCount, operation, typeMismatch ); } - if ( right is NodesType rightEnumerable1 ) + if ( right is NodeList rightEnumerable1 ) { var compare = CompareEnumerableToValue( rightEnumerable1, left, out var typeMismatch, out var nodeCount ); return AdjustResult( compare, nodeCount, operation, typeMismatch ); @@ -104,27 +104,28 @@ static int AdjustResult( int compare, int nodeCount, Operator operation, bool ty return (nodeCount != 1 || typeMismatch) switch // Test for a non-single value set, or a type comparison mismatch { - true when (operation == Operator.LessThan || operation == Operator.LessThanOrEqual) => compare < 0 ? -compare : compare, - true when (operation == Operator.GreaterThan || operation == Operator.GreaterThanOrEqual) => compare > 0 ? -compare : compare, + true when operation == Operator.LessThan || operation == Operator.LessThanOrEqual => compare < 0 ? -compare : compare, + true when operation == Operator.GreaterThan || operation == Operator.GreaterThanOrEqual => compare > 0 ? -compare : compare, _ => compare }; } - static void ThrowIfNotNormalized( INodeType nodeType ) + static void ThrowIfNotNormalized( IValueType nodeType ) { - if ( nodeType is NodesType { IsNormalized: false } ) + if ( nodeType is NodeList { IsNormalized: false } ) throw new NotSupportedException( "Unsupported non-single query." ); } } - public bool Exists( INodeType node ) + public bool Exists( IValueType node ) { return node switch { - ValueType boolValue => boolValue.Value, - ValueType floatValue => floatValue.Value != 0, - ValueType stringValue => !string.IsNullOrEmpty( stringValue.Value ), - NodesType nodes => nodes.Any(), + ScalarValue boolValue => boolValue.Value, + ScalarValue intValue => intValue.Value != 0, + ScalarValue floatValue => floatValue.Value != 0, + ScalarValue stringValue => !string.IsNullOrEmpty( stringValue.Value ), + NodeList nodes => nodes.Any(), _ => false }; } @@ -154,7 +155,7 @@ private int CompareEnumerables( IEnumerable left, IEnumerable righ return 0; // Sequences are equal } - private int CompareEnumerableToValue( IEnumerable enumeration, INodeType value, out bool typeMismatch, out int nodeCount ) + private int CompareEnumerableToValue( IEnumerable enumeration, IValueType value, out bool typeMismatch, out int nodeCount ) { nodeCount = 0; typeMismatch = false; @@ -175,7 +176,7 @@ private int CompareEnumerableToValue( IEnumerable enumeration, INodeType if ( nodeCount == 0 ) { - if ( value is Nothing ) // Considered equal + if ( value.ValueKind == ValueKind.Nothing ) // Considered equal return 0; return -1; @@ -185,16 +186,16 @@ private int CompareEnumerableToValue( IEnumerable enumeration, INodeType } - private static int CompareValues( INodeType left, INodeType right, out bool typeMismatch ) + private static int CompareValues( IValueType left, IValueType right, out bool typeMismatch ) { typeMismatch = false; - if ( left is Null or Nothing && right is Null or Nothing ) + if ( IsNullOrNothing( left ) && IsNullOrNothing( right ) ) { return 0; } - if ( left?.GetType() != right?.GetType() ) + if ( IsTypeMismatch( left, right ) && !IsFloatToIntComparison( left, right ) ) { typeMismatch = true; // Type mismatch: important for non-equality comparisons return -1; @@ -202,29 +203,46 @@ private static int CompareValues( INodeType left, INodeType right, out bool type return left switch { - ValueType leftStringValue when right is ValueType rightStringValue => + ScalarValue leftStringValue when right is ScalarValue rightStringValue => string.Compare( leftStringValue.Value, rightStringValue.Value, StringComparison.Ordinal ), - ValueType leftBoolValue when right is ValueType rightBoolValue => + ScalarValue leftBoolValue when right is ScalarValue rightBoolValue => leftBoolValue.Value.CompareTo( rightBoolValue.Value ), - ValueType leftFloatValue when right is ValueType rightFloatValue => + ScalarValue leftFloatValue when right is ScalarValue rightFloatValue => Math.Abs( leftFloatValue.Value - rightFloatValue.Value ) < Tolerance ? 0 : leftFloatValue.Value.CompareTo( rightFloatValue.Value ), + ScalarValue leftIntValue when right is ScalarValue rightIntValue => + leftIntValue.Value.CompareTo( rightIntValue.Value ), + + ScalarValue leftIntValue when right is ScalarValue rightFloatValue => + Math.Abs( leftIntValue.Value - rightFloatValue.Value ) < Tolerance ? 0 : rightFloatValue.Value.CompareTo( leftIntValue.Value ), + + ScalarValue leftFloatValue when right is ScalarValue rightIntValue => + Math.Abs( leftFloatValue.Value - rightIntValue.Value ) < Tolerance ? 0 : leftFloatValue.Value.CompareTo( rightIntValue.Value ), + _ => Comparer.Default.Compare( left, right ) }; + + // Helpers + static bool IsTypeMismatch( IValueType left, IValueType right ) => left?.GetType() != right?.GetType(); + static bool IsNullOrNothing( IValueType value ) => value is Null or Nothing; + + static bool IsFloatToIntComparison( IValueType left, IValueType right ) => + left is ScalarValue && right is ScalarValue || left is ScalarValue && right is ScalarValue; } - private static bool TryGetValueType( IValueAccessor accessor, TNode node, out INodeType nodeType ) + private static bool TryGetValueType( IValueAccessor accessor, TNode node, out IValueType nodeType ) { if ( accessor.TryGetValueFromNode( node, out var itemValue ) ) { nodeType = itemValue switch { - string itemString => new ValueType( itemString ), - bool itemBool => new ValueType( itemBool ), - float itemFloat => new ValueType( itemFloat ), - null => Constants.Null, + string itemString => new ScalarValue( itemString ), + bool itemBool => new ScalarValue( itemBool ), + float itemFloat => new ScalarValue( itemFloat ), + int itemInt => new ScalarValue( itemInt ), + null => Scalar.Null, _ => throw new NotSupportedException( "Unsupported value type." ) }; return true; diff --git a/src/Hyperbee.Json/Filters/Values/Constants.cs b/src/Hyperbee.Json/Filters/Values/Constants.cs deleted file mode 100644 index 31f4b581..00000000 --- a/src/Hyperbee.Json/Filters/Values/Constants.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public static class Constants -{ - public static ValueType True { get; } = new( true ); - public static ValueType False { get; } = new( false ); - - public static Null Null { get; } = new(); - public static Nothing Nothing { get; } = new(); -} diff --git a/src/Hyperbee.Json/Filters/Values/INodeType.cs b/src/Hyperbee.Json/Filters/Values/INodeType.cs deleted file mode 100644 index a7fea226..00000000 --- a/src/Hyperbee.Json/Filters/Values/INodeType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public interface INodeType -{ - public NodeTypeKind Kind { get; } - - public INodeTypeComparer Comparer { get; set; } -} diff --git a/src/Hyperbee.Json/Filters/Values/IValueType.cs b/src/Hyperbee.Json/Filters/Values/IValueType.cs new file mode 100644 index 00000000..a375c6f1 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/IValueType.cs @@ -0,0 +1,21 @@ +using Hyperbee.Json.Filters.Parser; + +namespace Hyperbee.Json.Filters.Values; + +public interface IValueType +{ + public ValueKind ValueKind { get; } + public IValueTypeComparer Comparer { get; set; } +} + +public struct Null : IValueType +{ + public readonly ValueKind ValueKind => ValueKind.Null; + public IValueTypeComparer Comparer { get; set; } +} + +public struct Nothing : IValueType +{ + public readonly ValueKind ValueKind => ValueKind.Nothing; + public IValueTypeComparer Comparer { get; set; } +} diff --git a/src/Hyperbee.Json/Filters/Values/NodesType.cs b/src/Hyperbee.Json/Filters/Values/NodeList.cs similarity index 56% rename from src/Hyperbee.Json/Filters/Values/NodesType.cs rename to src/Hyperbee.Json/Filters/Values/NodeList.cs index 76cb6bc0..004480d6 100644 --- a/src/Hyperbee.Json/Filters/Values/NodesType.cs +++ b/src/Hyperbee.Json/Filters/Values/NodeList.cs @@ -1,13 +1,14 @@ using System.Collections; +using Hyperbee.Json.Filters.Parser; namespace Hyperbee.Json.Filters.Values; -public struct NodesType( IEnumerable value, bool isNormalized ) : INodeType, IEnumerable +public struct NodeList( IEnumerable value, bool isNormalized ) : IValueType, IEnumerable { public readonly bool IsNormalized => isNormalized; - public readonly NodeTypeKind Kind => NodeTypeKind.NodeList; + public readonly ValueKind ValueKind => ValueKind.NodeList; - public INodeTypeComparer Comparer { get; set; } + public IValueTypeComparer Comparer { get; set; } public IEnumerable Value { get; } = value; diff --git a/src/Hyperbee.Json/Filters/Values/Nothing.cs b/src/Hyperbee.Json/Filters/Values/Nothing.cs deleted file mode 100644 index 79a22880..00000000 --- a/src/Hyperbee.Json/Filters/Values/Nothing.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public struct Nothing : INodeType -{ - public readonly NodeTypeKind Kind => NodeTypeKind.Nothing; - public INodeTypeComparer Comparer { get; set; } -} diff --git a/src/Hyperbee.Json/Filters/Values/Null.cs b/src/Hyperbee.Json/Filters/Values/Null.cs deleted file mode 100644 index 72a0942f..00000000 --- a/src/Hyperbee.Json/Filters/Values/Null.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public struct Null : INodeType -{ - public readonly NodeTypeKind Kind => NodeTypeKind.Null; - public INodeTypeComparer Comparer { get; set; } -} diff --git a/src/Hyperbee.Json/Filters/Values/ScalarValue.cs b/src/Hyperbee.Json/Filters/Values/ScalarValue.cs new file mode 100644 index 00000000..25b2d6fa --- /dev/null +++ b/src/Hyperbee.Json/Filters/Values/ScalarValue.cs @@ -0,0 +1,29 @@ +using Hyperbee.Json.Filters.Parser; + +namespace Hyperbee.Json.Filters.Values; + +public struct ScalarValue( T value ) : IValueType where T : IConvertible +{ + public readonly ValueKind ValueKind => ValueKind.Scalar; + public IValueTypeComparer Comparer { get; set; } = null; + + public T Value { get; } = value; + + // Implicit conversion operators for bool, string, float, and int + + public static implicit operator ScalarValue( bool value ) => new( (T) (IConvertible) value ); + public static implicit operator ScalarValue( string value ) => new( (T) (IConvertible) value ); + public static implicit operator ScalarValue( int value ) => new( (T) (IConvertible) value ); + public static implicit operator ScalarValue( float value ) => new( (T) (IConvertible) value ); +} + +public static class Scalar +{ + public static ScalarValue Value( T value ) where T : IConvertible => new( value ); + + public static ScalarValue True { get; } = new( true ); + public static ScalarValue False { get; } = new( false ); + + public static Null Null { get; } = new(); + public static Nothing Nothing { get; } = new(); +} diff --git a/src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs b/src/Hyperbee.Json/Filters/Values/ValueKind.cs similarity index 64% rename from src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs rename to src/Hyperbee.Json/Filters/Values/ValueKind.cs index 5e401893..86186191 100644 --- a/src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs +++ b/src/Hyperbee.Json/Filters/Values/ValueKind.cs @@ -1,10 +1,9 @@ namespace Hyperbee.Json.Filters.Values; -public enum NodeTypeKind +public enum ValueKind { Null, Nothing, - Value, - Node, + Scalar, NodeList } diff --git a/src/Hyperbee.Json/Filters/Values/ValueType.cs b/src/Hyperbee.Json/Filters/Values/ValueType.cs deleted file mode 100644 index 8cba9a7d..00000000 --- a/src/Hyperbee.Json/Filters/Values/ValueType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public struct ValueType( T value ) : INodeType -{ - public readonly NodeTypeKind Kind => NodeTypeKind.Value; - - public INodeTypeComparer Comparer { get; set; } - - public T Value { get; } = value; -} diff --git a/src/Hyperbee.Json/JsonPathQueryParser.cs b/src/Hyperbee.Json/JsonPathQueryParser.cs index 79f3c6f5..9647d2ab 100644 --- a/src/Hyperbee.Json/JsonPathQueryParser.cs +++ b/src/Hyperbee.Json/JsonPathQueryParser.cs @@ -49,11 +49,6 @@ internal static JsonPathQuery Parse( string query, bool allowDotWhitespace = fal return JsonPathQueries.GetOrAdd( query, x => QueryFactory( x.AsSpan(), allowDotWhitespace ) ); } - internal static JsonPathQuery ParseNoCache( ReadOnlySpan query, bool allowDotWhitespace = false ) - { - return QueryFactory( query, allowDotWhitespace ); - } - private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowDotWhitespace = false ) { // RFC - query cannot start or end with whitespace @@ -450,6 +445,9 @@ private static JsonPathQuery BuildJsonPathQuery( ReadOnlySpan query, IList return new JsonPathQuery( query.ToString(), rootSegment, normalized ); } + internal static void ClearCache() => JsonPathQueries.Clear(); + + [MethodImpl( MethodImplOptions.AggressiveInlining )] private static SelectorDescriptor GetSelectorDescriptor( SelectorKind selectorKind, ReadOnlySpan selectorSpan, bool nullable = true ) { @@ -776,7 +774,7 @@ static bool IsValidUnicodeEscapeSequence( ReadOnlySpan span ) if ( span.Length != 6 || span[1] != 'u' ) return false; - for ( int i = 2; i < 6; i++ ) + for ( var i = 2; i < 6; i++ ) { if ( !char.IsAsciiHexDigit( span[i] ) ) return false; diff --git a/src/Hyperbee.Json/JsonPathSegment.cs b/src/Hyperbee.Json/JsonPathSegment.cs index f78422df..4d44d524 100644 --- a/src/Hyperbee.Json/JsonPathSegment.cs +++ b/src/Hyperbee.Json/JsonPathSegment.cs @@ -4,7 +4,6 @@ namespace Hyperbee.Json; public record JsonPathQuery( string Query, JsonPathSegment Segments, bool Normalized ); - [DebuggerDisplay( "{Value}, SelectorKind = {SelectorKind}" )] public record SelectorDescriptor { diff --git a/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs index d3287966..c44b1882 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs @@ -1153,7 +1153,7 @@ public void Test_less_than_null_31() } ] """ ); - var results = document.Select( selector ); + var results = document.Select( selector ).ToArray(); var expect = JsonNode.Parse( """ [] @@ -3031,7 +3031,7 @@ public void Test_equals_number__with_and_without_decimal_fraction_110() } ] """ ); - var results = document.Select( selector ); + var results = document.Select( selector ).ToArray(); var expect = JsonNode.Parse( """ [ diff --git a/test/Hyperbee.Json.Cts/generate_tests.ps1 b/test/Hyperbee.Json.Cts/generate_tests.ps1 index 9db4fdff..f98bd5ed 100644 --- a/test/Hyperbee.Json.Cts/generate_tests.ps1 +++ b/test/Hyperbee.Json.Cts/generate_tests.ps1 @@ -43,7 +43,7 @@ function Get-JsonContent { $response = Invoke-WebRequestWithRetry -Url $Url $jsonContent = $response.Content - # Save the JSON content to a file in a pretty formatted way if SavePath is provided + # Save the JSON content to a file if path was provided if ($PSBoundParameters.ContainsKey('LocalPath')) { $prettyJson = $jsonContent | ConvertFrom-Json -AsHashtable | ConvertTo-Json -Depth 10 Set-Content -Path $LocalPath -Value $prettyJson diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs index 0f6a058e..32160c74 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Reflection; using System.Text.Json.Nodes; +using Hyperbee.Json.Descriptors.Element; using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; @@ -36,15 +37,9 @@ private class PathNodeFunction() : FilterExtensionFunction( PathMethodInfo, Filt public const string Name = "path"; private static readonly MethodInfo PathMethodInfo = GetMethod( nameof( Path ) ); - private static INodeType Path( INodeType arg ) + private static ScalarValue Path( IValueType argument ) { - if ( arg is NodesType nodes ) - { - var node = nodes.FirstOrDefault(); - return new ValueType( node?.GetPath() ); - } - - return Constants.Null; + return argument.TryGetNode( out var node ) ? node?.GetPath() : null; } } } diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs index e4b2e46e..90ee0241 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs @@ -120,8 +120,8 @@ public void Should_ReturnExpectedResult_WhenUsingExpressionEvaluator( string fil } [DataTestMethod] - [DataRow( "count(@.store.book) == 1", true, typeof( JsonElement ) )] - [DataRow( "count(@.store.book.*) == 4", true, typeof( JsonElement ) )] + [DataRow( "count(@.store.book) == 1", true, typeof( JsonElement ) )] // + [DataRow( "count(@.store.book.*) == 4", true, typeof( JsonElement ) )] // [DataRow( "length(@.store.book) == 4", true, typeof( JsonElement ) )] [DataRow( "length(@.store.book[0].category) == 9", true, typeof( JsonElement ) )] [DataRow( "match(@.store.book[0].title, \"Sayings.*\" )", true, typeof( JsonElement ) )] diff --git a/test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs b/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs similarity index 89% rename from test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs rename to test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs index 9bfe5c8d..f9799c52 100644 --- a/test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs @@ -122,8 +122,8 @@ public void NodeTypeComparer_ShouldCompare_WithRightJsonArray( object left, stri public void NodeTypeComparer_ShouldCompare_WithEmpty() { var comparer = GetComparer(); - var a = new NodesType( [], true ); - var b = new ValueType( 1F ); + var a = new NodeList( [], true ); + var b = new ScalarValue( 1F ); Assert.IsFalse( comparer.Compare( a, b, Operator.LessThan ) < 0 ); Assert.IsFalse( comparer.Compare( a, b, Operator.LessThanOrEqual ) <= 0 ); @@ -135,15 +135,15 @@ public void NodeTypeComparer_ShouldCompare_WithEmpty() Assert.IsTrue( comparer.Compare( a, b, Operator.NotEquals ) != 0 ); } - private static NodeTypeComparer GetComparer() => new( new NodeValueAccessor() ); + private static ValueTypeComparer GetComparer() => new( new NodeValueAccessor() ); - private static INodeType GetNodeType( object item ) => + private static IValueType GetNodeType( object item ) => item switch { - string itemString => new ValueType( itemString ), - float itemFloat => new ValueType( itemFloat ), - bool itemBool => new ValueType( itemBool ), - IEnumerable nodes => new NodesType( nodes, true ), - _ => Constants.Nothing + string itemString => new ScalarValue( itemString ), + float itemFloat => new ScalarValue( itemFloat ), + bool itemBool => new ScalarValue( itemBool ), + IEnumerable nodes => new NodeList( nodes, true ), + _ => Scalar.Nothing }; } From 252a26454ff8a934c30284aaba27c42772724aa5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 17:22:18 -0700 Subject: [PATCH 06/18] [FEATURE]: Improve FilterParser ExpressionFactory (#43) * Remove filter runtime context * Add quick outs to expression factories --------- Co-authored-by: Brenton Farmer --- README.md | 4 +- .../Element/Functions/CountElementFunction.cs | 2 +- .../Functions/LengthElementFunction.cs | 2 +- .../Element/Functions/MatchElementFunction.cs | 2 +- .../Functions/SearchElementFunction.cs | 2 +- .../Element/Functions/ValueElementFunction.cs | 2 +- .../Descriptors/FunctionRegistry.cs | 2 +- .../Descriptors/ITypeDescriptor.cs | 2 +- .../Node/Functions/CountNodeFunction.cs | 2 +- .../Node/Functions/LengthNodeFunction.cs | 2 +- .../Node/Functions/MatchNodeFunction.cs | 2 +- .../Node/Functions/SearchNodeFunction.cs | 2 +- .../Node/Functions/ValueNodeFunction.cs | 2 +- src/Hyperbee.Json/Filters/FilterEvaluator.cs | 15 ++-- .../Filters/Parser/ExpressionInfo.cs | 2 +- .../Expressions/FunctionExpressionFactory.cs | 33 ++++--- .../Parser/Expressions/IExpressionFactory.cs | 3 +- .../Expressions/JsonExpressionFactory.cs | 15 ++-- .../Expressions/LiteralExpressionFactory.cs | 9 +- .../Expressions/NotExpressionFactory.cs | 5 +- .../Expressions/ParenExpressionFactory.cs | 25 +++--- .../Expressions/SelectExpressionFactory.cs | 38 +++++---- ...ruthyExpression.cs => TruthyExpression.cs} | 15 +++- .../Filters/Parser/ExtensionFunction.cs | 85 +++++++++++++++++++ ...ilterExtensionInfo.cs => ExtensionInfo.cs} | 2 +- .../Filters/Parser/FilterExtensionFunction.cs | 84 ------------------ .../Filters/Parser/FilterParser.cs | 66 +++++++------- .../Filters/Parser/FilterParserContext.cs | 12 --- .../Filters/Parser/ValueTypeComparer.cs | 8 +- .../FilterExpressionParserEvaluator.cs | 14 +-- .../Hyperbee.Json.Cts.csproj | 18 +++- .../Hyperbee.Json.Tests.csproj | 2 +- ...tionTests.cs => ExtensionFunctionTests.cs} | 6 +- .../Parsers/FilterParserTests.cs | 11 ++- 34 files changed, 257 insertions(+), 239 deletions(-) rename src/Hyperbee.Json/Filters/Parser/Expressions/{FilterTruthyExpression.cs => TruthyExpression.cs} (63%) create mode 100644 src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs rename src/Hyperbee.Json/Filters/Parser/{FilterExtensionInfo.cs => ExtensionInfo.cs} (80%) delete mode 100644 src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs rename test/Hyperbee.Json.Tests/Parsers/{FilterExtensionFunctionTests.cs => ExtensionFunctionTests.cs} (80%) diff --git a/README.md b/README.md index 6bb250c8..42730235 100644 --- a/README.md +++ b/README.md @@ -193,10 +193,10 @@ You can also extend the supported function set by registering your own functions **Step 1:** Create a custom function that returns the path of a `JsonNode`. ```csharp -private class PathNodeFunction() : FilterExtensionFunction( PathMethodInfo, FilterExtensionInfo.MustCompare ) +public class PathNodeFunction() : ExtensionFunction( PathMethod, ExtensionInfo.MustCompare ) { public const string Name = "path"; - private static readonly MethodInfo PathMethodInfo = GetMethod( nameof( Path ) ); + private static readonly MethodInfo PathMethod = GetMethod( nameof( Path ) ); private static ScalarValue Path( IValueType argument ) { diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs index 349f983c..ccc3d29f 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class CountElementFunction() : FilterExtensionFunction( CountMethod, FilterExtensionInfo.MustCompare ) +public class CountElementFunction() : ExtensionFunction( CountMethod, ExtensionInfo.MustCompare ) { public const string Name = "count"; private static readonly MethodInfo CountMethod = GetMethod( nameof( Count ) ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs index ded983bc..acccdddb 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class LengthElementFunction() : FilterExtensionFunction( LengthMethod, FilterExtensionInfo.MustCompare | FilterExtensionInfo.ExpectNormalized ) +public class LengthElementFunction() : ExtensionFunction( LengthMethod, ExtensionInfo.MustCompare | ExtensionInfo.ExpectNormalized ) { public const string Name = "length"; private static readonly MethodInfo LengthMethod = GetMethod( nameof( Length ) ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs index e1148332..56921b42 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class MatchElementFunction() : FilterExtensionFunction( MatchMethod, FilterExtensionInfo.MustNotCompare ) +public class MatchElementFunction() : ExtensionFunction( MatchMethod, ExtensionInfo.MustNotCompare ) { public const string Name = "match"; private static readonly MethodInfo MatchMethod = GetMethod( nameof( Match ) ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs index 1b812cfd..9930a6e7 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs @@ -7,7 +7,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class SearchElementFunction() : FilterExtensionFunction( SearchMethod, FilterExtensionInfo.MustNotCompare ) +public class SearchElementFunction() : ExtensionFunction( SearchMethod, ExtensionInfo.MustNotCompare ) { public const string Name = "search"; private static readonly MethodInfo SearchMethod = GetMethod( nameof( Search ) ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs index f41e7fc3..6f185d3b 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class ValueElementFunction() : FilterExtensionFunction( ValueMethod, FilterExtensionInfo.MustCompare ) +public class ValueElementFunction() : ExtensionFunction( ValueMethod, ExtensionInfo.MustCompare ) { public const string Name = "value"; private static readonly MethodInfo ValueMethod = GetMethod( nameof( Value ) ); diff --git a/src/Hyperbee.Json/Descriptors/FunctionRegistry.cs b/src/Hyperbee.Json/Descriptors/FunctionRegistry.cs index df8fcfc4..4a06fbe7 100644 --- a/src/Hyperbee.Json/Descriptors/FunctionRegistry.cs +++ b/src/Hyperbee.Json/Descriptors/FunctionRegistry.cs @@ -7,7 +7,7 @@ public sealed class FunctionRegistry private Dictionary Functions { get; } = []; public void Register( string name, Func factory ) - where TFunction : FilterExtensionFunction + where TFunction : ExtensionFunction { Functions[name] = () => factory(); } diff --git a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs index 7f21175e..1fd6fda7 100644 --- a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs @@ -3,7 +3,7 @@ namespace Hyperbee.Json.Descriptors; -public delegate FilterExtensionFunction FunctionActivator(); +public delegate ExtensionFunction FunctionActivator(); public interface ITypeDescriptor { diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs index 5bd3f212..c6cdb547 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class CountNodeFunction() : FilterExtensionFunction( CountMethod, FilterExtensionInfo.MustCompare ) +public class CountNodeFunction() : ExtensionFunction( CountMethod, ExtensionInfo.MustCompare ) { public const string Name = "count"; private static readonly MethodInfo CountMethod = GetMethod( nameof( Count ) ); diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs index f924a23c..aeff2ead 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class LengthNodeFunction() : FilterExtensionFunction( LengthMethod, FilterExtensionInfo.MustCompare | FilterExtensionInfo.ExpectNormalized ) +public class LengthNodeFunction() : ExtensionFunction( LengthMethod, ExtensionInfo.MustCompare | ExtensionInfo.ExpectNormalized ) { public const string Name = "length"; private static readonly MethodInfo LengthMethod = GetMethod( nameof( Length ) ); diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs index 5612611f..5621750d 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class MatchNodeFunction() : FilterExtensionFunction( MatchMethod, FilterExtensionInfo.MustNotCompare ) +public class MatchNodeFunction() : ExtensionFunction( MatchMethod, ExtensionInfo.MustNotCompare ) { public const string Name = "match"; private static readonly MethodInfo MatchMethod = GetMethod( nameof( Match ) ); diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs index 632c05a8..b933d424 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class SearchNodeFunction() : FilterExtensionFunction( SearchMethod, FilterExtensionInfo.MustNotCompare ) +public class SearchNodeFunction() : ExtensionFunction( SearchMethod, ExtensionInfo.MustNotCompare ) { public const string Name = "search"; private static readonly MethodInfo SearchMethod = GetMethod( nameof( Search ) ); diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs index 05571780..f3d78674 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs @@ -7,7 +7,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class ValueNodeFunction() : FilterExtensionFunction( ValueMethod, FilterExtensionInfo.MustCompare ) +public class ValueNodeFunction() : ExtensionFunction( ValueMethod, ExtensionInfo.MustCompare ) { public const string Name = "value"; private static readonly MethodInfo ValueMethod = GetMethod( nameof( Value ) ); diff --git a/src/Hyperbee.Json/Filters/FilterEvaluator.cs b/src/Hyperbee.Json/Filters/FilterEvaluator.cs index 146b6c4f..60b5ccbc 100644 --- a/src/Hyperbee.Json/Filters/FilterEvaluator.cs +++ b/src/Hyperbee.Json/Filters/FilterEvaluator.cs @@ -5,25 +5,22 @@ namespace Hyperbee.Json.Filters; -public sealed class FilterEvaluator : IFilterEvaluator +public record FilterRuntimeContext( TNode Current, TNode Root, ITypeDescriptor Descriptor ); + +public sealed class FilterEvaluator( ITypeDescriptor descriptor ) : IFilterEvaluator { private static readonly ConcurrentDictionary, bool>> Compiled = new(); - private readonly ITypeDescriptor _typeDescriptor; - - public FilterEvaluator( ITypeDescriptor typeDescriptor ) - { - _typeDescriptor = typeDescriptor; - } + public ITypeDescriptor Descriptor { get; } = descriptor; public bool Evaluate( string filter, TNode current, TNode root ) { // Feature: split type descriptor into design/parse and runtime. (functions and json parsing are design time) - var compiled = Compiled.GetOrAdd( filter, _ => FilterParser.Compile( filter, _typeDescriptor ) ); + var compiled = Compiled.GetOrAdd( filter, _ => FilterParser.Compile( filter, Descriptor ) ); try { - var runtimeContext = new FilterRuntimeContext( current, root, _typeDescriptor ); + var runtimeContext = new FilterRuntimeContext( current, root, Descriptor ); return compiled( runtimeContext ); } catch ( RuntimeBinderException ) diff --git a/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs b/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs index 0d494ad5..c490b80f 100644 --- a/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs +++ b/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs @@ -3,5 +3,5 @@ internal class ExpressionInfo { public ExpressionKind Kind { get; set; } - public FilterExtensionInfo FunctionInfo { get; set; } + public ExtensionInfo FunctionInfo { get; set; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs index 0d8220dd..87bc1a71 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs @@ -1,27 +1,34 @@ using System.Linq.Expressions; +using Hyperbee.Json.Descriptors; namespace Hyperbee.Json.Filters.Parser.Expressions; internal class FunctionExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) { - if ( parserContext.Descriptor.Functions.TryGetActivator( state.Item.ToString(), out var functionActivator ) ) + if ( state.Item.IsEmpty || !char.IsLetter( state.Item[0] ) ) { - if ( state.TrailingWhitespace ) - throw new NotSupportedException( "Whitespace is not allowed after a function name." ); + expression = null; + return false; + } - var function = functionActivator(); + if ( !descriptor.Functions.TryGetActivator( state.Item.ToString(), out var functionActivator ) ) + { + expression = null; + return false; + } - expression = function - .GetExpression( ref state, parserContext ); // will recurse for each function argument. + if ( state.TrailingWhitespace ) + throw new NotSupportedException( "Whitespace is not allowed after a function name." ); - expressionInfo.Kind = ExpressionKind.Function; - expressionInfo.FunctionInfo = function.FunctionInfo; - return true; - } + var function = functionActivator(); + + expression = function + .GetExpression( ref state, descriptor ); // will recurse for each function argument. - expression = null; - return false; + exprInfo.Kind = ExpressionKind.Function; + exprInfo.FunctionInfo = function.FunctionInfo; + return true; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs index f7535ad1..17e66dd4 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs @@ -1,8 +1,9 @@ using System.Linq.Expressions; +using Hyperbee.Json.Descriptors; namespace Hyperbee.Json.Filters.Parser.Expressions; internal interface IExpressionFactory { - static abstract bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ); + static abstract bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ); } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs index 6c0c90ee..6d44a689 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs @@ -1,20 +1,21 @@ using System.Linq.Expressions; +using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser.Expressions; internal class JsonExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) { - if ( parserContext.Descriptor.Accessor.TryParseNode( state.Item.ToString(), out var node ) ) + if ( !descriptor.Accessor.TryParseNode( state.Item.ToString(), out var node ) ) { - expression = Expression.Constant( new NodeList( [node], isNormalized: true ) ); - expressionInfo.Kind = ExpressionKind.Json; - return true; + expression = null; + return false; } - expression = null; - return false; + expression = Expression.Constant( new NodeList( [node], isNormalized: true ) ); + exprInfo.Kind = ExpressionKind.Json; + return true; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs index fe71e3e4..81a41b25 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs @@ -1,20 +1,20 @@ using System.Linq.Expressions; +using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser.Expressions; internal class LiteralExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) { expression = GetLiteralExpression( state.Item ); if ( expression == null ) return false; - expressionInfo.Kind = ExpressionKind.Literal; + exprInfo.Kind = ExpressionKind.Literal; return true; - } private static ConstantExpression GetLiteralExpression( ReadOnlySpan item ) @@ -36,9 +36,6 @@ private static ConstantExpression GetLiteralExpression( ReadOnlySpan item return Expression.Constant( Scalar.Value( item[1..^1].ToString() ) ); // remove quotes // Check for numbers - // - // The current design treats all numbers are floats since we don't - // know what's in the data or the other side of the operator yet. if ( int.TryParse( item, out int intResult ) ) return Expression.Constant( Scalar.Value( intResult ) ); diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs index 72594ee5..32f4e430 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs @@ -1,17 +1,18 @@ using System.Linq.Expressions; +using Hyperbee.Json.Descriptors; namespace Hyperbee.Json.Filters.Parser.Expressions; internal class NotExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) { expression = null; if ( state.Operator != Operator.Not ) return false; - expressionInfo.Kind = ExpressionKind.Not; + exprInfo.Kind = ExpressionKind.Not; return true; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs index ea6e671d..080e22fc 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs @@ -1,24 +1,25 @@ using System.Linq.Expressions; +using Hyperbee.Json.Descriptors; namespace Hyperbee.Json.Filters.Parser.Expressions; internal class ParenExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo expressionInfo, FilterParserContext parserContext ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) { - if ( state.Operator == Operator.OpenParen && state.Item.IsEmpty ) + if ( state.Operator != Operator.OpenParen || !state.Item.IsEmpty ) { - var localState = state with - { - Terminal = FilterParser.ArgClose - }; - - expression = FilterParser.Parse( ref localState, parserContext ); // will recurse. - expressionInfo.Kind = ExpressionKind.Paren; - return true; + expression = null; + return false; } - expression = null; - return false; + var localState = state with + { + Terminal = FilterParser.ArgClose + }; + + expression = FilterParser.Parse( ref localState, descriptor ); // will recurse. + exprInfo.Kind = ExpressionKind.Paren; + return true; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs index fba81be2..12165a9e 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs @@ -1,49 +1,53 @@ using System.Linq.Expressions; using System.Reflection; +using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser.Expressions; internal class SelectExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo itemContext, FilterParserContext parserContext ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) { - expression = ExpressionHelper.GetExpression( state.Item, state.IsArgument, parserContext ); + var item = state.Item; - if ( expression == null ) + if ( item.IsEmpty || item[0] != '$' && item[0] != '@' ) + { + expression = null; return false; + } - itemContext.Kind = ExpressionKind.Select; + expression = ExpressionHelper.GetExpression( state.Item, state.IsArgument ); + exprInfo.Kind = ExpressionKind.Select; return true; } private static class ExpressionHelper { - private static readonly MethodInfo SelectMethod = typeof( ExpressionHelper ) - .GetMethod( nameof( Select ), BindingFlags.NonPublic | BindingFlags.Static ); + private static readonly MethodInfo SelectMethod = + typeof( ExpressionHelper ) + .GetMethod( nameof( Select ), BindingFlags.NonPublic | BindingFlags.Static ); - public static MethodCallExpression GetExpression( ReadOnlySpan item, bool allowDotWhitespace, FilterParserContext parserContext ) + public static MethodCallExpression GetExpression( ReadOnlySpan item, bool allowDotWhitespace ) { - if ( item.IsEmpty ) - return null; - - if ( item[0] != '$' && item[0] != '@' ) - return null; - return Expression.Call( SelectMethod, Expression.Constant( item.ToString() ), Expression.Constant( allowDotWhitespace ), - parserContext.RuntimeContext ); + FilterParser.RuntimeContextExpression ); } private static IValueType Select( string query, bool allowDotWhitespace, FilterRuntimeContext runtimeContext ) { - var compileQuery = JsonPathQueryParser.Parse( query, allowDotWhitespace ); + var compiledQuery = JsonPathQueryParser.Parse( query, allowDotWhitespace ); + + var value = query[0] == '$' + ? runtimeContext.Root + : runtimeContext.Current; // @ - var value = query[0] == '$' ? runtimeContext.Root : runtimeContext.Current; + var nodes = JsonPath.SelectInternal( value, runtimeContext.Root, compiledQuery ); - return new NodeList( JsonPath.SelectInternal( value, runtimeContext.Root, compileQuery ), compileQuery.Normalized ); + return new NodeList( nodes, compiledQuery.Normalized ); } } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/FilterTruthyExpression.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs similarity index 63% rename from src/Hyperbee.Json/Filters/Parser/Expressions/FilterTruthyExpression.cs rename to src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs index 02592781..36926a00 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/FilterTruthyExpression.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs @@ -5,16 +5,16 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; -public static class FilterTruthyExpression +public static class TruthyExpression { - private static readonly MethodInfo IsTruthyMethod = typeof( FilterTruthyExpression ).GetMethod( nameof( IsTruthy ), BindingFlags.NonPublic | BindingFlags.Static ); + private static readonly MethodInfo IsTruthyMethod = typeof( TruthyExpression ).GetMethod( nameof( IsTruthy ), BindingFlags.NonPublic | BindingFlags.Static ); public static Expression IsTruthyExpression( Expression expression ) => expression.Type == typeof( bool ) ? expression : Expression.Call( IsTruthyMethod, expression ); - private static bool IsTruthy( object value ) + private static bool IsTruthy( IValueType value ) { var truthy = value switch { @@ -24,10 +24,17 @@ private static bool IsTruthy( object value ) ScalarValue intValue => intValue.Value != 0, ScalarValue floatValue => floatValue.Value != 0, ScalarValue valueString => !string.IsNullOrEmpty( valueString.Value ) && !valueString.Value.Equals( "false", StringComparison.OrdinalIgnoreCase ), - IEnumerable enumerable => enumerable.Cast().Any(), // NodeList + IEnumerable enumerable => Any( enumerable ), // NodeList _ => true }; return truthy; + + static bool Any( IEnumerable enumerable ) // Avoid cast to object: enumerable.Cast().Any() + { + var enumerator = enumerable.GetEnumerator(); + using var disposable = enumerator as IDisposable; + return enumerator.MoveNext(); + } } } diff --git a/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs b/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs new file mode 100644 index 00000000..faa2c7e4 --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs @@ -0,0 +1,85 @@ +using System.Linq.Expressions; +using System.Reflection; +using Hyperbee.Json.Descriptors; +using Hyperbee.Json.Filters.Values; + +namespace Hyperbee.Json.Filters.Parser; + +public abstract class ExtensionFunction +{ + private readonly int _argumentCount; + private readonly MethodInfo _methodInfo; + + public ExtensionInfo FunctionInfo { get; } + + protected ExtensionFunction( MethodInfo methodInfo, ExtensionInfo info ) + { + _argumentCount = methodInfo.GetParameters().Length; + _methodInfo = methodInfo; + + FunctionInfo = info; + } + + internal Expression GetExpression( ref ParserState state, ITypeDescriptor descriptor ) + { + var arguments = new Expression[_argumentCount]; + var expectNormalized = FunctionInfo.HasFlag( ExtensionInfo.ExpectNormalized ); + + for ( var i = 0; i < _argumentCount; i++ ) + { + var localState = state with + { + Item = [], + IsArgument = true, + Terminal = i == _argumentCount - 1 + ? FilterParser.ArgClose + : FilterParser.ArgComma + }; + + if ( localState.EndOfBuffer ) + throw new NotSupportedException( $"Invalid arguments for filter: \"{state.Buffer}\"." ); + + var argument = FilterParser.Parse( ref localState, descriptor ); + arguments[i] = ArgumentExpression( expectNormalized, argument ); + } + + // Call the method and cast the result to support covariant returns + var callExpression = Expression.Call( _methodInfo, arguments ); + var castExpression = Expression.Convert( callExpression, typeof( IValueType ) ); + + return castExpression; + } + + private Expression ArgumentExpression( bool expectNormalized, Expression argument ) + { + if ( expectNormalized ) + { + // Create expression that throws if not normalized. + return Expression.Call( + ExpressionHelper.ValidateArgumentMethod, + Expression.Constant( _methodInfo.Name ), + Expression.Convert( argument, typeof( IValueType ) ) + ); + } + + return Expression.Convert( argument, typeof( IValueType ) ); + } + + protected static MethodInfo GetMethod( string methodName ) => + typeof( T ).GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ); + + private static class ExpressionHelper // helper removes need for MakeGenericMethod + { + public static readonly MethodInfo ValidateArgumentMethod = + typeof( ExpressionHelper ) + .GetMethod( nameof( ValidateArgument ), BindingFlags.NonPublic | BindingFlags.Static ); + + private static IValueType ValidateArgument( string methodName, IValueType argument ) + { + if ( argument is NodeList { IsNormalized: false } ) + throw new NotSupportedException( $"Function {methodName} does not support non-singular arguments." ); + + return argument; + } + } +} diff --git a/src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs b/src/Hyperbee.Json/Filters/Parser/ExtensionInfo.cs similarity index 80% rename from src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs rename to src/Hyperbee.Json/Filters/Parser/ExtensionInfo.cs index 394a6f9e..794bfc87 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs +++ b/src/Hyperbee.Json/Filters/Parser/ExtensionInfo.cs @@ -1,7 +1,7 @@ namespace Hyperbee.Json.Filters.Parser; [Flags] -public enum FilterExtensionInfo +public enum ExtensionInfo { MustCompare = 0x01, MustNotCompare = 0x02, diff --git a/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs b/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs deleted file mode 100644 index fbc9aaf1..00000000 --- a/src/Hyperbee.Json/Filters/Parser/FilterExtensionFunction.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Linq.Expressions; -using System.Reflection; -using Hyperbee.Json.Filters.Values; - -namespace Hyperbee.Json.Filters.Parser -{ - public abstract class FilterExtensionFunction - { - private readonly int _argumentCount; - private readonly MethodInfo _methodInfo; - - public FilterExtensionInfo FunctionInfo { get; } - - protected FilterExtensionFunction( MethodInfo methodInfo, FilterExtensionInfo filterInfo ) - { - _argumentCount = methodInfo.GetParameters().Length; - _methodInfo = methodInfo; - - FunctionInfo = filterInfo; - } - - internal Expression GetExpression( ref ParserState state, FilterParserContext parserContext ) - { - var arguments = new Expression[_argumentCount]; - var expectNormalized = FunctionInfo.HasFlag( FilterExtensionInfo.ExpectNormalized ); - - for ( var i = 0; i < _argumentCount; i++ ) - { - var localState = state with - { - Item = [], - IsArgument = true, - Terminal = i == _argumentCount - 1 - ? FilterParser.ArgClose - : FilterParser.ArgComma - }; - - if ( localState.EndOfBuffer ) - throw new NotSupportedException( $"Invalid arguments for filter: \"{state.Buffer}\"." ); - - var argument = FilterParser.Parse( ref localState, parserContext ); - arguments[i] = ArgumentExpression( expectNormalized, argument ); - } - - // Call the method and cast the result to INodeType - var callExpression = Expression.Call( _methodInfo, arguments ); - var castExpression = Expression.Convert( callExpression, typeof( IValueType ) ); - - return castExpression; - } - - private Expression ArgumentExpression( bool expectNormalized, Expression argument ) - { - if ( expectNormalized ) - { - // Create expression that throws if not normalized. - return Expression.Call( - ExpressionHelper.ValidateArgumentMethod, - Expression.Constant( _methodInfo.Name ), - Expression.Convert( argument, typeof( IValueType ) ) - ); - } - - return Expression.Convert( argument, typeof( IValueType ) ); - } - - protected static MethodInfo GetMethod( string methodName ) => - typeof( T ).GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ); - - private static class ExpressionHelper // this helper removes need for MakeGenericMethod - { - public static readonly MethodInfo ValidateArgumentMethod = - typeof( ExpressionHelper ).GetMethod( nameof( ValidateArgument ), BindingFlags.NonPublic | BindingFlags.Static ); - - static IValueType ValidateArgument( string methodName, IValueType argument ) - { - if ( argument is NodeList { IsNormalized: false } ) - throw new NotSupportedException( $"Function {methodName} does not support non-singular arguments." ); - - return argument; - } - } - } -} diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index c8dd9d6b..d6dce345 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -26,16 +26,16 @@ public abstract class FilterParser public class FilterParser : FilterParser { + // use a common instance + internal static readonly ParameterExpression RuntimeContextExpression = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); + public static Func, bool> Compile( ReadOnlySpan filter, ITypeDescriptor descriptor ) { - var context = new FilterParserContext( descriptor ); - - var expression = Parse( filter, context ); - - return Expression.Lambda, bool>>( expression, context.RuntimeContext ).Compile(); + var expression = Parse( filter, descriptor ); + return Expression.Lambda, bool>>( expression, RuntimeContextExpression ).Compile(); } - internal static Expression Parse( ReadOnlySpan filter, FilterParserContext parserContext ) + internal static Expression Parse( ReadOnlySpan filter, ITypeDescriptor descriptor ) { filter = filter.Trim(); // remove leading and trailing whitespace to simplify parsing @@ -43,16 +43,16 @@ internal static Expression Parse( ReadOnlySpan filter, FilterParserContext var parenDepth = 0; var state = new ParserState( filter, [], ref pos, ref parenDepth, Operator.NonOperator, EndLine ); - var expression = Parse( ref state, parserContext ); + var expression = Parse( ref state, descriptor ); - return FilterTruthyExpression.IsTruthyExpression( expression ); + return TruthyExpression.IsTruthyExpression( expression ); } - internal static Expression Parse( ref ParserState state, FilterParserContext parserContext ) // recursion entrypoint + internal static Expression Parse( ref ParserState state, ITypeDescriptor descriptor ) // recursion entrypoint { // validate input - if ( parserContext == null ) - throw new ArgumentNullException( nameof( parserContext ) ); + if ( descriptor == null ) + throw new ArgumentNullException( nameof( descriptor ) ); if ( state.EndOfBuffer ) throw new NotSupportedException( $"Invalid filter: \"{state.Buffer}\"." ); @@ -63,7 +63,7 @@ internal static Expression Parse( ref ParserState state, FilterParserContext parserContext ) + private static ExprItem GetExprItem( ref ParserState state, ITypeDescriptor descriptor ) { var expressionInfo = new ExpressionInfo(); - if ( NotExpressionFactory.TryGetExpression( ref state, out var expression, ref expressionInfo, parserContext ) ) + if ( NotExpressionFactory.TryGetExpression( ref state, out var expression, ref expressionInfo, descriptor ) ) return ExprItem( ref state, expression, expressionInfo ); - if ( ParenExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) // will recurse. + if ( ParenExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) // will recurse. return ExprItem( ref state, expression, expressionInfo ); - if ( SelectExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) + if ( SelectExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) return ExprItem( ref state, expression, expressionInfo ); - if ( FunctionExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) // may recurse for each function argument. + if ( FunctionExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) // may recurse for each function argument. return ExprItem( ref state, expression, expressionInfo ); - if ( LiteralExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) + if ( LiteralExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) return ExprItem( ref state, expression, expressionInfo ); - if ( JsonExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, parserContext ) ) + if ( JsonExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) return ExprItem( ref state, expression, expressionInfo ); throw new NotSupportedException( $"Unsupported literal: {state.Buffer.ToString()}" ); @@ -278,7 +278,7 @@ static bool Next( ref ParserState state, char expected ) } } - private static Expression Merge( in ParserState state, ExprItem left, ref int index, List items, FilterParserContext parserContext, bool mergeOneOnly = false ) + private static Expression Merge( in ParserState state, ExprItem left, ref int index, List items, ITypeDescriptor descriptor, bool mergeOneOnly = false ) { if ( items.Count == 1 ) { @@ -292,12 +292,12 @@ private static Expression Merge( in ParserState state, ExprItem left, ref int in while ( !CanMergeItems( left, right ) ) { - Merge( in state, right, ref index, items, parserContext, mergeOneOnly: true ); // recursive call - right becomes left + Merge( in state, right, ref index, items, descriptor, mergeOneOnly: true ); // recursive call - right becomes left } ThrowIfInvalidComparison( in state, left, right ); - MergeItems( left, right, parserContext ); + MergeItems( left, right, descriptor ); if ( mergeOneOnly ) return left.Expression; @@ -332,10 +332,10 @@ Operator.LessThan or } } - private static void MergeItems( ExprItem left, ExprItem right, FilterParserContext parserContext ) + private static void MergeItems( ExprItem left, ExprItem right, ITypeDescriptor descriptor ) { - left.Expression = BindComparerExpression( parserContext, left.Expression ); - right.Expression = BindComparerExpression( parserContext, right.Expression ); + left.Expression = BindComparerExpression( descriptor, left.Expression ); + right.Expression = BindComparerExpression( descriptor, right.Expression ); left.Expression = left.Operator switch { @@ -370,11 +370,11 @@ static Expression ConvertBoolToScalarExpression( Expression leftExpression ) return conditionalExpression; } - static Expression BindComparerExpression( FilterParserContext parserContext, Expression expression ) + static Expression BindComparerExpression( ITypeDescriptor descriptor, Expression expression ) { // Create an Expression that does: // - // static IValueType BindComparerExpression(FilterParserContext parserContext, IValueType value) + // static IValueType BindComparerExpression(ITypeDescriptor descriptor, IValueType value) // { // value.Comparer = parserContext.Descriptor.Comparer; // return value; @@ -391,7 +391,7 @@ static Expression BindComparerExpression( FilterParserContext parserConte var comparerAssign = Expression.Assign( Expression.PropertyOrField( valueVariable, "Comparer" ), - Expression.Constant( parserContext.Descriptor.Comparer, typeof( IValueTypeComparer ) ) + Expression.Constant( descriptor.Comparer, typeof( IValueTypeComparer ) ) ); return Expression.Block( @@ -419,13 +419,13 @@ private static void ThrowIfFunctionInvalidCompare( in ParserState state, ExprIte if ( item.ExpressionInfo.Kind != ExpressionKind.Function ) return; - if ( (item.ExpressionInfo.FunctionInfo & FilterExtensionInfo.MustCompare) == FilterExtensionInfo.MustCompare && + if ( (item.ExpressionInfo.FunctionInfo & ExtensionInfo.MustCompare) == ExtensionInfo.MustCompare && !item.Operator.IsComparison() ) { throw new NotSupportedException( $"Function must compare: {state.Buffer.ToString()}." ); } - if ( (item.ExpressionInfo.FunctionInfo & FilterExtensionInfo.MustNotCompare) == FilterExtensionInfo.MustNotCompare && + if ( (item.ExpressionInfo.FunctionInfo & ExtensionInfo.MustNotCompare) == ExtensionInfo.MustNotCompare && item.Operator.IsComparison() ) { throw new NotSupportedException( $"Function must not compare: {state.Buffer.ToString()}." ); @@ -446,8 +446,8 @@ private static void ThrowIfConstantIsNotCompared( in ParserState state, ExprItem // ExprItem - [DebuggerDisplay( "Operator = {Operator}, {ExpressionInfo.Kind}" )] - private class ExprItem( Expression expression, Operator op, ExpressionInfo expressionInfo ) + [DebuggerDisplay( "{ExpressionInfo.Kind}, Operator = {Operator}" )] + private sealed class ExprItem( Expression expression, Operator op, ExpressionInfo expressionInfo ) { public ExpressionInfo ExpressionInfo { get; } = expressionInfo; public Expression Expression { get; set; } = expression; diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs b/src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs deleted file mode 100644 index 286cdb58..00000000 --- a/src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Json.Descriptors; - -namespace Hyperbee.Json.Filters.Parser; - -internal record FilterParserContext( ITypeDescriptor Descriptor ) -{ - public ParameterExpression RuntimeContext { get; init; } = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); -} - - -public record FilterRuntimeContext( TNode Current, TNode Root, ITypeDescriptor Descriptor ); diff --git a/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs b/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs index 45abfa42..c6301fd6 100644 --- a/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs +++ b/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs @@ -238,10 +238,10 @@ private static bool TryGetValueType( IValueAccessor accessor, TNode node, { nodeType = itemValue switch { - string itemString => new ScalarValue( itemString ), - bool itemBool => new ScalarValue( itemBool ), - float itemFloat => new ScalarValue( itemFloat ), - int itemInt => new ScalarValue( itemInt ), + string itemString => Scalar.Value( itemString ), + bool itemBool => Scalar.Value( itemBool ), + float itemFloat => Scalar.Value( itemFloat ), + int itemInt => Scalar.Value( itemInt ), null => Scalar.Null, _ => throw new NotSupportedException( "Unsupported value type." ) }; diff --git a/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs b/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs index b2df8b6f..cc2a2ba7 100644 --- a/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs +++ b/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs @@ -1,6 +1,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using BenchmarkDotNet.Attributes; +using Hyperbee.Json.Descriptors; using Hyperbee.Json.Descriptors.Element; using Hyperbee.Json.Descriptors.Node; using Hyperbee.Json.Filters.Parser; @@ -9,8 +10,8 @@ namespace Hyperbee.Json.Benchmark; public class FilterExpressionParserEvaluator { - private FilterParserContext _nodeExecutionParserContext; - private FilterParserContext _elementExecutionParserContext; + private ITypeDescriptor _nodeTypeDescriptor; + private ITypeDescriptor _elementTypeDescriptor; [Params( "(\"world\" == 'world') && (true || false)" )] public string Filter; @@ -18,20 +19,19 @@ public class FilterExpressionParserEvaluator [GlobalSetup] public void Setup() { - _nodeExecutionParserContext = new FilterParserContext( new NodeTypeDescriptor() ); - - _elementExecutionParserContext = new FilterParserContext( new ElementTypeDescriptor() ); + _nodeTypeDescriptor = new NodeTypeDescriptor(); + _elementTypeDescriptor = new ElementTypeDescriptor(); } [Benchmark] public void JsonPathFilterParser_JsonElement() { - FilterParser.Parse( Filter, _elementExecutionParserContext ); + FilterParser.Parse( Filter, _elementTypeDescriptor ); } [Benchmark] public void JsonPathFilterParser_JsonNode() { - FilterParser.Parse( Filter, _nodeExecutionParserContext ); + FilterParser.Parse( Filter, _nodeTypeDescriptor ); } } diff --git a/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj b/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj index 5448cc6f..9a9750fe 100644 --- a/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj +++ b/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj @@ -10,10 +10,13 @@ - - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + @@ -24,4 +27,11 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/test/Hyperbee.Json.Tests/Hyperbee.Json.Tests.csproj b/test/Hyperbee.Json.Tests/Hyperbee.Json.Tests.csproj index 660bd272..98dc3fa6 100644 --- a/test/Hyperbee.Json.Tests/Hyperbee.Json.Tests.csproj +++ b/test/Hyperbee.Json.Tests/Hyperbee.Json.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs b/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs similarity index 80% rename from test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs rename to test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs index 32160c74..905d8f67 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterExtensionFunctionTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs @@ -11,7 +11,7 @@ namespace Hyperbee.Json.Tests.Parsers; [TestClass] -public class FilterExtensionFunctionTests : JsonTestBase +public class ExtensionFunctionTests : JsonTestBase { [TestMethod] public void Should_CallCustomFunction() @@ -32,10 +32,10 @@ public void Should_CallCustomFunction() Assert.AreEqual( "$.store.book[2].title", results[0].GetPath() ); } - private class PathNodeFunction() : FilterExtensionFunction( PathMethodInfo, FilterExtensionInfo.MustCompare ) + private class PathNodeFunction() : ExtensionFunction( PathMethod, ExtensionInfo.MustCompare ) { public const string Name = "path"; - private static readonly MethodInfo PathMethodInfo = GetMethod( nameof( Path ) ); + private static readonly MethodInfo PathMethod = GetMethod( nameof( Path ) ); private static ScalarValue Path( IValueType argument ) { diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs index 90ee0241..d7e7590a 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs @@ -6,6 +6,7 @@ using Hyperbee.Json.Descriptors.Element; using Hyperbee.Json.Descriptors.Node; using Hyperbee.Json.Extensions; +using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Tests.TestSupport; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -198,12 +199,14 @@ private static (Expression, ParameterExpression) GetExpression( string filter, T { if ( sourceType == typeof( JsonElement ) ) { - var elementContext = new FilterParserContext( new ElementTypeDescriptor() ); - return (FilterParser.Parse( filter, elementContext ), elementContext.RuntimeContext); + var elementDescriptor = new ElementTypeDescriptor(); + var elementRuntimeContext = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); + return (FilterParser.Parse( filter, elementDescriptor ), elementRuntimeContext); } - var nodeContext = new FilterParserContext( new NodeTypeDescriptor() ); - return (FilterParser.Parse( filter, nodeContext ), nodeContext.RuntimeContext); + var nodeDescriptor = new NodeTypeDescriptor(); + var nodeRuntimeContext = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); + return (FilterParser.Parse( filter, nodeDescriptor ), nodeRuntimeContext); } private static bool Execute( Expression expression, ParameterExpression param, Type sourceType ) From e426f856c4f88c1e297fdae0f620934cdd573e47 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:25:08 -0700 Subject: [PATCH 07/18] [FEATURE]: Refactor Expression, Comparer and Descriptor usage in Filter Parser (#45) * Remove filter runtime context * Remove Descriptor from filter parse argument list and all related methods * Cleaned up filter extension methods --------- Co-authored-by: Brenton Farmer --- .../Element/ElementTypeDescriptor.cs | 2 +- .../Functions/LengthElementFunction.cs | 24 ++-- .../Element/Functions/MatchElementFunction.cs | 8 +- .../Functions/SearchElementFunction.cs | 9 +- .../Element/Functions/ValueElementFunction.cs | 2 +- .../Node/Functions/LengthNodeFunction.cs | 25 ++-- .../Node/Functions/MatchNodeFunction.cs | 8 +- .../Node/Functions/SearchNodeFunction.cs | 8 +- .../Node/Functions/ValueNodeFunction.cs | 2 +- .../Descriptors/Node/NodeTypeDescriptor.cs | 2 +- src/Hyperbee.Json/Filters/FilterEvaluator.cs | 12 +- .../Parser/Expressions/CompareExpression.cs | 116 +++++++++++------ .../Expressions/FunctionExpressionFactory.cs | 2 +- .../Expressions/LiteralExpressionFactory.cs | 2 +- .../Expressions/NotExpressionFactory.cs | 2 +- .../Expressions/ParenExpressionFactory.cs | 4 +- .../Expressions/SelectExpressionFactory.cs | 2 +- .../Parser/Expressions/TruthyExpression.cs | 6 +- .../Filters/Parser/ExtensionFunction.cs | 5 +- .../Filters/Parser/FilterParser.cs | 117 +++++++----------- .../Filters/Values/IValueType.cs | 14 +-- src/Hyperbee.Json/Filters/Values/NodeList.cs | 13 +- .../Filters/Values/ScalarValue.cs | 20 ++- src/Hyperbee.Json/JsonPathQueryParser.cs | 4 +- .../FilterExpressionParserEvaluator.cs | 17 +-- ....cs => JsonPathParseAndSelectEvaluator.cs} | 2 +- .../Parsers/FilterParserTests.cs | 66 +++++----- .../Parsers/ValueTypeComparerTests.cs | 12 +- 28 files changed, 242 insertions(+), 264 deletions(-) rename test/Hyperbee.Json.Benchmark/{JsonPathParseAndSelect.cs => JsonPathParseAndSelectEvaluator.cs} (98%) diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs index 2d132809..c5d1445f 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs @@ -17,7 +17,7 @@ public class ElementTypeDescriptor : ITypeDescriptor _accessor ??= new ElementValueAccessor(); public IFilterEvaluator FilterEvaluator => - _evaluator ??= new FilterEvaluator( this ); + _evaluator ??= new FilterEvaluator(); public IValueTypeComparer Comparer => _comparer ??= new ValueTypeComparer( Accessor ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs index acccdddb..7615f1fb 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs @@ -1,6 +1,5 @@ using System.Reflection; using System.Text.Json; -using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; @@ -13,20 +12,19 @@ public class LengthElementFunction() : ExtensionFunction( LengthMethod, Extensio public static IValueType Length( IValueType argument ) { - if ( argument.TryGetValue( out var stringValue ) ) + switch ( argument.ValueKind ) { - return new ScalarValue( stringValue.Length ); - } + case ValueKind.Scalar when argument.TryGetValue( out var value ): + return Scalar.Value( value.Length ); - if ( argument.TryGetNode( out var node ) ) - { - return node.ValueKind switch - { - JsonValueKind.String => new ScalarValue( node.GetString()?.Length ?? 0 ), - JsonValueKind.Array => new ScalarValue( node.GetArrayLength() ), - JsonValueKind.Object => new ScalarValue( node.EnumerateObject().Count() ), - _ => Scalar.Nothing - }; + case ValueKind.NodeList when argument.TryGetNode( out var node ): + return node.ValueKind switch + { + JsonValueKind.String => Scalar.Value( node.GetString()?.Length ?? 0 ), + JsonValueKind.Array => Scalar.Value( node.GetArrayLength() ), + JsonValueKind.Object => Scalar.Value( node.EnumerateObject().Count() ), + _ => Scalar.Nothing + }; } return Scalar.Nothing; diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs index 56921b42..37833a74 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs @@ -11,15 +11,15 @@ public class MatchElementFunction() : ExtensionFunction( MatchMethod, ExtensionI public const string Name = "match"; private static readonly MethodInfo MatchMethod = GetMethod( nameof( Match ) ); - public static ScalarValue Match( IValueType input, IValueType pattern ) + public static ScalarValue Match( IValueType argValue, IValueType argPattern ) { - if ( !input.TryGetValue( out var value ) || value == null ) + if ( !argValue.TryGetValue( out var value ) || value == null ) return false; - if ( !pattern.TryGetValue( out var patternValue ) || patternValue == null ) + if ( !argPattern.TryGetValue( out var pattern ) || pattern == null ) return false; - var regex = new Regex( $"^{IRegexp.ConvertToIRegexp( patternValue )}$" ); + var regex = new Regex( $"^{IRegexp.ConvertToIRegexp( pattern )}$" ); return regex.IsMatch( value ); } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs index 9930a6e7..8aeb5055 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Text.Json; using System.Text.RegularExpressions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; @@ -12,15 +11,15 @@ public class SearchElementFunction() : ExtensionFunction( SearchMethod, Extensio public const string Name = "search"; private static readonly MethodInfo SearchMethod = GetMethod( nameof( Search ) ); - public static ScalarValue Search( IValueType input, IValueType pattern ) + public static ScalarValue Search( IValueType argValue, IValueType argPattern ) { - if ( !input.TryGetValue( out var value ) || value == null ) + if ( !argValue.TryGetValue( out var value ) || value == null ) return false; - if ( !pattern.TryGetValue( out var patternValue ) || patternValue == null ) + if ( !argPattern.TryGetValue( out var pattern ) || pattern == null ) return false; - var regex = new Regex( IRegexp.ConvertToIRegexp( patternValue ) ); + var regex = new Regex( IRegexp.ConvertToIRegexp( pattern ) ); return regex.IsMatch( value ); } } diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs index 6f185d3b..a31621bd 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs @@ -20,7 +20,7 @@ public static IValueType Value( IValueType argument ) return node.ValueKind switch { - JsonValueKind.Number when node.TryGetInt32( out var intValue ) => Scalar.Value( intValue ), + JsonValueKind.Number when node.TryGetInt32( out var value ) => Scalar.Value( value ), JsonValueKind.Number => Scalar.Value( node.GetSingle() ), JsonValueKind.String => Scalar.Value( node.GetString() ), JsonValueKind.Object => Scalar.Value( node.EnumerateObject().Any() ), diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs index aeff2ead..f247961c 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs @@ -11,20 +11,21 @@ public class LengthNodeFunction() : ExtensionFunction( LengthMethod, ExtensionIn public const string Name = "length"; private static readonly MethodInfo LengthMethod = GetMethod( nameof( Length ) ); - public static IValueType Length( IValueType input ) + public static IValueType Length( IValueType argument ) { - if ( input.TryGetValue( out var stringValue ) ) - return Scalar.Value( stringValue.Length ); - - if ( input.TryGetNode( out var node ) ) + switch ( argument.ValueKind ) { - return node?.GetValueKind() switch - { - JsonValueKind.String => Scalar.Value( node.GetValue()?.Length ?? 0 ), - JsonValueKind.Array => Scalar.Value( node.AsArray().Count ), - JsonValueKind.Object => Scalar.Value( node.AsObject().Count ), - _ => Scalar.Nothing - }; + case ValueKind.Scalar when argument.TryGetValue( out var value ): + return Scalar.Value( value.Length ); + + case ValueKind.NodeList when argument.TryGetNode( out var node ): + return node?.GetValueKind() switch + { + JsonValueKind.String => Scalar.Value( node.GetValue()?.Length ?? 0 ), + JsonValueKind.Array => Scalar.Value( node.AsArray().Count ), + JsonValueKind.Object => Scalar.Value( node.AsObject().Count ), + _ => Scalar.Nothing + }; } return Scalar.Nothing; diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs index 5621750d..9bb561f2 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs @@ -11,15 +11,15 @@ public class MatchNodeFunction() : ExtensionFunction( MatchMethod, ExtensionInfo public const string Name = "match"; private static readonly MethodInfo MatchMethod = GetMethod( nameof( Match ) ); - public static ScalarValue Match( IValueType input, IValueType pattern ) + public static ScalarValue Match( IValueType argValue, IValueType argPattern ) { - if ( !input.TryGetValue( out var value ) || value == null ) + if ( !argValue.TryGetValue( out var value ) || value == null ) return false; - if ( !pattern.TryGetValue( out var patternValue ) || patternValue == null ) + if ( !argPattern.TryGetValue( out var pattern ) || pattern == null ) return false; - var regex = new Regex( $"^{IRegexp.ConvertToIRegexp( patternValue )}$" ); + var regex = new Regex( $"^{IRegexp.ConvertToIRegexp( pattern )}$" ); return regex.IsMatch( value ); } } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs index b933d424..1baa28f1 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs @@ -11,15 +11,15 @@ public class SearchNodeFunction() : ExtensionFunction( SearchMethod, ExtensionIn public const string Name = "search"; private static readonly MethodInfo SearchMethod = GetMethod( nameof( Search ) ); - public static ScalarValue Search( IValueType input, IValueType pattern ) + public static ScalarValue Search( IValueType argValue, IValueType argPattern ) { - if ( !input.TryGetValue( out var value ) || value == null ) + if ( !argValue.TryGetValue( out var value ) || value == null ) return false; - if ( !pattern.TryGetValue( out var patternValue ) || patternValue == null ) + if ( !argPattern.TryGetValue( out var pattern ) || pattern == null ) return false; - var regex = new Regex( IRegexp.ConvertToIRegexp( patternValue ) ); + var regex = new Regex( IRegexp.ConvertToIRegexp( pattern ) ); return regex.IsMatch( value ); } } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs index f3d78674..38b9740e 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs @@ -21,7 +21,7 @@ public static IValueType Value( IValueType argument ) return node?.GetValueKind() switch { - JsonValueKind.Number when node.AsValue().TryGetValue( out var intValue ) => Scalar.Value( intValue ), + JsonValueKind.Number when node.AsValue().TryGetValue( out var value ) => Scalar.Value( value ), JsonValueKind.Number => Scalar.Value( node.GetValue() ), JsonValueKind.String => Scalar.Value( node.GetValue() ), JsonValueKind.Object => Scalar.Value( node.AsObject().Count != 0 ), diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs index c23336db..af756abd 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs @@ -17,7 +17,7 @@ public class NodeTypeDescriptor : ITypeDescriptor _accessor ??= new NodeValueAccessor(); public IFilterEvaluator FilterEvaluator => - _evaluator ??= new FilterEvaluator( this ); + _evaluator ??= new FilterEvaluator(); public IValueTypeComparer Comparer => _comparer ??= new ValueTypeComparer( Accessor ); diff --git a/src/Hyperbee.Json/Filters/FilterEvaluator.cs b/src/Hyperbee.Json/Filters/FilterEvaluator.cs index 60b5ccbc..6ab85d73 100644 --- a/src/Hyperbee.Json/Filters/FilterEvaluator.cs +++ b/src/Hyperbee.Json/Filters/FilterEvaluator.cs @@ -1,26 +1,22 @@ using System.Collections.Concurrent; -using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Parser; using Microsoft.CSharp.RuntimeBinder; namespace Hyperbee.Json.Filters; -public record FilterRuntimeContext( TNode Current, TNode Root, ITypeDescriptor Descriptor ); +public record FilterRuntimeContext( TNode Current, TNode Root ); -public sealed class FilterEvaluator( ITypeDescriptor descriptor ) : IFilterEvaluator +public sealed class FilterEvaluator : IFilterEvaluator { private static readonly ConcurrentDictionary, bool>> Compiled = new(); - public ITypeDescriptor Descriptor { get; } = descriptor; - public bool Evaluate( string filter, TNode current, TNode root ) { - // Feature: split type descriptor into design/parse and runtime. (functions and json parsing are design time) - var compiled = Compiled.GetOrAdd( filter, _ => FilterParser.Compile( filter, Descriptor ) ); + var compiled = Compiled.GetOrAdd( filter, _ => FilterParser.Compile( filter ) ); try { - var runtimeContext = new FilterRuntimeContext( current, root, Descriptor ); + var runtimeContext = new FilterRuntimeContext( current, root ); return compiled( runtimeContext ); } catch ( RuntimeBinderException ) diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs index 0e76ca43..acf9c336 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs @@ -6,56 +6,102 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; public static class CompareExpression { - private static readonly MethodInfo AreEqualMethod = typeof( CompareExpression ).GetMethod( nameof( AreEqual ) ); - private static readonly MethodInfo AreNotEqualMethod = typeof( CompareExpression ).GetMethod( nameof( AreNotEqual ) ); - private static readonly MethodInfo IsLessThanMethod = typeof( CompareExpression ).GetMethod( nameof( IsLessThan ) ); - private static readonly MethodInfo IsLessThanOrEqualMethod = typeof( CompareExpression ).GetMethod( nameof( IsLessThanOrEqual ) ); - private static readonly MethodInfo IsGreaterThanMethod = typeof( CompareExpression ).GetMethod( nameof( IsGreaterThan ) ); - private static readonly MethodInfo IsGreaterThanOrEqualMethod = typeof( CompareExpression ).GetMethod( nameof( IsGreaterThanOrEqual ) ); - - private static readonly MethodInfo AndAlsoMethod = typeof( CompareExpression ).GetMethod( nameof( AndAlso ) ); - private static readonly MethodInfo OrElseMethod = typeof( CompareExpression ).GetMethod( nameof( OrElse ) ); - private static readonly MethodInfo NotMethod = typeof( CompareExpression ).GetMethod( nameof( NotBoolean ) ); - - public static Expression Equal( Expression left, Expression right ) => Expression.Call( AreEqualMethod, left, right ); - public static Expression NotEqual( Expression left, Expression right ) => Expression.Call( AreNotEqualMethod, left, right ); - public static Expression LessThan( Expression left, Expression right ) => Expression.Call( IsLessThanMethod, left, right ); - public static Expression LessThanOrEqual( Expression left, Expression right ) => Expression.Call( IsLessThanOrEqualMethod, left, right ); - public static Expression GreaterThan( Expression left, Expression right ) => Expression.Call( IsGreaterThanMethod, left, right ); - public static Expression GreaterThanOrEqual( Expression left, Expression right ) => Expression.Call( IsGreaterThanOrEqualMethod, left, right ); - - public static Expression And( Expression left, Expression right ) => Expression.Call( AndAlsoMethod, left, right ); - public static Expression Or( Expression left, Expression right ) => Expression.Call( OrElseMethod, left, right ); - public static Expression Not( Expression expression ) => Expression.Call( NotMethod, expression ); - - public static bool AreEqual( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.Equals ) == 0; - public static bool AreNotEqual( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.NotEquals ) != 0; - public static bool IsLessThan( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.LessThan ) < 0; - public static bool IsLessThanOrEqual( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.LessThanOrEqual ) <= 0; - public static bool IsGreaterThan( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.GreaterThan ) > 0; - public static bool IsGreaterThanOrEqual( IValueType left, IValueType right ) => left.Comparer.Compare( left, right, Operator.GreaterThanOrEqual ) >= 0; - - public static bool AndAlso( IValueType left, IValueType right ) + // Expressions + + public static Expression Equal( Expression left, Expression right, Expression comparer ) + => Expression.Call( AreEqualMethod, left, right, comparer ); + + public static Expression NotEqual( Expression left, Expression right, Expression comparer ) + => Expression.Call( AreNotEqualMethod, left, right, comparer ); + + public static Expression LessThan( Expression left, Expression right, Expression comparer ) + => Expression.Call( IsLessThanMethod, left, right, comparer ); + + public static Expression LessThanOrEqual( Expression left, Expression right, Expression comparer ) + => Expression.Call( IsLessThanOrEqualMethod, left, right, comparer ); + + public static Expression GreaterThan( Expression left, Expression right, Expression comparer ) + => Expression.Call( IsGreaterThanMethod, left, right, comparer ); + + public static Expression GreaterThanOrEqual( Expression left, Expression right, Expression comparer ) + => Expression.Call( IsGreaterThanOrEqualMethod, left, right, comparer ); + + public static Expression And( Expression left, Expression right, Expression comparer ) + => Expression.Call( AndAlsoMethod, left, right, comparer ); + + public static Expression Or( Expression left, Expression right, Expression comparer ) + => Expression.Call( OrElseMethod, left, right, comparer ); + + public static Expression Not( Expression expression, Expression comparer ) + => Expression.Call( NotMethod, expression, comparer ); + + // MethodInfo + + private const BindingFlags BindingAttr = BindingFlags.Static | BindingFlags.NonPublic; + + private static readonly MethodInfo AreEqualMethod = typeof( CompareExpression ).GetMethod( nameof( AreEqual ), BindingAttr ); + private static readonly MethodInfo AreNotEqualMethod = typeof( CompareExpression ).GetMethod( nameof( AreNotEqual ), BindingAttr ); + private static readonly MethodInfo IsLessThanMethod = typeof( CompareExpression ).GetMethod( nameof( IsLessThan ), BindingAttr ); + private static readonly MethodInfo IsLessThanOrEqualMethod = typeof( CompareExpression ).GetMethod( nameof( IsLessThanOrEqual ), BindingAttr ); + private static readonly MethodInfo IsGreaterThanMethod = typeof( CompareExpression ).GetMethod( nameof( IsGreaterThan ), BindingAttr ); + private static readonly MethodInfo IsGreaterThanOrEqualMethod = typeof( CompareExpression ).GetMethod( nameof( IsGreaterThanOrEqual ), BindingAttr ); + private static readonly MethodInfo AndAlsoMethod = typeof( CompareExpression ).GetMethod( nameof( AndAlso ), BindingAttr ); + private static readonly MethodInfo OrElseMethod = typeof( CompareExpression ).GetMethod( nameof( OrElse ), BindingAttr ); + private static readonly MethodInfo NotMethod = typeof( CompareExpression ).GetMethod( nameof( NotBoolean ), BindingAttr ); + + // Methods + + private static bool AreEqual( IValueType left, IValueType right, IValueTypeComparer comparer ) + { + return comparer.Compare( left, right, Operator.Equals ) == 0; + } + + private static bool AreNotEqual( IValueType left, IValueType right, IValueTypeComparer comparer ) + { + return comparer.Compare( left, right, Operator.NotEquals ) != 0; + } + + private static bool IsLessThan( IValueType left, IValueType right, IValueTypeComparer comparer ) + { + return comparer.Compare( left, right, Operator.LessThan ) < 0; + } + + private static bool IsLessThanOrEqual( IValueType left, IValueType right, IValueTypeComparer comparer ) + { + return comparer.Compare( left, right, Operator.LessThanOrEqual ) <= 0; + } + + private static bool IsGreaterThan( IValueType left, IValueType right, IValueTypeComparer comparer ) + { + return comparer.Compare( left, right, Operator.GreaterThan ) > 0; + } + + private static bool IsGreaterThanOrEqual( IValueType left, IValueType right, IValueTypeComparer comparer ) + { + return comparer.Compare( left, right, Operator.GreaterThanOrEqual ) >= 0; + } + + private static bool AndAlso( IValueType left, IValueType right, IValueTypeComparer comparer ) { if ( left is ScalarValue leftBoolValue && right is ScalarValue rightBoolValue ) return leftBoolValue.Value && rightBoolValue.Value; - return left.Comparer.Exists( left ) && right.Comparer.Exists( right ); + return comparer.Exists( left ) && comparer.Exists( right ); } - public static bool OrElse( IValueType left, IValueType right ) + private static bool OrElse( IValueType left, IValueType right, IValueTypeComparer comparer ) { if ( left is ScalarValue leftBoolValue && right is ScalarValue rightBoolValue ) return leftBoolValue.Value || rightBoolValue.Value; - return left.Comparer.Exists( left ) || right.Comparer.Exists( right ); + return comparer.Exists( left ) || comparer.Exists( right ); } - public static bool NotBoolean( IValueType value ) + private static bool NotBoolean( IValueType value, IValueTypeComparer comparer ) { if ( value is ScalarValue { Value: false } ) return true; - return !value.Comparer.Exists( value ); + return !comparer.Exists( value ); } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs index 87bc1a71..54facac2 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs @@ -25,7 +25,7 @@ public static bool TryGetExpression( ref ParserState state, out Expressio var function = functionActivator(); expression = function - .GetExpression( ref state, descriptor ); // will recurse for each function argument. + .GetExpression( ref state ); // will recurse for each function argument. exprInfo.Kind = ExpressionKind.Function; exprInfo.FunctionInfo = function.FunctionInfo; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs index 81a41b25..ca121b44 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class LiteralExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor _ = null ) { expression = GetLiteralExpression( state.Item ); diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs index 32f4e430..67767f3b 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class NotExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor _ = null ) { expression = null; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs index 080e22fc..df16c2a5 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class ParenExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor _ = null ) { if ( state.Operator != Operator.OpenParen || !state.Item.IsEmpty ) { @@ -18,7 +18,7 @@ public static bool TryGetExpression( ref ParserState state, out Expressio Terminal = FilterParser.ArgClose }; - expression = FilterParser.Parse( ref localState, descriptor ); // will recurse. + expression = FilterParser.Parse( ref localState ); // will recurse. exprInfo.Kind = ExpressionKind.Paren; return true; } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs index 12165a9e..300aba59 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs @@ -7,7 +7,7 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class SelectExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor _ = null ) { var item = state.Item; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs index 36926a00..16937c8b 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs @@ -9,10 +9,12 @@ public static class TruthyExpression { private static readonly MethodInfo IsTruthyMethod = typeof( TruthyExpression ).GetMethod( nameof( IsTruthy ), BindingFlags.NonPublic | BindingFlags.Static ); - public static Expression IsTruthyExpression( Expression expression ) => - expression.Type == typeof( bool ) + public static Expression IsTruthyExpression( Expression expression ) + { + return expression.Type == typeof( bool ) ? expression : Expression.Call( IsTruthyMethod, expression ); + } private static bool IsTruthy( IValueType value ) { diff --git a/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs b/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs index faa2c7e4..7064fcdd 100644 --- a/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs +++ b/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs @@ -1,6 +1,5 @@ using System.Linq.Expressions; using System.Reflection; -using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser; @@ -20,7 +19,7 @@ protected ExtensionFunction( MethodInfo methodInfo, ExtensionInfo info ) FunctionInfo = info; } - internal Expression GetExpression( ref ParserState state, ITypeDescriptor descriptor ) + internal Expression GetExpression( ref ParserState state ) { var arguments = new Expression[_argumentCount]; var expectNormalized = FunctionInfo.HasFlag( ExtensionInfo.ExpectNormalized ); @@ -39,7 +38,7 @@ internal Expression GetExpression( ref ParserState state, ITypeDescriptor if ( localState.EndOfBuffer ) throw new NotSupportedException( $"Invalid arguments for filter: \"{state.Buffer}\"." ); - var argument = FilterParser.Parse( ref localState, descriptor ); + var argument = FilterParser.Parse( ref localState ); arguments[i] = ArgumentExpression( expectNormalized, argument ); } diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index d6dce345..e77e73b3 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -28,14 +28,16 @@ public class FilterParser : FilterParser { // use a common instance internal static readonly ParameterExpression RuntimeContextExpression = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); + internal static readonly ITypeDescriptor Descriptor = JsonTypeDescriptorRegistry.GetDescriptor(); - public static Func, bool> Compile( ReadOnlySpan filter, ITypeDescriptor descriptor ) + public static Func, bool> Compile( ReadOnlySpan filter ) { - var expression = Parse( filter, descriptor ); + var expression = Parse( filter ); + return Expression.Lambda, bool>>( expression, RuntimeContextExpression ).Compile(); } - internal static Expression Parse( ReadOnlySpan filter, ITypeDescriptor descriptor ) + internal static Expression Parse( ReadOnlySpan filter ) { filter = filter.Trim(); // remove leading and trailing whitespace to simplify parsing @@ -43,17 +45,13 @@ internal static Expression Parse( ReadOnlySpan filter, ITypeDescriptor descriptor ) // recursion entrypoint + internal static Expression Parse( ref ParserState state ) // recursion entrypoint { - // validate input - if ( descriptor == null ) - throw new ArgumentNullException( nameof( descriptor ) ); - if ( state.EndOfBuffer ) throw new NotSupportedException( $"Invalid filter: \"{state.Buffer}\"." ); @@ -63,7 +61,7 @@ internal static Expression Parse( ref ParserState state, ITypeDescriptor do { MoveNext( ref state ); - items.Add( GetExprItem( ref state, descriptor ) ); // will recurse for nested expressions + items.Add( GetExprItem( ref state ) ); // will recurse for nested expressions } while ( state.IsParsing ); @@ -75,30 +73,30 @@ internal static Expression Parse( ref ParserState state, ITypeDescriptor var baseItem = items[0]; var index = 1; - return Merge( in state, baseItem, ref index, items, descriptor ); + return Merge( in state, baseItem, ref index, items ); } - private static ExprItem GetExprItem( ref ParserState state, ITypeDescriptor descriptor ) + private static ExprItem GetExprItem( ref ParserState state ) { var expressionInfo = new ExpressionInfo(); - if ( NotExpressionFactory.TryGetExpression( ref state, out var expression, ref expressionInfo, descriptor ) ) + if ( NotExpressionFactory.TryGetExpression( ref state, out var expression, ref expressionInfo ) ) return ExprItem( ref state, expression, expressionInfo ); - if ( ParenExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) // will recurse. + if ( ParenExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) // will recurse. return ExprItem( ref state, expression, expressionInfo ); - if ( SelectExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) + if ( SelectExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) return ExprItem( ref state, expression, expressionInfo ); - if ( FunctionExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) // may recurse for each function argument. + if ( FunctionExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, Descriptor ) ) // may recurse for each function argument. return ExprItem( ref state, expression, expressionInfo ); - if ( LiteralExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) + if ( LiteralExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) return ExprItem( ref state, expression, expressionInfo ); - if ( JsonExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, descriptor ) ) + if ( JsonExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, Descriptor ) ) return ExprItem( ref state, expression, expressionInfo ); throw new NotSupportedException( $"Unsupported literal: {state.Buffer.ToString()}" ); @@ -278,11 +276,11 @@ static bool Next( ref ParserState state, char expected ) } } - private static Expression Merge( in ParserState state, ExprItem left, ref int index, List items, ITypeDescriptor descriptor, bool mergeOneOnly = false ) + private static Expression Merge( in ParserState state, ExprItem left, ref int index, List items, bool mergeOneOnly = false ) { if ( items.Count == 1 ) { - ThrowIfInvalidComparison( in state, left, null ); // single item, no recursion + ThrowIfInvalidCompare( in state, left, null ); // single item, no recursion } else { @@ -292,12 +290,12 @@ private static Expression Merge( in ParserState state, ExprItem left, ref int in while ( !CanMergeItems( left, right ) ) { - Merge( in state, right, ref index, items, descriptor, mergeOneOnly: true ); // recursive call - right becomes left + Merge( in state, right, ref index, items, mergeOneOnly: true ); // recursive call - right becomes left } - ThrowIfInvalidComparison( in state, left, right ); + ThrowIfInvalidCompare( in state, left, right ); - MergeItems( left, right, descriptor ); + MergeItems( left, right ); if ( mergeOneOnly ) return left.Expression; @@ -332,80 +330,49 @@ Operator.LessThan or } } - private static void MergeItems( ExprItem left, ExprItem right, ITypeDescriptor descriptor ) + private static void MergeItems( ExprItem left, ExprItem right ) { - left.Expression = BindComparerExpression( descriptor, left.Expression ); - right.Expression = BindComparerExpression( descriptor, right.Expression ); + left.Expression = ConvertExpression( left.Expression ); + right.Expression = ConvertExpression( right.Expression ); + + var comparer = Expression.Constant( Descriptor.Comparer, typeof( IValueTypeComparer ) ); left.Expression = left.Operator switch { - Operator.Equals => CompareExpression.Equal( left.Expression, right.Expression ), - Operator.NotEquals => CompareExpression.NotEqual( left.Expression, right.Expression ), - Operator.GreaterThan => CompareExpression.GreaterThan( left.Expression, right.Expression ), - Operator.GreaterThanOrEqual => CompareExpression.GreaterThanOrEqual( left.Expression, right.Expression ), - Operator.LessThan => CompareExpression.LessThan( left.Expression, right.Expression ), - Operator.LessThanOrEqual => CompareExpression.LessThanOrEqual( left.Expression, right.Expression ), - Operator.And => CompareExpression.And( left.Expression, right.Expression ), - Operator.Or => CompareExpression.Or( left.Expression, right.Expression ), - Operator.Not => CompareExpression.Not( right.Expression ), + Operator.Equals => CompareExpression.Equal( left.Expression, right.Expression, comparer ), + Operator.NotEquals => CompareExpression.NotEqual( left.Expression, right.Expression, comparer ), + Operator.GreaterThan => CompareExpression.GreaterThan( left.Expression, right.Expression, comparer ), + Operator.GreaterThanOrEqual => CompareExpression.GreaterThanOrEqual( left.Expression, right.Expression, comparer ), + Operator.LessThan => CompareExpression.LessThan( left.Expression, right.Expression, comparer ), + Operator.LessThanOrEqual => CompareExpression.LessThanOrEqual( left.Expression, right.Expression, comparer ), + Operator.And => CompareExpression.And( left.Expression, right.Expression, comparer ), + Operator.Or => CompareExpression.Or( left.Expression, right.Expression, comparer ), + Operator.Not => CompareExpression.Not( right.Expression, comparer ), _ => throw new InvalidOperationException( $"Invalid operator {left.Operator}" ) }; - left.Expression = ConvertBoolToScalarExpression( left.Expression ); + left.Expression = ConvertBoolToValueTypeExpression( left.Expression ); left.Operator = right.Operator; left.ExpressionInfo.Kind = ExpressionKind.Merged; return; - static Expression ConvertBoolToScalarExpression( Expression leftExpression ) + static Expression ConvertBoolToValueTypeExpression( Expression leftExpression ) { - // convert bool result to Scalar.True or Scalar.False - Expression conditionalExpression = Expression.Condition( - leftExpression, - Expression.Constant( Scalar.True, typeof( IValueType ) ), - Expression.Constant( Scalar.False, typeof( IValueType ) ) - ); - - return conditionalExpression; + // Convert to ScalarValue using implicit operator and then return as a IValueType + return ConvertExpression( ConvertExpression>( leftExpression ) ); } - static Expression BindComparerExpression( ITypeDescriptor descriptor, Expression expression ) + static Expression ConvertExpression( Expression expression ) { - // Create an Expression that does: - // - // static IValueType BindComparerExpression(ITypeDescriptor descriptor, IValueType value) - // { - // value.Comparer = parserContext.Descriptor.Comparer; - // return value; - // } - - if ( expression == null ) - return null; - - var valueVariable = Expression.Variable( typeof( IValueType ), "value" ); - - var valueAssign = Expression.Assign( - valueVariable, - Expression.Convert( expression, typeof( IValueType ) ) ); - - var comparerAssign = Expression.Assign( - Expression.PropertyOrField( valueVariable, "Comparer" ), - Expression.Constant( descriptor.Comparer, typeof( IValueTypeComparer ) ) - ); - - return Expression.Block( - [valueVariable], - valueAssign, - comparerAssign, - valueVariable - ); + return expression == null ? null : Expression.Convert( expression, typeof( TType ) ); } } // Throw helpers - private static void ThrowIfInvalidComparison( in ParserState state, ExprItem left, ExprItem right ) + private static void ThrowIfInvalidCompare( in ParserState state, ExprItem left, ExprItem right ) { ThrowIfConstantIsNotCompared( in state, left, right ); ThrowIfFunctionInvalidCompare( in state, left ); diff --git a/src/Hyperbee.Json/Filters/Values/IValueType.cs b/src/Hyperbee.Json/Filters/Values/IValueType.cs index a375c6f1..0edf82b8 100644 --- a/src/Hyperbee.Json/Filters/Values/IValueType.cs +++ b/src/Hyperbee.Json/Filters/Values/IValueType.cs @@ -1,21 +1,17 @@ -using Hyperbee.Json.Filters.Parser; - + namespace Hyperbee.Json.Filters.Values; public interface IValueType { public ValueKind ValueKind { get; } - public IValueTypeComparer Comparer { get; set; } } -public struct Null : IValueType +public readonly struct Null : IValueType { - public readonly ValueKind ValueKind => ValueKind.Null; - public IValueTypeComparer Comparer { get; set; } + public ValueKind ValueKind => ValueKind.Null; } -public struct Nothing : IValueType +public readonly struct Nothing : IValueType { - public readonly ValueKind ValueKind => ValueKind.Nothing; - public IValueTypeComparer Comparer { get; set; } + public ValueKind ValueKind => ValueKind.Nothing; } diff --git a/src/Hyperbee.Json/Filters/Values/NodeList.cs b/src/Hyperbee.Json/Filters/Values/NodeList.cs index 004480d6..3ddb0f7a 100644 --- a/src/Hyperbee.Json/Filters/Values/NodeList.cs +++ b/src/Hyperbee.Json/Filters/Values/NodeList.cs @@ -1,18 +1,15 @@ using System.Collections; -using Hyperbee.Json.Filters.Parser; namespace Hyperbee.Json.Filters.Values; -public struct NodeList( IEnumerable value, bool isNormalized ) : IValueType, IEnumerable +public readonly struct NodeList( IEnumerable value, bool isNormalized ) : IValueType, IEnumerable { - public readonly bool IsNormalized => isNormalized; - public readonly ValueKind ValueKind => ValueKind.NodeList; - - public IValueTypeComparer Comparer { get; set; } + public bool IsNormalized => isNormalized; + public ValueKind ValueKind => ValueKind.NodeList; public IEnumerable Value { get; } = value; - public readonly IEnumerator GetEnumerator() => Value.GetEnumerator(); + public IEnumerator GetEnumerator() => Value.GetEnumerator(); - readonly IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } diff --git a/src/Hyperbee.Json/Filters/Values/ScalarValue.cs b/src/Hyperbee.Json/Filters/Values/ScalarValue.cs index 25b2d6fa..3d6f6fc3 100644 --- a/src/Hyperbee.Json/Filters/Values/ScalarValue.cs +++ b/src/Hyperbee.Json/Filters/Values/ScalarValue.cs @@ -1,20 +1,18 @@ -using Hyperbee.Json.Filters.Parser; +using System.Diagnostics; namespace Hyperbee.Json.Filters.Values; -public struct ScalarValue( T value ) : IValueType where T : IConvertible +[DebuggerDisplay( "{ValueKind}, Value = {Value}" )] +public readonly struct ScalarValue( TType value ) : IValueType where TType : IConvertible { - public readonly ValueKind ValueKind => ValueKind.Scalar; - public IValueTypeComparer Comparer { get; set; } = null; + public ValueKind ValueKind => ValueKind.Scalar; - public T Value { get; } = value; + public TType Value { get; } = value; - // Implicit conversion operators for bool, string, float, and int - - public static implicit operator ScalarValue( bool value ) => new( (T) (IConvertible) value ); - public static implicit operator ScalarValue( string value ) => new( (T) (IConvertible) value ); - public static implicit operator ScalarValue( int value ) => new( (T) (IConvertible) value ); - public static implicit operator ScalarValue( float value ) => new( (T) (IConvertible) value ); + public static implicit operator ScalarValue( bool value ) => new( (TType) (IConvertible) value ); + public static implicit operator ScalarValue( string value ) => new( (TType) (IConvertible) value ); + public static implicit operator ScalarValue( int value ) => new( (TType) (IConvertible) value ); + public static implicit operator ScalarValue( float value ) => new( (TType) (IConvertible) value ); } public static class Scalar diff --git a/src/Hyperbee.Json/JsonPathQueryParser.cs b/src/Hyperbee.Json/JsonPathQueryParser.cs index 9647d2ab..d50b28b5 100644 --- a/src/Hyperbee.Json/JsonPathQueryParser.cs +++ b/src/Hyperbee.Json/JsonPathQueryParser.cs @@ -270,7 +270,7 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD throw new NotSupportedException( "Invalid bracket expression syntax. Bracket expression cannot be empty." ); // validate the selector and get its kind - selectorKind = GetValidSelectorKind( selectorSpan ); + selectorKind = GetUnionSelectorKind( selectorSpan ); // create the selector descriptor SelectorDescriptor descriptor; @@ -470,7 +470,7 @@ private static ReadOnlySpan GetSelectorSpan( State state, ReadOnlySpan selector ) + private static SelectorKind GetUnionSelectorKind( ReadOnlySpan selector ) { // selector order matters diff --git a/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs b/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs index cc2a2ba7..e9626464 100644 --- a/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs +++ b/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs @@ -1,37 +1,24 @@ using System.Text.Json; using System.Text.Json.Nodes; using BenchmarkDotNet.Attributes; -using Hyperbee.Json.Descriptors; -using Hyperbee.Json.Descriptors.Element; -using Hyperbee.Json.Descriptors.Node; using Hyperbee.Json.Filters.Parser; namespace Hyperbee.Json.Benchmark; public class FilterExpressionParserEvaluator { - private ITypeDescriptor _nodeTypeDescriptor; - private ITypeDescriptor _elementTypeDescriptor; - [Params( "(\"world\" == 'world') && (true || false)" )] public string Filter; - [GlobalSetup] - public void Setup() - { - _nodeTypeDescriptor = new NodeTypeDescriptor(); - _elementTypeDescriptor = new ElementTypeDescriptor(); - } - [Benchmark] public void JsonPathFilterParser_JsonElement() { - FilterParser.Parse( Filter, _elementTypeDescriptor ); + FilterParser.Parse( Filter ); } [Benchmark] public void JsonPathFilterParser_JsonNode() { - FilterParser.Parse( Filter, _nodeTypeDescriptor ); + FilterParser.Parse( Filter ); } } diff --git a/test/Hyperbee.Json.Benchmark/JsonPathParseAndSelect.cs b/test/Hyperbee.Json.Benchmark/JsonPathParseAndSelectEvaluator.cs similarity index 98% rename from test/Hyperbee.Json.Benchmark/JsonPathParseAndSelect.cs rename to test/Hyperbee.Json.Benchmark/JsonPathParseAndSelectEvaluator.cs index d892242b..75f5d3fb 100644 --- a/test/Hyperbee.Json.Benchmark/JsonPathParseAndSelect.cs +++ b/test/Hyperbee.Json.Benchmark/JsonPathParseAndSelectEvaluator.cs @@ -8,7 +8,7 @@ namespace Hyperbee.Json.Benchmark; -public class JsonPathParseAndSelect +public class JsonPathParseAndSelectEvaluator { [Params( "$.store.book[0]", diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs index d7e7590a..3a17c5d1 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs @@ -3,8 +3,6 @@ using System.Linq.Expressions; using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Descriptors.Element; -using Hyperbee.Json.Descriptors.Node; using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; @@ -199,34 +197,41 @@ private static (Expression, ParameterExpression) GetExpression( string filter, T { if ( sourceType == typeof( JsonElement ) ) { - var elementDescriptor = new ElementTypeDescriptor(); - var elementRuntimeContext = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); - return (FilterParser.Parse( filter, elementDescriptor ), elementRuntimeContext); + var runtimeContext = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); + return (FilterParser.Parse( filter ), runtimeContext); } - var nodeDescriptor = new NodeTypeDescriptor(); - var nodeRuntimeContext = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); - return (FilterParser.Parse( filter, nodeDescriptor ), nodeRuntimeContext); + if ( sourceType == typeof( JsonNode ) ) + { + var runtimeContext = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); + return (FilterParser.Parse( filter ), runtimeContext); + } + + throw new NotImplementedException(); } private static bool Execute( Expression expression, ParameterExpression param, Type sourceType ) { if ( sourceType == typeof( JsonElement ) ) { - var func = Expression + var filterFunc = Expression .Lambda, bool>>( expression, param ) .Compile(); - var descriptor = new ElementTypeDescriptor(); - return func( new FilterRuntimeContext( new JsonElement(), new JsonElement(), descriptor ) ); + + var runtimeContext = new FilterRuntimeContext( new JsonElement(), new JsonElement() ); + + return filterFunc( runtimeContext ); } if ( sourceType == typeof( JsonNode ) ) { - var func = Expression + var filterFunc = Expression .Lambda, bool>>( expression, param ) .Compile(); - var descriptor = new NodeTypeDescriptor(); - return func( new FilterRuntimeContext( new JsonObject(), new JsonObject(), descriptor ) ); + + var runtimeContext = new FilterRuntimeContext( new JsonObject(), new JsonObject() ); + + return filterFunc( runtimeContext ); } throw new NotImplementedException(); @@ -237,40 +242,25 @@ private static bool CompileAndExecuteFilter( string filter, Type sourceType ) if ( sourceType == typeof( JsonElement ) ) { var source = GetDocument(); - var descriptor = new ElementTypeDescriptor(); - var func = FilterParser.Compile( filter, descriptor ); + var filterFunc = FilterParser.Compile( filter ); + var runtimeContext = new FilterRuntimeContext( source.RootElement, source.RootElement ); - return func( new FilterRuntimeContext( source.RootElement, source.RootElement, descriptor ) ); + return filterFunc( runtimeContext ); } else { - // arrange var source = GetDocument(); - var descriptor = new NodeTypeDescriptor(); - var func = FilterParser.Compile( filter, descriptor ); + var filterFunc = FilterParser.Compile( filter ); + var runtimeContext = new FilterRuntimeContext( source, source ); - // act - return func( new FilterRuntimeContext( source, source, descriptor ) ); + return filterFunc( runtimeContext ); } } private static float Select( string filter, Type sourceType ) { - if ( sourceType == typeof( JsonElement ) ) - { - // arrange - var source = GetDocument(); - - // act - return source.Select( filter ).First().GetSingle(); - } - else - { - // arrange - var source = GetDocument(); - - // act - return source.Select( filter ).First().GetValue(); - } + return sourceType == typeof( JsonElement ) + ? GetDocument().Select( filter ).First().GetSingle() + : GetDocument().Select( filter ).First().GetValue(); } } diff --git a/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs b/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs index f9799c52..e1116256 100644 --- a/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs @@ -137,13 +137,15 @@ public void NodeTypeComparer_ShouldCompare_WithEmpty() private static ValueTypeComparer GetComparer() => new( new NodeValueAccessor() ); - private static IValueType GetNodeType( object item ) => - item switch + private static IValueType GetNodeType( object item ) + { + return item switch { - string itemString => new ScalarValue( itemString ), - float itemFloat => new ScalarValue( itemFloat ), - bool itemBool => new ScalarValue( itemBool ), + string itemString => Scalar.Value( itemString ), + float itemFloat => Scalar.Value( itemFloat ), + bool itemBool => Scalar.Value( itemBool ), IEnumerable nodes => new NodeList( nodes, true ), _ => Scalar.Nothing }; + } } From ad9297bc34538718048d1f14871ed79bc8c13ef0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:21:05 -0700 Subject: [PATCH 08/18] [FEATURE]: Reduce memory allocations in EnumerateChildren (#47) * Performance and naming refactors --------- Co-authored-by: Brenton Farmer --- README.md | 81 ++++++------- .../Element/ElementValueAccessor.cs | 36 +++--- .../Descriptors/IValueAccessor.cs | 8 +- .../Descriptors/Node/NodeValueAccessor.cs | 44 +++---- .../Descriptors/Node/ValueTypeExtensions.cs | 3 +- .../Extensions/EnumerableExtensions.cs | 2 +- .../{JsonHelper.cs => JsonDynamicHelper.cs} | 2 +- .../Extensions/JsonElementExtensions.cs | 4 +- ...xception.cs => FilterCompilerException.cs} | 8 +- src/Hyperbee.Json/Filters/FilterEvaluator.cs | 2 +- .../Expressions/FunctionExpressionFactory.cs | 3 +- .../Filters/Parser/FilterParser.cs | 34 +++--- src/Hyperbee.Json/Filters/Parser/Operator.cs | 2 +- .../Filters/Parser/ParserState.cs | 2 +- src/Hyperbee.Json/JsonPath.cs | 4 +- src/Hyperbee.Json/JsonPathQueryParser.cs | 107 +++++++----------- test/Hyperbee.Json.Cts/TestHelper.cs | 4 +- .../Dynamic/JsonDynamicTests.cs | 2 +- ...escentTests.cs => JsonPathDescentTests.cs} | 10 +- .../Query/JsonPathUnionTests.cs | 2 +- 20 files changed, 171 insertions(+), 189 deletions(-) rename src/Hyperbee.Json/Extensions/{JsonHelper.cs => JsonDynamicHelper.cs} (92%) rename src/Hyperbee.Json/Filters/{FilterException.cs => FilterCompilerException.cs} (50%) rename test/Hyperbee.Json.Tests/Query/{JsonPathRecursiveDescentTests.cs => JsonPathDescentTests.cs} (89%) diff --git a/README.md b/README.md index 42730235..b0a7422c 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,14 @@ JsonTypeDescriptorRegistry.GetDescriptor().Functions var results = source.Select( "$..[?path(@) == '$.store.book[2].title']" ); ``` +## Why Choose [Hyperbee.Json](https://github.com/Stillpoint-Software/Hyperbee.Json) ? + +- High Performance. +- Supports both `JsonElement`, and `JsonNode`. +- Deferred execution queries with `IEnumerable`. +- Extendable to support additional JSON document types and functions. +- RFC conforming JSONPath implementation. + ## Comparison with Other Libraries There are excellent libraries available for RFC-9535 .NET JsonPath. @@ -231,6 +239,7 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. - **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) @@ -247,20 +256,13 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. - **Pros:** - Comprehensive feature set. + - Deferred execution queries with `IEnumerable`. - Documentation and examples. - Strong community support. - **Cons:** - No support for `JsonElement`, or `JsonNode`. -### Why Choose [Hyperbee.Json](https://github.com/Stillpoint-Software/Hyperbee.Json) ? - -- High Performance. -- Supports both `JsonElement`, and `JsonNode`. -- Deferred execution queries with `IEnumerable`. -- Extendable to support additional JSON document types and functions. -- RFC conforming JSONPath implementation. - ## Benchmarks Here is a performance comparison of various queries on the standard book store document. @@ -305,38 +307,37 @@ Here is a performance comparison of various queries on the standard book store d ``` ``` -| Method | Filter | Mean | Error | StdDev | Allocated -|------------------------ |--------------------------------- |---------- |----------- |---------- |---------- -| Hyperbee_JsonElement | $..* `First()` | 3.186 us | 0.6615 us | 0.0363 us | 4.3 KB -| Hyperbee_JsonNode | $..* `First()` | 3.521 us | 0.1192 us | 0.0065 us | 3.45 KB -| JsonEverything_JsonNode | $..* `First()` | 3.545 us | 0.7400 us | 0.0406 us | 3.53 KB -| JsonCons_JsonElement | $..* `First()` | 5.793 us | 1.3811 us | 0.0757 us | 8.48 KB -| Newtonsoft_JObject | $..* `First()` | 9.119 us | 5.3278 us | 0.2920 us | 14.22 KB -| | | | | | -| JsonCons_JsonElement | $..* | 6.098 us | 2.0947 us | 0.1148 us | 8.45 KB -| Hyperbee_JsonElement | $..* | 8.812 us | 1.6812 us | 0.0922 us | 11.1 KB -| Hyperbee_JsonNode | $..* | 10.621 us | 1.2452 us | 0.0683 us | 10.91 KB -| Newtonsoft_JObject | $..* | 11.037 us | 5.4690 us | 0.2998 us | 14.86 KB -| JsonEverything_JsonNode | $..* | 23.329 us | 2.2255 us | 0.1220 us | 36.81 KB -| | | | | | -| Hyperbee_JsonElement | $..price | 5.248 us | 3.4306 us | 0.1880 us | 6.45 KB -| JsonCons_JsonElement | $..price | 5.402 us | 0.3285 us | 0.0180 us | 5.65 KB -| Hyperbee_JsonNode | $..price | 8.483 us | 2.0999 us | 0.1151 us | 8.86 KB -| Newtonsoft_JObject | $..price | 10.109 us | 9.6403 us | 0.5284 us | 14.4 KB -| JsonEverything_JsonNode | $..price | 17.054 us | 10.5303 us | 0.5772 us | 27.63 KB -| | | | | | -| Hyperbee_JsonElement | $.store.book[?(@.price == 8.99)] | 4.486 us | 3.2931 us | 0.1805 us | 5.82 KB -| JsonCons_JsonElement | $.store.book[?(@.price == 8.99)] | 5.381 us | 3.3826 us | 0.1854 us | 5.05 KB -| Hyperbee_JsonNode | $.store.book[?(@.price == 8.99)] | 7.354 us | 4.9887 us | 0.2734 us | 8.47 KB -| Newtonsoft_JObject | $.store.book[?(@.price == 8.99)] | 10.519 us | 3.5514 us | 0.1947 us | 15.84 KB -| JsonEverything_JsonNode | $.store.book[?(@.price == 8.99)] | 11.912 us | 7.6346 us | 0.4185 us | 15.85 KB -| | | | | | -| Hyperbee_JsonElement | $.store.book[0] | 2.722 us | 0.5813 us | 0.0319 us | 2.27 KB -| JsonCons_JsonElement | $.store.book[0] | 3.150 us | 1.7316 us | 0.0949 us | 3.21 KB -| Hyperbee_JsonNode | $.store.book[0] | 3.339 us | 0.1733 us | 0.0095 us | 2.77 KB -| JsonEverything_JsonNode | $.store.book[0] | 4.974 us | 3.2013 us | 0.1755 us | 5.96 KB -| Newtonsoft_JObject | $.store.book[0] | 9.482 us | 7.0303 us | 0.3854 us | 14.56 KB - +| Method | Filter | Mean | Error | StdDev | Allocated +|------------------------ |--------------------------------- |---------- |---------- |---------- |---------- +| Hyperbee_JsonElement | $..* `First()` | 3.210 us | 0.2597 us | 0.0142 us | 3.95 KB +| Hyperbee_JsonNode | $..* `First()` | 3.366 us | 2.7086 us | 0.1485 us | 3.4 KB +| JsonEverything_JsonNode | $..* `First()` | 3.408 us | 3.2650 us | 0.1790 us | 3.53 KB +| JsonCons_JsonElement | $..* `First()` | 6.090 us | 6.1994 us | 0.3398 us | 8.48 KB +| Newtonsoft_JObject | $..* `First()` | 9.366 us | 1.4505 us | 0.0795 us | 14.22 KB +| | | | | | +| JsonCons_JsonElement | $..* | 5.812 us | 1.3734 us | 0.0753 us | 8.45 KB +| Hyperbee_JsonElement | $..* | 7.929 us | 1.5446 us | 0.0847 us | 8.66 KB +| Hyperbee_JsonNode | $..* | 10.061 us | 9.3866 us | 0.5145 us | 10.58 KB +| Newtonsoft_JObject | $..* | 11.061 us | 0.9381 us | 0.0514 us | 14.86 KB +| JsonEverything_JsonNode | $..* | 23.492 us | 1.7850 us | 0.0978 us | 36.81 KB +| | | | | | +| Hyperbee_JsonElement | $..price | 4.813 us | 3.2103 us | 0.1760 us | 4.63 KB +| JsonCons_JsonElement | $..price | 5.244 us | 2.4403 us | 0.1338 us | 5.65 KB +| Hyperbee_JsonNode | $..price | 7.741 us | 6.5786 us | 0.3606 us | 7.8 KB +| Newtonsoft_JObject | $..price | 10.335 us | 7.3216 us | 0.4013 us | 14.4 KB +| JsonEverything_JsonNode | $..price | 16.982 us | 9.8753 us | 0.5413 us | 27.63 KB +| | | | | | +| Hyperbee_JsonElement | $.store.book[?(@.price == 8.99)] | 4.464 us | 2.2718 us | 0.1245 us | 5.68 KB +| JsonCons_JsonElement | $.store.book[?(@.price == 8.99)] | 5.262 us | 4.0734 us | 0.2233 us | 5.05 KB +| Hyperbee_JsonNode | $.store.book[?(@.price == 8.99)] | 7.423 us | 0.7463 us | 0.0409 us | 8.31 KB +| Newtonsoft_JObject | $.store.book[?(@.price == 8.99)] | 10.386 us | 7.7620 us | 0.4255 us | 15.84 KB +| JsonEverything_JsonNode | $.store.book[?(@.price == 8.99)] | 12.621 us | 4.5079 us | 0.2471 us | 15.85 KB +| | | | | | +| Hyperbee_JsonElement | $.store.book[0] | 2.787 us | 1.3694 us | 0.0751 us | 2.27 KB +| JsonCons_JsonElement | $.store.book[0] | 3.297 us | 0.1607 us | 0.0088 us | 3.21 KB +| Hyperbee_JsonNode | $.store.book[0] | 3.345 us | 0.5145 us | 0.0282 us | 2.77 KB +| JsonEverything_JsonNode | $.store.book[0] | 4.875 us | 2.6974 us | 0.1479 us | 5.96 KB +| Newtonsoft_JObject | $.store.book[0] | 9.134 us | 5.0882 us | 0.2789 us | 14.56 KB ``` ## Additional Documentation diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs index 8028a058..4e25c239 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; +using System.Text.Json.Nodes; using Hyperbee.Json.Extensions; namespace Hyperbee.Json.Descriptors.Element; @@ -10,36 +11,41 @@ internal class ElementValueAccessor : IValueAccessor { public IEnumerable<(JsonElement, string, SelectorKind)> EnumerateChildren( JsonElement value, bool includeValues = true ) { + // allocating is faster than using yield return and less memory intensive + // because we avoid calling reverse on the enumerable (which anyway allocates a new array) + switch ( value.ValueKind ) { case JsonValueKind.Array: { - for ( var index = value.GetArrayLength() - 1; index >= 0; index-- ) + var length = value.GetArrayLength(); + var results = new (JsonElement, string, SelectorKind)[length]; + + for ( var index = length - 1; index >= 0; index-- ) { var child = value[index]; - if ( includeValues || child.ValueKind is JsonValueKind.Array or JsonValueKind.Object ) - yield return (child, index.ToString(), SelectorKind.Index); + results[index] = (child, index.ToString(), SelectorKind.Index); } - break; + return results; } case JsonValueKind.Object: { - if ( includeValues ) + var results = new Stack<(JsonElement, string, SelectorKind)>(); // stack will reverse the list + foreach ( var child in value.EnumerateObject() ) { - foreach ( var child in value.EnumerateObject().Reverse() ) - yield return (child.Value, child.Name, SelectorKind.Name); - } - else - { - foreach ( var child in value.EnumerateObject().Where( property => property.Value.ValueKind is JsonValueKind.Array or JsonValueKind.Object ).Reverse() ) - yield return (child.Value, child.Name, SelectorKind.Name); + if ( includeValues || child.Value.ValueKind is JsonValueKind.Array or JsonValueKind.Object ) + { + results.Push( (child.Value, child.Name, SelectorKind.Name) ); + } } - break; + return results; } } + + return []; } [MethodImpl( MethodImplOptions.AggressiveInlining )] @@ -76,7 +82,7 @@ public int GetArrayLength( in JsonElement value ) : 0; } - public bool TryGetChildValue( in JsonElement value, string childSelector, SelectorKind selectorKind, out JsonElement childValue ) + public bool TryGetChild( in JsonElement value, string childSelector, SelectorKind selectorKind, out JsonElement childValue ) { switch ( value.ValueKind ) { @@ -154,7 +160,7 @@ public bool TryParseNode( ReadOnlySpan item, out JsonElement element ) return false; } - public bool TryGetValueFromNode( JsonElement element, out object value ) + public bool TryGetValueFromNode( JsonElement element, out IConvertible value ) { switch ( element.ValueKind ) { diff --git a/src/Hyperbee.Json/Descriptors/IValueAccessor.cs b/src/Hyperbee.Json/Descriptors/IValueAccessor.cs index c390baaa..656288bf 100644 --- a/src/Hyperbee.Json/Descriptors/IValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/IValueAccessor.cs @@ -1,4 +1,6 @@ -namespace Hyperbee.Json.Descriptors; +using System.Text.Json.Nodes; + +namespace Hyperbee.Json.Descriptors; public interface IValueAccessor { @@ -6,9 +8,9 @@ public interface IValueAccessor bool TryGetElementAt( in TNode value, int index, out TNode element ); NodeKind GetNodeKind( in TNode value ); int GetArrayLength( in TNode value ); - bool TryGetChildValue( in TNode value, string childSelector, SelectorKind selectorKind, out TNode childValue ); + bool TryGetChild( in TNode value, string childSelector, SelectorKind selectorKind, out TNode childValue ); bool TryParseNode( ReadOnlySpan item, out TNode value ); bool DeepEquals( TNode left, TNode right ); - bool TryGetValueFromNode( TNode item, out object value ); + bool TryGetValueFromNode( TNode item, out IConvertible value ); bool TryGetFromPointer( in TNode value, JsonPathSegment segment, out TNode childValue ); } diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs index c9695505..731b89b0 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs @@ -10,33 +10,39 @@ internal class NodeValueAccessor : IValueAccessor { public IEnumerable<(JsonNode, string, SelectorKind)> EnumerateChildren( JsonNode value, bool includeValues = true ) { + // allocating is faster than using yield return and less memory intensive + // because we avoid calling reverse on the enumerable (which anyway allocates a new array) + switch ( value ) { case JsonArray arrayValue: - for ( var index = arrayValue.Count - 1; index >= 0; index-- ) { - var child = arrayValue[index]; - - if ( includeValues || child is JsonObject or JsonArray ) - yield return (child, index.ToString(), SelectorKind.Index); - } + var length = arrayValue.Count; + var results = new (JsonNode, string, SelectorKind)[length]; - break; - case JsonObject objectValue: + for ( var index = length - 1; index >= 0; index-- ) + { + var child = arrayValue[index]; + if ( includeValues || child is JsonObject or JsonArray ) + results[index] = (child, index.ToString(), SelectorKind.Index); + } - if ( includeValues ) - { - foreach ( var child in objectValue.Reverse() ) - yield return (child.Value, child.Key, SelectorKind.Name); + return results; } - else + case JsonObject objectValue: { - foreach ( var child in objectValue.Where( property => property.Value is JsonObject or JsonArray ).Reverse() ) - yield return (child.Value, child.Key, SelectorKind.Name); - } + var results = new Stack<(JsonNode, string, SelectorKind)>(); // stack will reverse the list + foreach ( var child in objectValue ) + { + if ( includeValues || child.Value is JsonObject or JsonArray ) + results.Push( (child.Value, child.Key, SelectorKind.Name) ); + } - break; + return results; + } } + + return []; } [MethodImpl( MethodImplOptions.AggressiveInlining )] @@ -75,7 +81,7 @@ public int GetArrayLength( in JsonNode value ) return 0; } - public bool TryGetChildValue( in JsonNode value, string childSelector, SelectorKind selectorKind, out JsonNode childValue ) + public bool TryGetChild( in JsonNode value, string childSelector, SelectorKind selectorKind, out JsonNode childValue ) { switch ( value ) { @@ -150,7 +156,7 @@ public bool TryParseNode( ReadOnlySpan item, out JsonNode node ) } } - public bool TryGetValueFromNode( JsonNode node, out object value ) + public bool TryGetValueFromNode( JsonNode node, out IConvertible value ) { switch ( node?.GetValueKind() ) { diff --git a/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs b/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs index e5c43746..cf1fe208 100644 --- a/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs +++ b/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs @@ -1,5 +1,4 @@ -using System.Text.Json; -using System.Text.Json.Nodes; +using System.Text.Json.Nodes; using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Values; diff --git a/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs b/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs index 566a337f..550560f2 100644 --- a/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs +++ b/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs @@ -12,6 +12,6 @@ public static T OneOrDefault( this IEnumerable source ) return default; var singleItem = iterator.Current; - return iterator.MoveNext() ? default : singleItem; + return iterator.MoveNext() ? default : singleItem; // More than one element returns default } } diff --git a/src/Hyperbee.Json/Extensions/JsonHelper.cs b/src/Hyperbee.Json/Extensions/JsonDynamicHelper.cs similarity index 92% rename from src/Hyperbee.Json/Extensions/JsonHelper.cs rename to src/Hyperbee.Json/Extensions/JsonDynamicHelper.cs index 1de2809e..9fa0383e 100644 --- a/src/Hyperbee.Json/Extensions/JsonHelper.cs +++ b/src/Hyperbee.Json/Extensions/JsonDynamicHelper.cs @@ -4,7 +4,7 @@ namespace Hyperbee.Json.Extensions; -public static class JsonHelper +public static class JsonDynamicHelper { // conversion diff --git a/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs b/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs index b5cc3319..8869ab39 100644 --- a/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs +++ b/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs @@ -6,9 +6,9 @@ public static class JsonElementExtensions { // Deep Equals/Compare extensions - public static bool DeepEquals( this JsonElement elmA, JsonElement elmB, JsonDocumentOptions options = default ) + public static bool DeepEquals( this JsonElement element1, JsonElement element2, JsonDocumentOptions options = default ) { var comparer = new JsonElementDeepEqualityComparer( options.MaxDepth ); - return comparer.Equals( elmA, elmB ); + return comparer.Equals( element1, element2 ); } } diff --git a/src/Hyperbee.Json/Filters/FilterException.cs b/src/Hyperbee.Json/Filters/FilterCompilerException.cs similarity index 50% rename from src/Hyperbee.Json/Filters/FilterException.cs rename to src/Hyperbee.Json/Filters/FilterCompilerException.cs index 97de57da..f27f53d6 100644 --- a/src/Hyperbee.Json/Filters/FilterException.cs +++ b/src/Hyperbee.Json/Filters/FilterCompilerException.cs @@ -1,19 +1,19 @@ namespace Hyperbee.Json.Filters; [Serializable] -public class FilterException : Exception +public class FilterCompilerException : Exception { - public FilterException() + public FilterCompilerException() : base( "JsonPath filter evaluator exception." ) { } - public FilterException( string message ) + public FilterCompilerException( string message ) : base( message ) { } - public FilterException( string message, Exception innerException ) + public FilterCompilerException( string message, Exception innerException ) : base( message, innerException ) { } diff --git a/src/Hyperbee.Json/Filters/FilterEvaluator.cs b/src/Hyperbee.Json/Filters/FilterEvaluator.cs index 6ab85d73..8ea4a7cc 100644 --- a/src/Hyperbee.Json/Filters/FilterEvaluator.cs +++ b/src/Hyperbee.Json/Filters/FilterEvaluator.cs @@ -29,7 +29,7 @@ public bool Evaluate( string filter, TNode current, TNode root ) } catch ( Exception ex ) { - throw new FilterException( "Error compiling filter expression.", ex ); + throw new FilterCompilerException( "Error compiling filter expression.", ex ); } } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs index 54facac2..00058a40 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs @@ -24,8 +24,7 @@ public static bool TryGetExpression( ref ParserState state, out Expressio var function = functionActivator(); - expression = function - .GetExpression( ref state ); // will recurse for each function argument. + expression = function.GetExpression( ref state ); // will recurse for each function argument. exprInfo.Kind = ExpressionKind.Function; exprInfo.FunctionInfo = function.FunctionInfo; diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index e77e73b3..567e1d50 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -26,7 +26,6 @@ public abstract class FilterParser public class FilterParser : FilterParser { - // use a common instance internal static readonly ParameterExpression RuntimeContextExpression = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); internal static readonly ITypeDescriptor Descriptor = JsonTypeDescriptorRegistry.GetDescriptor(); @@ -56,12 +55,12 @@ internal static Expression Parse( ref ParserState state ) // recursion entrypoin throw new NotSupportedException( $"Invalid filter: \"{state.Buffer}\"." ); // parse the expression - var items = new List(); + var items = new Queue(); do { MoveNext( ref state ); - items.Add( GetExprItem( ref state ) ); // will recurse for nested expressions + items.Enqueue( GetExprItem( ref state ) ); // will recurse for nested expressions } while ( state.IsParsing ); @@ -70,10 +69,9 @@ internal static Expression Parse( ref ParserState state ) // recursion entrypoin throw new NotSupportedException( $"Unbalanced parenthesis in filter: \"{state.Buffer}\"." ); // merge the expressions - var baseItem = items[0]; - var index = 1; + var baseItem = items.Dequeue(); - return Merge( in state, baseItem, ref index, items ); + return Merge( in state, baseItem, items ); } @@ -276,21 +274,21 @@ static bool Next( ref ParserState state, char expected ) } } - private static Expression Merge( in ParserState state, ExprItem left, ref int index, List items, bool mergeOneOnly = false ) + private static Expression Merge( in ParserState state, ExprItem left, Queue items, bool mergeOneOnly = false ) { - if ( items.Count == 1 ) + if ( items.Count == 0 ) { ThrowIfInvalidCompare( in state, left, null ); // single item, no recursion } else { - while ( index < items.Count ) + while ( items.Count > 0 ) { - var right = items[index++]; + var right = items.Dequeue(); while ( !CanMergeItems( left, right ) ) { - Merge( in state, right, ref index, items, mergeOneOnly: true ); // recursive call - right becomes left + Merge( in state, right, items, mergeOneOnly: true ); // recursive call - right becomes left } ThrowIfInvalidCompare( in state, left, right ); @@ -360,7 +358,7 @@ private static void MergeItems( ExprItem left, ExprItem right ) static Expression ConvertBoolToValueTypeExpression( Expression leftExpression ) { - // Convert to ScalarValue using implicit operator and then return as a IValueType + // Convert bool to ScalarValue implicit operator and then return as an IValueType return ConvertExpression( ConvertExpression>( leftExpression ) ); } @@ -386,17 +384,13 @@ private static void ThrowIfFunctionInvalidCompare( in ParserState state, ExprIte if ( item.ExpressionInfo.Kind != ExpressionKind.Function ) return; - if ( (item.ExpressionInfo.FunctionInfo & ExtensionInfo.MustCompare) == ExtensionInfo.MustCompare && - !item.Operator.IsComparison() ) - { + var functionInfo = item.ExpressionInfo.FunctionInfo; + + if ( functionInfo.HasFlag( ExtensionInfo.MustCompare ) && !item.Operator.IsComparison() ) throw new NotSupportedException( $"Function must compare: {state.Buffer.ToString()}." ); - } - if ( (item.ExpressionInfo.FunctionInfo & ExtensionInfo.MustNotCompare) == ExtensionInfo.MustNotCompare && - item.Operator.IsComparison() ) - { + if ( functionInfo.HasFlag( ExtensionInfo.MustNotCompare ) && item.Operator.IsComparison() ) throw new NotSupportedException( $"Function must not compare: {state.Buffer.ToString()}." ); - } } private static void ThrowIfConstantIsNotCompared( in ParserState state, ExprItem left, ExprItem right ) diff --git a/src/Hyperbee.Json/Filters/Parser/Operator.cs b/src/Hyperbee.Json/Filters/Parser/Operator.cs index acbbea67..ff5bd4c0 100644 --- a/src/Hyperbee.Json/Filters/Parser/Operator.cs +++ b/src/Hyperbee.Json/Filters/Parser/Operator.cs @@ -35,7 +35,7 @@ public enum Operator Bracket = 0xF0 | NonOperator, } -public static class OperatorExtensions +internal static class OperatorExtensions { public static bool IsNonOperator( this Operator op ) { diff --git a/src/Hyperbee.Json/Filters/Parser/ParserState.cs b/src/Hyperbee.Json/Filters/Parser/ParserState.cs index 5dd2c720..3b0c0794 100644 --- a/src/Hyperbee.Json/Filters/Parser/ParserState.cs +++ b/src/Hyperbee.Json/Filters/Parser/ParserState.cs @@ -3,7 +3,7 @@ namespace Hyperbee.Json.Filters.Parser; [DebuggerDisplay( "{Buffer.ToString()}, Item = {Item.ToString()}, Operator = {Operator}, Pos = {Pos.ToString()}" )] -public ref struct ParserState +internal ref struct ParserState { public ReadOnlySpan Buffer { get; } public ReadOnlySpan Item { get; internal set; } diff --git a/src/Hyperbee.Json/JsonPath.cs b/src/Hyperbee.Json/JsonPath.cs index 7e56066f..5df06880 100644 --- a/src/Hyperbee.Json/JsonPath.cs +++ b/src/Hyperbee.Json/JsonPath.cs @@ -141,7 +141,7 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N continue; // don't allow indexing in to objects // try to access object or array using name or index - if ( accessor.TryGetChildValue( value, selector, selectorKind, out var childValue ) ) + if ( accessor.TryGetChild( value, selector, selectorKind, out var childValue ) ) stack.Push( value, childValue, selector, segmentNext ); continue; @@ -269,7 +269,7 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N // Object: [name1,name2,...] Names over object case SelectorKind.Name when nodeKind == NodeKind.Object: { - if ( accessor.TryGetChildValue( value, selector, selectorKind, out var childValue ) ) + if ( accessor.TryGetChild( value, selector, selectorKind, out var childValue ) ) stack.Push( value, childValue, selector, segmentNext ); continue; diff --git a/src/Hyperbee.Json/JsonPathQueryParser.cs b/src/Hyperbee.Json/JsonPathQueryParser.cs index d50b28b5..17618700 100644 --- a/src/Hyperbee.Json/JsonPathQueryParser.cs +++ b/src/Hyperbee.Json/JsonPathQueryParser.cs @@ -67,10 +67,10 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD var bracketDepth = 0; var parenDepth = 0; - char[] whitespaceTerminators = []; + Span whitespaceTerminators = ['\0', '\0']; // '\0' is used as a sentinel value var whiteSpaceReplay = true; - var tokens = new List(); + var segments = new List(); var selectors = new List(); var state = State.Start; @@ -78,19 +78,11 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD do { - // read next character - char c; + // Read next character + char c = i < n ? query[i++] : '\0'; - if ( i < n ) - { - c = query[i++]; - } - else // end of input - { - if ( state != State.Whitespace ) // whitespace is a sub-state, allow it to exit - state = State.Finish; - c = '\0'; // Set char to null terminator to signal end of input - } + if ( state != State.Whitespace && c == '\0' ) // whitespace is a sub-state, allow it to exit + state = State.Finish; // end of input // process character ReadOnlySpan selectorSpan; @@ -107,9 +99,10 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD if ( query[^1] == '.' && query[^2] == '.' ) throw new NotSupportedException( "`..` cannot be the last segment." ); - InsertToken( tokens, new SelectorDescriptor { SelectorKind = SelectorKind.Root, Value = c.ToString() } ); + InsertSegment( segments, new SelectorDescriptor { SelectorKind = SelectorKind.Root, Value = c.ToString() } ); - whitespaceTerminators = ['.', '[']; + whitespaceTerminators[0] = '.'; + whitespaceTerminators[1] = '['; state = State.Whitespace; returnState = State.DotChild; break; @@ -130,10 +123,10 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD break; default: - if ( c != '\0' && whitespaceTerminators.Length > 0 && !whitespaceTerminators.Contains( c ) ) + if ( c != '\0' && whitespaceTerminators[0] != '\0' && !whitespaceTerminators.Contains( c ) ) throw new NotSupportedException( $"Invalid character `{c}` at pos {i - 1}." ); - whitespaceTerminators = []; + whitespaceTerminators[0] = '\0'; // reset state = returnState; // transition back to the appropriate state selectorStart = i; // start of the next selector @@ -151,32 +144,9 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD switch ( c ) { case '[': // end-of-child - selectorSpan = GetSelectorSpan( state, query, selectorStart, i ); - selectorKind = selectorSpan switch - { - "$" => throw new NotSupportedException( $"Invalid use of root `$` at pos {i - 1}." ), - "@" => throw new NotSupportedException( $"Invalid use of local root `$` at pos {i - 1}." ), - "*" => SelectorKind.Wildcard, - _ => SelectorKind.Name - }; - - if ( selectorKind == SelectorKind.Name && !selectorSpan.IsEmpty ) - { - ThrowIfQuoted( selectorSpan ); - ThrowIfInvalidUnquotedName( selectorSpan ); - } - - InsertToken( tokens, GetSelectorDescriptor( selectorKind, selectorSpan ) ); - - state = State.Whitespace; - whiteSpaceReplay = false; - returnState = State.UnionItem; - bracketDepth = 1; - i--; // replay character - break; - case '.': // end-of-child - if ( i == n ) + + if ( i == n && c == '.' ) // dot( . ) is not allowed at the end of the query throw new NotSupportedException( $"Missing character after `.` at pos {i - 1}." ); selectorSpan = GetSelectorSpan( state, query, selectorStart, i ); @@ -194,15 +164,29 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD ThrowIfInvalidUnquotedName( selectorSpan ); } - InsertToken( tokens, GetSelectorDescriptor( selectorKind, selectorSpan ) ); + InsertSegment( segments, GetSelectorDescriptor( selectorKind, selectorSpan ) ); - if ( i < n && query[i] == '.' ) // peek next character + // continue parsing next child + switch ( c ) { - InsertToken( tokens, GetSelectorDescriptor( SelectorKind.Descendant, ".." ) ); - i++; // advance past second `.` + case '.': // continue dot child + if ( i < n && query[i] == '.' ) // peek next character for `..` + { + InsertSegment( segments, GetSelectorDescriptor( SelectorKind.Descendant, ".." ) ); + i++; // advance past second `.` + } + selectorStart = i; + break; + + case '[': // transition to union + state = State.Whitespace; + whiteSpaceReplay = false; + returnState = State.UnionItem; + bracketDepth = 1; + i--; // replay character + break; } - selectorStart = i; break; case '\'': @@ -337,15 +321,15 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD switch ( c ) { case ',': - whitespaceTerminators = []; state = State.Whitespace; returnState = State.UnionNext; break; case ']': - InsertToken( tokens, [.. selectors] ); + InsertSegment( segments, [.. selectors] ); selectors.Clear(); - whitespaceTerminators = ['.', '[']; + whitespaceTerminators[0] = '.'; + whitespaceTerminators[1] = '['; state = State.Whitespace; returnState = State.DotChild; break; @@ -409,7 +393,7 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD ThrowIfInvalidUnquotedName( selectorSpan ); } - InsertToken( tokens, GetSelectorDescriptor( finalKind, selectorSpan ) ); + InsertSegment( segments, GetSelectorDescriptor( finalKind, selectorSpan ) ); } state = State.Final; @@ -420,7 +404,7 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD } } while ( state != State.Final ); - return BuildJsonPathQuery( query, tokens ); + return BuildJsonPathQuery( query, segments ); } private static JsonPathQuery BuildJsonPathQuery( ReadOnlySpan query, IList segments ) @@ -508,21 +492,12 @@ private static SelectorKind GetUnionSelectorKind( ReadOnlySpan selector ) } [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static void InsertToken( ICollection tokens, SelectorDescriptor selector ) - { - if ( selector?.Value == null ) // ignore null selectors - return; - - InsertToken( tokens, [selector] ); - } - - [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static void InsertToken( ICollection tokens, SelectorDescriptor[] selectors ) + private static void InsertSegment( List segments, params SelectorDescriptor[] selectors ) { - if ( selectors == null || selectors.Length == 0 ) // ignore empty selectors - return; + if ( selectors == null || selectors.Length == 0 || (selectors.Length == 1 && selectors[0]?.Value == null) ) + return; // ignore null and empty selectors. this is valid in some cases like `].` and `..` - tokens.Add( new JsonPathSegment( selectors ) ); + segments.Add( new JsonPathSegment( selectors ) ); } [MethodImpl( MethodImplOptions.AggressiveInlining )] diff --git a/test/Hyperbee.Json.Cts/TestHelper.cs b/test/Hyperbee.Json.Cts/TestHelper.cs index d7bfd2c1..5a0670a8 100644 --- a/test/Hyperbee.Json.Cts/TestHelper.cs +++ b/test/Hyperbee.Json.Cts/TestHelper.cs @@ -9,7 +9,7 @@ internal static class TestHelper public static JsonArray ConvertToJsonArraySet( JsonNode jsonNode ) { if ( jsonNode is JsonArray jsonArray && jsonArray[0] is JsonArray ) - return jsonArray; + return jsonArray; // already a set JsonArray jsonArraySet = new JsonArray( jsonNode ); @@ -21,7 +21,7 @@ public static JsonArray ConvertToJsonArray( IEnumerable nodes, bool fo var nodeArray = nodes.ToArray(); if ( !force && nodeArray.Length == 1 && nodeArray[0] is JsonArray array ) - return array; + return array; // already an array var jsonArray = new JsonArray(); diff --git a/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs b/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs index 3ca4ff68..11129dc7 100644 --- a/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs +++ b/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs @@ -21,7 +21,7 @@ private enum Thing public void DynamicJsonElement_ShouldReturnCorrectResults() { var source = GetDocument(); - var element = JsonHelper.ConvertToDynamic( source ); + var element = JsonDynamicHelper.ConvertToDynamic( source ); var book = element.store.book[0]; var author = book.author; diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathRecursiveDescentTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathDescentTests.cs similarity index 89% rename from test/Hyperbee.Json.Tests/Query/JsonPathRecursiveDescentTests.cs rename to test/Hyperbee.Json.Tests/Query/JsonPathDescentTests.cs index 94768b71..7181998f 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathRecursiveDescentTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathDescentTests.cs @@ -8,13 +8,13 @@ namespace Hyperbee.Json.Tests.Query; [TestClass] -public class JsonPathRecursiveDescentTests : JsonTestBase +public class JsonPathDescentTests : JsonTestBase { [DataTestMethod] [DataRow( "$..", typeof( JsonDocument ) )] [DataRow( "$..", typeof( JsonNode ) )] [ExpectedException( typeof( NotSupportedException ) )] - public void RecursiveDescent( string query, Type sourceType ) + public void Descent( string query, Type sourceType ) { // consensus: none @@ -33,7 +33,7 @@ public void RecursiveDescent( string query, Type sourceType ) [DataTestMethod] [DataRow( "$..*", typeof( JsonDocument ) )] [DataRow( "$..*", typeof( JsonNode ) )] - public void RecursiveDescentOnNestedArrays( string query, Type sourceType ) + public void DescentOnNestedArrays( string query, Type sourceType ) { const string json = """ [ @@ -60,7 +60,7 @@ public void RecursiveDescentOnNestedArrays( string query, Type sourceType ) [DataRow( "$.key..", typeof( JsonDocument ) )] [DataRow( "$.key..", typeof( JsonNode ) )] [ExpectedException( typeof( NotSupportedException ) )] - public void RecursiveDescentAfterDotNotation( string query, Type sourceType ) + public void DescentAfterDotNotation( string query, Type sourceType ) { // consensus: NOT_SUPPORTED @@ -82,7 +82,7 @@ public void RecursiveDescentAfterDotNotation( string query, Type sourceType ) [DataTestMethod] [DataRow( "$..[1].key", typeof( JsonDocument ) )] [DataRow( "$..[1].key", typeof( JsonNode ) )] - public void DotNotationAfterBracketNotationAfterRecursiveDescent( string query, Type sourceType ) + public void DotNotationAfterBracketNotationAfterDescent( string query, Type sourceType ) { // consensus: [200, 42, 500] diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathUnionTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathUnionTests.cs index 4ae03251..4c10de4a 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathUnionTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathUnionTests.cs @@ -153,7 +153,7 @@ public void UnionWithMultipleKeys( string query, Type sourceType ) public void UnionWithKeysAfterRecursiveDescent( string query, Type sourceType ) { // consensus: ["cc1", "cc2", "cc3", "cc5", "dd1", "dd2", "dd4"] - // any order + // any order const string json = """ [ From a97e8ab498bb49794a82a22ee6a6c137ce418748 Mon Sep 17 00:00:00 2001 From: Brenton Farmer Date: Wed, 17 Jul 2024 16:31:55 -0700 Subject: [PATCH 09/18] fix develop --- .../Descriptors/Element/ElementValueAccessor.cs | 5 +++-- src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs | 5 +++-- test/Hyperbee.Json.Tests/Query/JsonPathBookstoreTests.cs | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs index 4e25c239..10e61ff6 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs @@ -21,11 +21,12 @@ internal class ElementValueAccessor : IValueAccessor var length = value.GetArrayLength(); var results = new (JsonElement, string, SelectorKind)[length]; - for ( var index = length - 1; index >= 0; index-- ) + var reverseIndex = length - 1; + for ( var index = 0; index < length; index++, reverseIndex-- ) { var child = value[index]; if ( includeValues || child.ValueKind is JsonValueKind.Array or JsonValueKind.Object ) - results[index] = (child, index.ToString(), SelectorKind.Index); + results[reverseIndex] = (child, index.ToString(), SelectorKind.Index); } return results; diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs index 731b89b0..60fd9e50 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs @@ -20,11 +20,12 @@ internal class NodeValueAccessor : IValueAccessor var length = arrayValue.Count; var results = new (JsonNode, string, SelectorKind)[length]; - for ( var index = length - 1; index >= 0; index-- ) + var reverseIndex = length - 1; + for ( var index = 0; index < length; index++, reverseIndex-- ) { var child = arrayValue[index]; if ( includeValues || child is JsonObject or JsonArray ) - results[index] = (child, index.ToString(), SelectorKind.Index); + results[reverseIndex] = (child, index.ToString(), SelectorKind.Index); } return results; diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathBookstoreTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathBookstoreTests.cs index aab3384f..5c778a30 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathBookstoreTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathBookstoreTests.cs @@ -49,7 +49,7 @@ public void TheAuthorsOfAllBooksInTheStore( string query, Type sourceType ) public void AllAuthors( string query, Type sourceType ) { var source = GetDocumentFromResource( sourceType ); - var matches = source.Select( query ); + var matches = source.Select( query ).ToList(); var expected = new[] { source.FromJsonPathPointer( "$.store.book[0].author" ), From a15fb8ce0da2659492278c93b0f4c42681f06ef5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:54:26 -0700 Subject: [PATCH 10/18] [FEATURE]: Create cts test coverage for JsonElement (#49) * generate_tests.ps1 should create tests for both JsonNode and JsonElement. --------- Co-authored-by: Brenton Farmer --- .../Element/ElementValueAccessor.cs | 5 +- .../Descriptors/Node/NodeValueAccessor.cs | 4 +- .../Expressions/ParenExpressionFactory.cs | 2 +- .../Filters/Parser/ExtensionFunction.cs | 2 +- .../Filters/Parser/FilterParser.cs | 6 +- .../Filters/Parser/ParserState.cs | 8 +- src/Hyperbee.Json/Internal/SpanHelper.cs | 2 +- src/Hyperbee.Json/JsonPath.cs | 28 +- src/Hyperbee.Json/JsonPathQueryParser.cs | 45 +- .../JsonPathSliceSyntaxHelper.cs | 4 +- .../Hyperbee.Json.Cts.csproj | 4 +- .../TestSupport/IJsonDocument.cs | 8 + .../TestSupport/JsonElementHelper.cs | 79 + .../JsonNodeHelper.cs} | 18 +- .../TestSupport/TestHelper.cs | 40 + .../Tests/cts-basic-tests.cs | 994 ++-- .../Tests/cts-filter-tests.cs | 3552 +++++++------ .../Tests/cts-functions-tests.cs | 2022 ++++---- .../Tests/cts-index-selector-tests.cs | 280 +- .../Tests/cts-name-selector-tests.cs | 2370 +++++---- .../Tests/cts-slice-selector-tests.cs | 1040 ++-- .../Tests/cts-whitespace-tests.cs | 4604 +++++++++-------- test/Hyperbee.Json.Cts/generate_tests.ps1 | 61 +- .../Hyperbee.Json.Tests.csproj | 4 +- .../Query/JsonPathArrayTests.cs | 78 +- .../Query/JsonPathBookstoreTests.cs | 28 +- .../Query/JsonPathBracketNotationTests.cs | 132 +- .../Query/JsonPathDescentTests.cs | 8 +- .../Query/JsonPathDotNotationTests.cs | 10 +- .../Query/JsonPathFilterExpressionTests.cs | 28 +- .../Query/JsonPathRootOnScalarTests.cs | 12 +- .../Query/JsonPathUnionTests.cs | 12 +- .../{IJsonPathSource.cs => IJsonDocument.cs} | 2 +- .../TestSupport/JsonDocumentSource.cs | 13 - .../TestSupport/JsonElementHelper.cs | 31 + .../TestSupport/JsonNodeHelper.cs | 32 + .../TestSupport/JsonNodeSource.cs | 13 - .../TestSupport/JsonTestBase.cs | 26 +- .../TestSupport/JsonValueHelper.cs | 90 - .../TestSupport/TestHelper.cs | 49 + 40 files changed, 8518 insertions(+), 7228 deletions(-) create mode 100644 test/Hyperbee.Json.Cts/TestSupport/IJsonDocument.cs create mode 100644 test/Hyperbee.Json.Cts/TestSupport/JsonElementHelper.cs rename test/Hyperbee.Json.Cts/{TestHelper.cs => TestSupport/JsonNodeHelper.cs} (71%) create mode 100644 test/Hyperbee.Json.Cts/TestSupport/TestHelper.cs rename test/Hyperbee.Json.Tests/TestSupport/{IJsonPathSource.cs => IJsonDocument.cs} (85%) delete mode 100644 test/Hyperbee.Json.Tests/TestSupport/JsonDocumentSource.cs create mode 100644 test/Hyperbee.Json.Tests/TestSupport/JsonElementHelper.cs create mode 100644 test/Hyperbee.Json.Tests/TestSupport/JsonNodeHelper.cs delete mode 100644 test/Hyperbee.Json.Tests/TestSupport/JsonNodeSource.cs delete mode 100644 test/Hyperbee.Json.Tests/TestSupport/JsonValueHelper.cs create mode 100644 test/Hyperbee.Json.Tests/TestSupport/TestHelper.cs diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs index 10e61ff6..2c2487ac 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; -using System.Text.Json.Nodes; using Hyperbee.Json.Extensions; namespace Hyperbee.Json.Descriptors.Element; @@ -25,8 +24,11 @@ internal class ElementValueAccessor : IValueAccessor for ( var index = 0; index < length; index++, reverseIndex-- ) { var child = value[index]; + if ( includeValues || child.ValueKind is JsonValueKind.Array or JsonValueKind.Object ) + { results[reverseIndex] = (child, index.ToString(), SelectorKind.Index); + } } return results; @@ -201,7 +203,6 @@ public bool TryGetValueFromNode( JsonElement element, out IConvertible value ) return true; } - public bool TryGetFromPointer( in JsonElement element, JsonPathSegment segment, out JsonElement childValue ) { return element.TryGetFromJsonPathPointer( segment, out childValue ); diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs index 60fd9e50..010a4209 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs @@ -24,8 +24,11 @@ internal class NodeValueAccessor : IValueAccessor for ( var index = 0; index < length; index++, reverseIndex-- ) { var child = arrayValue[index]; + if ( includeValues || child is JsonObject or JsonArray ) + { results[reverseIndex] = (child, index.ToString(), SelectorKind.Index); + } } return results; @@ -202,7 +205,6 @@ public bool TryGetValueFromNode( JsonNode node, out IConvertible value ) return true; } - public bool TryGetFromPointer( in JsonNode node, JsonPathSegment segment, out JsonNode childValue ) { return node.TryGetFromJsonPathPointer( segment, out childValue ); diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs index df16c2a5..dc7f05b7 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs @@ -15,7 +15,7 @@ public static bool TryGetExpression( ref ParserState state, out Expressio var localState = state with { - Terminal = FilterParser.ArgClose + TerminalCharacter = FilterParser.ArgClose }; expression = FilterParser.Parse( ref localState ); // will recurse. diff --git a/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs b/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs index 7064fcdd..2947b4a2 100644 --- a/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs +++ b/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs @@ -30,7 +30,7 @@ internal Expression GetExpression( ref ParserState state ) { Item = [], IsArgument = true, - Terminal = i == _argumentCount - 1 + TerminalCharacter = i == _argumentCount - 1 ? FilterParser.ArgClose : FilterParser.ArgComma }; diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index 567e1d50..b0ba49fb 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -26,7 +26,7 @@ public abstract class FilterParser public class FilterParser : FilterParser { - internal static readonly ParameterExpression RuntimeContextExpression = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); + internal static readonly ParameterExpression RuntimeContextExpression = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); // must use a common instance internal static readonly ITypeDescriptor Descriptor = JsonTypeDescriptorRegistry.GetDescriptor(); public static Func, bool> Compile( ReadOnlySpan filter ) @@ -38,7 +38,7 @@ public static Func, bool> Compile( ReadOnlySpan filter ) { - filter = filter.Trim(); // remove leading and trailing whitespace to simplify parsing + filter = filter.Trim(); // remove leading and trailing whitespace simplifies parsing var pos = 0; var parenDepth = 0; @@ -160,7 +160,7 @@ static bool IsFinished( in ParserState state, char ch ) if ( state.Operator.IsNonOperator() == false ) return true; - return ch == state.Terminal; // terminal character [ '\0' or ',' or ')' ] + return ch == state.TerminalCharacter; // [ '\0' or ',' or ')' ] } } diff --git a/src/Hyperbee.Json/Filters/Parser/ParserState.cs b/src/Hyperbee.Json/Filters/Parser/ParserState.cs index 3b0c0794..bf734eee 100644 --- a/src/Hyperbee.Json/Filters/Parser/ParserState.cs +++ b/src/Hyperbee.Json/Filters/Parser/ParserState.cs @@ -14,22 +14,22 @@ internal ref struct ParserState public ref int ParenDepth; public Operator Operator { get; set; } - public char Terminal { get; init; } + public char TerminalCharacter { get; init; } public readonly ref int Pos; - internal ParserState( ReadOnlySpan buffer, ReadOnlySpan item, ref int pos, ref int parenDepth, Operator tokenType, char terminal ) + internal ParserState( ReadOnlySpan buffer, ReadOnlySpan item, ref int pos, ref int parenDepth, Operator tokenType, char terminalCharacter ) { Buffer = buffer; Item = item; Operator = tokenType; - Terminal = terminal; + TerminalCharacter = terminalCharacter; Pos = ref pos; ParenDepth = ref parenDepth; } public readonly bool EndOfBuffer => Pos >= Buffer.Length; - public readonly bool IsParsing => Pos < Buffer.Length && Previous != Terminal; + public readonly bool IsParsing => Pos < Buffer.Length && Previous != TerminalCharacter; public readonly char Current => Buffer[Pos]; public readonly char Previous => Buffer[Pos - 1]; diff --git a/src/Hyperbee.Json/Internal/SpanHelper.cs b/src/Hyperbee.Json/Internal/SpanHelper.cs index af3cf370..98fc9583 100644 --- a/src/Hyperbee.Json/Internal/SpanHelper.cs +++ b/src/Hyperbee.Json/Internal/SpanHelper.cs @@ -4,7 +4,7 @@ internal enum SpanUnescapeOptions { Single, SingleThenUnquote, - Mixed + Multiple } internal static class SpanHelper diff --git a/src/Hyperbee.Json/JsonPath.cs b/src/Hyperbee.Json/JsonPath.cs index 5df06880..51bfe4b1 100644 --- a/src/Hyperbee.Json/JsonPath.cs +++ b/src/Hyperbee.Json/JsonPath.cs @@ -111,7 +111,8 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N if ( key != null ) processor?.Invoke( parent, value, key, segmentNext ); - // yield matches + // yield match + if ( segmentNext.IsFinal ) { yield return value; @@ -237,7 +238,9 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N if ( nodeKind != NodeKind.Array ) continue; - foreach ( var index in EnumerateSlice( value, selector, accessor ) ) + var (upper, lower, step) = GetSliceRange( value, selector, accessor ); + + for ( var index = lower; step > 0 ? index < upper : index > upper; index += step ) { if ( accessor.TryGetElementAt( value, index, out var childValue ) ) stack.Push( value, childValue, index.ToString(), segmentNext ); @@ -286,30 +289,21 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N } while ( stack.TryPop( out args ) ); } - private static IEnumerable EnumerateSlice( TNode value, string sliceExpr, IValueAccessor accessor ) + private static (int Upper, int Lower, int Step) GetSliceRange( TNode value, string sliceExpr, IValueAccessor accessor ) { var length = accessor.GetArrayLength( value ); if ( length == 0 ) - yield break; + return (0, 0, 0); var (lower, upper, step) = JsonPathSliceSyntaxHelper.ParseExpression( sliceExpr, length, reverse: true ); - switch ( step ) + if ( step < 0 ) { - case > 0: - { - for ( var index = lower; index < upper; index += step ) - yield return index; - break; - } - case < 0: - { - for ( var index = upper; index > lower; index += step ) - yield return index; - break; - } + (lower, upper) = (upper, lower); } + + return (upper, lower, step); } [DebuggerDisplay( "Parent = {Parent}, Value = {Value}, {Segment}" )] diff --git a/src/Hyperbee.Json/JsonPathQueryParser.cs b/src/Hyperbee.Json/JsonPathQueryParser.cs index 17618700..750c47f7 100644 --- a/src/Hyperbee.Json/JsonPathQueryParser.cs +++ b/src/Hyperbee.Json/JsonPathQueryParser.cs @@ -266,19 +266,11 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD case SelectorKind.Name: ThrowIfInvalidQuotedName( selectorSpan ); + if ( escaped ) { - var builder = new SpanBuilder( selectorSpan.Length ); - try - { - SpanHelper.Unescape( selectorSpan, ref builder, SpanUnescapeOptions.SingleThenUnquote ); // unescape and then unquote - descriptor = GetSelectorDescriptor( selectorKind, builder, nullable: false ); - escaped = false; - } - finally // ensure builder is disposed - { - builder.Dispose(); - } + descriptor = GetUnescapedSelectorDescriptor( selectorKind, selectorSpan, nullable: false, SpanUnescapeOptions.SingleThenUnquote ); // unescape and then unquote + escaped = false; } else { @@ -288,19 +280,11 @@ private static JsonPathQuery QueryFactory( ReadOnlySpan query, bool allowD break; case SelectorKind.Filter: + if ( escaped ) { - var builder = new SpanBuilder( selectorSpan.Length ); - try - { - SpanHelper.Unescape( selectorSpan, ref builder, SpanUnescapeOptions.Mixed ); // unescape one or more strings - descriptor = GetSelectorDescriptor( selectorKind, builder ); - escaped = false; - } - finally // ensure builder is disposed - { - builder.Dispose(); - } + descriptor = GetUnescapedSelectorDescriptor( selectorKind, selectorSpan, nullable: true, SpanUnescapeOptions.Multiple ); // unescape one or more strings + escaped = false; } else { @@ -446,6 +430,23 @@ private static SelectorDescriptor GetSelectorDescriptor( SelectorKind selectorKi return new SelectorDescriptor { SelectorKind = selectorKind, Value = selectorValue }; } + private static SelectorDescriptor GetUnescapedSelectorDescriptor( SelectorKind selectorKind, ReadOnlySpan selectorSpan, bool nullable, SpanUnescapeOptions unescapeOptions ) + { + // SpanBuilder must be disposed, but it is a ref struct, so we can't use `using` + + var builder = new SpanBuilder( selectorSpan.Length ); + + try + { + SpanHelper.Unescape( selectorSpan, ref builder, unescapeOptions ); // unescape and then unquote + return GetSelectorDescriptor( selectorKind, builder, nullable ); + } + finally // ensure builder is disposed + { + builder.Dispose(); + } + } + [MethodImpl( MethodImplOptions.AggressiveInlining )] private static ReadOnlySpan GetSelectorSpan( State state, ReadOnlySpan buffer, int start, int stop ) { diff --git a/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs b/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs index c3bb999b..0faaa739 100644 --- a/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs +++ b/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs @@ -96,14 +96,14 @@ private static (int Lower, int Upper, int Step) GetBoundedValues( int start, int upper = Math.Min( Math.Max( normalizedStart, -1 ), length - 1 ); } - return reverse ? ReverseBoundedValues( lower, upper, step ) : (lower, upper, step); + return reverse ? ReverseValues( lower, upper, step ) : (lower, upper, step); static int Normalize( int value, int length ) => value >= 0 ? value : length + value; } // rewrite the slice to execute in reverse order - private static (int Lower, int Upper, int Step) ReverseBoundedValues( int lower, int upper, int step ) + private static (int Lower, int Upper, int Step) ReverseValues( int lower, int upper, int step ) { step = -step; diff --git a/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj b/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj index 9a9750fe..b10e3db7 100644 --- a/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj +++ b/test/Hyperbee.Json.Cts/Hyperbee.Json.Cts.csproj @@ -15,8 +15,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/test/Hyperbee.Json.Cts/TestSupport/IJsonDocument.cs b/test/Hyperbee.Json.Cts/TestSupport/IJsonDocument.cs new file mode 100644 index 00000000..cd6e9f3f --- /dev/null +++ b/test/Hyperbee.Json.Cts/TestSupport/IJsonDocument.cs @@ -0,0 +1,8 @@ + +namespace Hyperbee.Json.Cts.TestSupport; + +public interface IJsonDocument +{ + public dynamic Root { get; } + IEnumerable Select( string query ); +} diff --git a/test/Hyperbee.Json.Cts/TestSupport/JsonElementHelper.cs b/test/Hyperbee.Json.Cts/TestSupport/JsonElementHelper.cs new file mode 100644 index 00000000..7aa2e237 --- /dev/null +++ b/test/Hyperbee.Json.Cts/TestSupport/JsonElementHelper.cs @@ -0,0 +1,79 @@ +using System.Text.Json; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Cts.TestSupport; + +public class JsonPathDocument( string source ) : IJsonDocument +{ + private JsonDocument Document { get; } = JsonDocument.Parse( source ); + + public dynamic Root => Document.RootElement; + public IEnumerable Select( string query ) => Document.Select( query ).Cast(); +} + +internal static class JsonElementHelper +{ + private static JsonElement ConvertToJsonArraySet( JsonElement jsonElement ) + { + // Check if the jsonElement is already a JsonArray containing a JsonArray + if ( jsonElement.ValueKind == JsonValueKind.Array && jsonElement[0].ValueKind == JsonValueKind.Array ) + { + return jsonElement; // already a set + } + + // Create a new JsonArray set + using var stream = new MemoryStream(); + using ( var writer = new Utf8JsonWriter( stream ) ) + { + writer.WriteStartArray(); + writer.WriteStartArray(); + + foreach ( JsonElement element in jsonElement.EnumerateArray() ) + { + element.WriteTo( writer ); + } + + writer.WriteEndArray(); + writer.WriteEndArray(); + } + + stream.Seek( 0, SeekOrigin.Begin ); + return JsonDocument.Parse( stream ).RootElement; + } + + private static JsonElement ConvertToJsonArray( IEnumerable nodes ) + { + return CreateJsonArray( nodes ).RootElement; + + static JsonDocument CreateJsonArray( IEnumerable elements ) + { + using var stream = new MemoryStream(); + using ( var writer = new Utf8JsonWriter( stream ) ) + { + writer.WriteStartArray(); + foreach ( var element in elements ) + { + element.WriteTo( writer ); + } + + writer.WriteEndArray(); + } + + stream.Seek( 0, SeekOrigin.Begin ); + return JsonDocument.Parse( stream ); + } + } + + public static bool MatchAny( IEnumerable results, JsonElement expected ) + { + var expectedSet = ConvertToJsonArraySet( expected ); + var compare = ConvertToJsonArray( results ); + return expectedSet.EnumerateArray().Any( expect => expect.DeepEquals( compare ) ); + } + + public static bool MatchOne( IEnumerable results, JsonElement expected ) + { + var compare = ConvertToJsonArray( results ); + return expected.DeepEquals( compare ); + } +} diff --git a/test/Hyperbee.Json.Cts/TestHelper.cs b/test/Hyperbee.Json.Cts/TestSupport/JsonNodeHelper.cs similarity index 71% rename from test/Hyperbee.Json.Cts/TestHelper.cs rename to test/Hyperbee.Json.Cts/TestSupport/JsonNodeHelper.cs index 5a0670a8..065f77cb 100644 --- a/test/Hyperbee.Json.Cts/TestHelper.cs +++ b/test/Hyperbee.Json.Cts/TestSupport/JsonNodeHelper.cs @@ -1,12 +1,19 @@ using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; -namespace Hyperbee.Json.Cts; +namespace Hyperbee.Json.Cts.TestSupport; -internal static class TestHelper +public class JsonPathNode( string source ) : IJsonDocument { - // Result Helpers + private JsonNode? Document { get; } = JsonNode.Parse( source ); - public static JsonArray ConvertToJsonArraySet( JsonNode jsonNode ) + public dynamic Root => Document!; + public IEnumerable Select( string query ) => Document.Select( query ); +} + +internal static class JsonNodeHelper +{ + private static JsonArray ConvertToJsonArraySet( JsonNode jsonNode ) { if ( jsonNode is JsonArray jsonArray && jsonArray[0] is JsonArray ) return jsonArray; // already a set @@ -16,7 +23,7 @@ public static JsonArray ConvertToJsonArraySet( JsonNode jsonNode ) return jsonArraySet; } - public static JsonArray ConvertToJsonArray( IEnumerable nodes, bool force = false ) + private static JsonArray ConvertToJsonArray( IEnumerable nodes, bool force = false ) { var nodeArray = nodes.ToArray(); @@ -52,3 +59,4 @@ public static bool MatchOne( IEnumerable results, JsonNode expected ) return JsonNode.DeepEquals( expect, compare ); } } + diff --git a/test/Hyperbee.Json.Cts/TestSupport/TestHelper.cs b/test/Hyperbee.Json.Cts/TestSupport/TestHelper.cs new file mode 100644 index 00000000..440f4087 --- /dev/null +++ b/test/Hyperbee.Json.Cts/TestSupport/TestHelper.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Hyperbee.Json.Cts.TestSupport; + +internal static class TestHelper +{ + public static IJsonDocument Parse( Type target, string source ) + { + if ( target == typeof( JsonElement ) ) + return new JsonPathDocument( source ); + + if ( target == typeof( JsonNode ) ) + return new JsonPathNode( source ); + + throw new NotSupportedException(); + } + + public static bool MatchAny( Type target, IEnumerable results, dynamic expected ) + { + if ( target == typeof( JsonElement ) ) + return JsonElementHelper.MatchAny( results.Cast(), expected ); + + if ( target == typeof( JsonNode ) ) + return JsonNodeHelper.MatchAny( results.Cast(), expected ); + + throw new NotSupportedException(); + } + + public static bool MatchOne( Type target, IEnumerable results, dynamic expected ) + { + if ( target == typeof( JsonElement ) ) + return JsonElementHelper.MatchOne( results.Cast(), expected ); + + if ( target == typeof( JsonNode ) ) + return JsonNodeHelper.MatchOne( results.Cast(), expected ); + + throw new NotSupportedException(); + } +} diff --git a/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs index 43a743a7..d1849866 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs @@ -1,201 +1,223 @@ // This file was auto generated. +using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; +using Hyperbee.Json.Cts.TestSupport; -namespace Hyperbee.Json.Cts.Tests +namespace Hyperbee.Json.Cts.Tests; + +[TestClass] +public class CtsBasicTest { - [TestClass] - public class CtsBasicTest + [DataTestMethod( @"root (1)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_root_1( Type documentType ) { - - [TestMethod( @"root (1)" )] - public void Test_root_1() - { - var selector = "$"; - var document = JsonNode.Parse( - """ + const string selector = "$"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ [ "first", "second" ] ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"no leading whitespace (2)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_no_leading_whitespace_2( Type documentType ) + { + const string selector = " $"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"no trailing whitespace (3)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_no_trailing_whitespace_3( Type documentType ) + { + const string selector = "$ "; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"no leading whitespace (2)" )] - public void Test_no_leading_whitespace_2() - { - var selector = " $"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"no trailing whitespace (3)" )] - public void Test_no_trailing_whitespace_3() - { - var selector = "$ "; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"name shorthand (4)" )] - public void Test_name_shorthand_4() - { - var selector = "$.a"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"name shorthand (4)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_name_shorthand_4( Type documentType ) + { + const string selector = "$.a"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"name shorthand, extended unicode ☺ (5)" )] - public void Test_name_shorthand__extended_unicode___5() - { - var selector = "$.☺"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"name shorthand, extended unicode ☺ (5)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_name_shorthand__extended_unicode___5( Type documentType ) + { + const string selector = "$.☺"; + var document = TestHelper.Parse( documentType, + """ { "☺": "A", "b": "B" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"name shorthand, underscore (6)" )] - public void Test_name_shorthand__underscore_6() - { - var selector = "$._"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"name shorthand, underscore (6)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_name_shorthand__underscore_6( Type documentType ) + { + const string selector = "$._"; + var document = TestHelper.Parse( documentType, + """ { "_": "A", "_foo": "B" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"name shorthand, symbol (7)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_name_shorthand__symbol_7( Type documentType ) + { + const string selector = "$.&"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"name shorthand, number (8)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_name_shorthand__number_8( Type documentType ) + { + const string selector = "$.1"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"name shorthand, symbol (7)" )] - public void Test_name_shorthand__symbol_7() - { - var selector = "$.&"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"name shorthand, number (8)" )] - public void Test_name_shorthand__number_8() - { - var selector = "$.1"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"name shorthand, absent data (9)" )] - public void Test_name_shorthand__absent_data_9() - { - var selector = "$.c"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"name shorthand, absent data (9)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_name_shorthand__absent_data_9( Type documentType ) + { + const string selector = "$.c"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"name shorthand, array data (10)" )] - public void Test_name_shorthand__array_data_10() - { - var selector = "$.a"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"name shorthand, array data (10)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_name_shorthand__array_data_10( Type documentType ) + { + const string selector = "$.a"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"wildcard shorthand, object data (11)" )] - public void Test_wildcard_shorthand__object_data_11() - { - var selector = "$.*"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"wildcard shorthand, object data (11)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_wildcard_shorthand__object_data_11( Type documentType ) + { + const string selector = "$.*"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B" } """ ); - var results = document.Select( selector ); - var expectOneOf = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expectOneOf = TestHelper.Parse( documentType, + """ [ [ "A", @@ -206,66 +228,72 @@ public void Test_wildcard_shorthand__object_data_11() "A" ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchAny( results, expectOneOf! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchAny( documentType, results, expectOneOf ); + Assert.IsTrue( match ); + } - [TestMethod( @"wildcard shorthand, array data (12)" )] - public void Test_wildcard_shorthand__array_data_12() - { - var selector = "$.*"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"wildcard shorthand, array data (12)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_wildcard_shorthand__array_data_12( Type documentType ) + { + const string selector = "$.*"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "first", "second" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"wildcard selector, array data (13)" )] - public void Test_wildcard_selector__array_data_13() - { - var selector = "$[*]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"wildcard selector, array data (13)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_wildcard_selector__array_data_13( Type documentType ) + { + const string selector = "$[*]"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "first", "second" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"wildcard shorthand, then name shorthand (14)" )] - public void Test_wildcard_shorthand__then_name_shorthand_14() - { - var selector = "$.*.a"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"wildcard shorthand, then name shorthand (14)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_wildcard_shorthand__then_name_shorthand_14( Type documentType ) + { + const string selector = "$.*.a"; + var document = TestHelper.Parse( documentType, + """ { "x": { "a": "Ax", @@ -277,9 +305,9 @@ public void Test_wildcard_shorthand__then_name_shorthand_14() } } """ ); - var results = document.Select( selector ); - var expectOneOf = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expectOneOf = TestHelper.Parse( documentType, + """ [ [ "Ax", @@ -290,18 +318,20 @@ public void Test_wildcard_shorthand__then_name_shorthand_14() "Ax" ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchAny( results, expectOneOf! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchAny( documentType, results, expectOneOf ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors (15)" )] - public void Test_multiple_selectors_15() - { - var selector = "$[0,2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors (15)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors_15( Type documentType ) + { + const string selector = "$[0,2]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -315,34 +345,38 @@ public void Test_multiple_selectors_15() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 2 ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"multiple selectors, space instead of comma (16)" )] - public void Test_multiple_selectors__space_instead_of_comma_16() - { - var selector = "$[0 2]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"multiple selectors, name and index, array data (17)" )] - public void Test_multiple_selectors__name_and_index__array_data_17() - { - var selector = "$['a',1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, space instead of comma (16)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__space_instead_of_comma_16( Type documentType ) + { + const string selector = "$[0 2]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"multiple selectors, name and index, array data (17)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__name_and_index__array_data_17( Type documentType ) + { + const string selector = "$['a',1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -356,47 +390,51 @@ public void Test_multiple_selectors__name_and_index__array_data_17() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, name and index, object data (18)" )] - public void Test_multiple_selectors__name_and_index__object_data_18() - { - var selector = "$['a',1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, name and index, object data (18)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__name_and_index__object_data_18( Type documentType ) + { + const string selector = "$['a',1]"; + var document = TestHelper.Parse( documentType, + """ { "a": 1, "b": 2 } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, index and slice (19)" )] - public void Test_multiple_selectors__index_and_slice_19() - { - var selector = "$[1,5:7]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, index and slice (19)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__index_and_slice_19( Type documentType ) + { + const string selector = "$[1,5:7]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -410,26 +448,28 @@ public void Test_multiple_selectors__index_and_slice_19() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1, 5, 6 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, index and slice, overlapping (20)" )] - public void Test_multiple_selectors__index_and_slice__overlapping_20() - { - var selector = "$[1,0:3]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, index and slice, overlapping (20)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__index_and_slice__overlapping_20( Type documentType ) + { + const string selector = "$[1,0:3]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -443,27 +483,29 @@ public void Test_multiple_selectors__index_and_slice__overlapping_20() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1, 0, 1, 2 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, duplicate index (21)" )] - public void Test_multiple_selectors__duplicate_index_21() - { - var selector = "$[1,1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, duplicate index (21)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__duplicate_index_21( Type documentType ) + { + const string selector = "$[1,1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -477,25 +519,27 @@ public void Test_multiple_selectors__duplicate_index_21() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, wildcard and index (22)" )] - public void Test_multiple_selectors__wildcard_and_index_22() - { - var selector = "$[*,1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, wildcard and index (22)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__wildcard_and_index_22( Type documentType ) + { + const string selector = "$[*,1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -509,9 +553,9 @@ public void Test_multiple_selectors__wildcard_and_index_22() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -525,26 +569,28 @@ public void Test_multiple_selectors__wildcard_and_index_22() 9, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, wildcard and name (23)" )] - public void Test_multiple_selectors__wildcard_and_name_23() - { - var selector = "$[*,'a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, wildcard and name (23)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__wildcard_and_name_23( Type documentType ) + { + const string selector = "$[*,'a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B" } """ ); - var results = document.Select( selector ); - var expectOneOf = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expectOneOf = TestHelper.Parse( documentType, + """ [ [ "A", @@ -557,18 +603,20 @@ public void Test_multiple_selectors__wildcard_and_name_23() "A" ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchAny( results, expectOneOf! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchAny( documentType, results, expectOneOf ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, wildcard and slice (24)" )] - public void Test_multiple_selectors__wildcard_and_slice_24() - { - var selector = "$[*,0:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, wildcard and slice (24)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__wildcard_and_slice_24( Type documentType ) + { + const string selector = "$[*,0:2]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -582,9 +630,9 @@ public void Test_multiple_selectors__wildcard_and_slice_24() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -599,27 +647,29 @@ public void Test_multiple_selectors__wildcard_and_slice_24() 0, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, multiple wildcards (25)" )] - public void Test_multiple_selectors__multiple_wildcards_25() - { - var selector = "$[*,*]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, multiple wildcards (25)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__multiple_wildcards_25( Type documentType ) + { + const string selector = "$[*,*]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, 2 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -628,27 +678,31 @@ public void Test_multiple_selectors__multiple_wildcards_25() 1, 2 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"empty segment (26)" )] - public void Test_empty_segment_26() - { - var selector = "$[]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"descendant segment, index (27)" )] - public void Test_descendant_segment__index_27() - { - var selector = "$..[1]"; - var document = JsonNode.Parse( - """ + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"empty segment (26)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_empty_segment_26( Type documentType ) + { + const string selector = "$[]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"descendant segment, index (27)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__index_27( Type documentType ) + { + const string selector = "$..[1]"; + var document = TestHelper.Parse( documentType, + """ { "o": [ 0, @@ -660,25 +714,27 @@ public void Test_descendant_segment__index_27() ] } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1, 3 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"descendant segment, name shorthand (28)" )] - public void Test_descendant_segment__name_shorthand_28() - { - var selector = "$..a"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"descendant segment, name shorthand (28)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__name_shorthand_28( Type documentType ) + { + const string selector = "$..a"; + var document = TestHelper.Parse( documentType, + """ { "o": [ { @@ -690,73 +746,79 @@ public void Test_descendant_segment__name_shorthand_28() ] } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "b", "c" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"descendant segment, wildcard shorthand, array data (29)" )] - public void Test_descendant_segment__wildcard_shorthand__array_data_29() - { - var selector = "$..*"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"descendant segment, wildcard shorthand, array data (29)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__wildcard_shorthand__array_data_29( Type documentType ) + { + const string selector = "$..*"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"descendant segment, wildcard selector, array data (30)" )] - public void Test_descendant_segment__wildcard_selector__array_data_30() - { - var selector = "$..[*]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"descendant segment, wildcard selector, array data (30)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__wildcard_selector__array_data_30( Type documentType ) + { + const string selector = "$..[*]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"descendant segment, wildcard selector, nested arrays (31)" )] - public void Test_descendant_segment__wildcard_selector__nested_arrays_31() - { - var selector = "$..[*]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"descendant segment, wildcard selector, nested arrays (31)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__wildcard_selector__nested_arrays_31( Type documentType ) + { + const string selector = "$..[*]"; + var document = TestHelper.Parse( documentType, + """ [ [ [ @@ -768,9 +830,9 @@ public void Test_descendant_segment__wildcard_selector__nested_arrays_31() ] ] """ ); - var results = document.Select( selector ); - var expectOneOf = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expectOneOf = TestHelper.Parse( documentType, + """ [ [ [ @@ -803,18 +865,20 @@ public void Test_descendant_segment__wildcard_selector__nested_arrays_31() 1 ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchAny( results, expectOneOf! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchAny( documentType, results, expectOneOf ); + Assert.IsTrue( match ); + } - [TestMethod( @"descendant segment, wildcard selector, nested objects (32)" )] - public void Test_descendant_segment__wildcard_selector__nested_objects_32() - { - var selector = "$..[*]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"descendant segment, wildcard selector, nested objects (32)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__wildcard_selector__nested_objects_32( Type documentType ) + { + const string selector = "$..[*]"; + var document = TestHelper.Parse( documentType, + """ { "a": { "c": { @@ -826,9 +890,9 @@ public void Test_descendant_segment__wildcard_selector__nested_objects_32() } } """ ); - var results = document.Select( selector ); - var expectOneOf = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expectOneOf = TestHelper.Parse( documentType, + """ [ [ { @@ -921,40 +985,44 @@ public void Test_descendant_segment__wildcard_selector__nested_objects_32() 1 ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchAny( results, expectOneOf! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchAny( documentType, results, expectOneOf ); + Assert.IsTrue( match ); + } - [TestMethod( @"descendant segment, wildcard shorthand, object data (33)" )] - public void Test_descendant_segment__wildcard_shorthand__object_data_33() - { - var selector = "$..*"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"descendant segment, wildcard shorthand, object data (33)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__wildcard_shorthand__object_data_33( Type documentType ) + { + const string selector = "$..*"; + var document = TestHelper.Parse( documentType, + """ { "a": "b" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "b" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"descendant segment, wildcard shorthand, nested data (34)" )] - public void Test_descendant_segment__wildcard_shorthand__nested_data_34() - { - var selector = "$..*"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"descendant segment, wildcard shorthand, nested data (34)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__wildcard_shorthand__nested_data_34( Type documentType ) + { + const string selector = "$..*"; + var document = TestHelper.Parse( documentType, + """ { "o": [ { @@ -963,9 +1031,9 @@ public void Test_descendant_segment__wildcard_shorthand__nested_data_34() ] } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ [ { @@ -977,18 +1045,20 @@ public void Test_descendant_segment__wildcard_shorthand__nested_data_34() }, "b" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"descendant segment, multiple selectors (35)" )] - public void Test_descendant_segment__multiple_selectors_35() - { - var selector = "$..['a','d']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"descendant segment, multiple selectors (35)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__multiple_selectors_35( Type documentType ) + { + const string selector = "$..['a','d']"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -1000,27 +1070,29 @@ public void Test_descendant_segment__multiple_selectors_35() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "b", "e", "c", "f" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"descendant segment, object traversal, multiple selectors (36)" )] - public void Test_descendant_segment__object_traversal__multiple_selectors_36() - { - var selector = "$..['a','d']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"descendant segment, object traversal, multiple selectors (36)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_descendant_segment__object_traversal__multiple_selectors_36( Type documentType ) + { + const string selector = "$..['a','d']"; + var document = TestHelper.Parse( documentType, + """ { "x": { "a": "b", @@ -1032,9 +1104,9 @@ public void Test_descendant_segment__object_traversal__multiple_selectors_36() } } """ ); - var results = document.Select( selector ); - var expectOneOf = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expectOneOf = TestHelper.Parse( documentType, + """ [ [ "b", @@ -1049,20 +1121,22 @@ public void Test_descendant_segment__object_traversal__multiple_selectors_36() "e" ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchAny( results, expectOneOf! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchAny( documentType, results, expectOneOf ); + Assert.IsTrue( match ); + } - [TestMethod( @"bald descendant segment (37)" )] - public void Test_bald_descendant_segment_37() - { - var selector = "$.."; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"bald descendant segment (37)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_bald_descendant_segment_37( Type documentType ) + { + const string selector = "$.."; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); } } + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs index c44b1882..7a5d79fa 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs @@ -1,28 +1,30 @@ // This file was auto generated. +using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; +using Hyperbee.Json.Cts.TestSupport; -namespace Hyperbee.Json.Cts.Tests +namespace Hyperbee.Json.Cts.Tests; + +[TestClass] +public class CtsFilterTest { - [TestClass] - public class CtsFilterTest + [DataTestMethod( @"existence, without segments (1)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_existence__without_segments_1( Type documentType ) { - - [TestMethod( @"existence, without segments (1)" )] - public void Test_existence__without_segments_1() - { - var selector = "$[?@]"; - var document = JsonNode.Parse( - """ + const string selector = "$[?@]"; + var document = TestHelper.Parse( documentType, + """ { "a": 1, "b": null } """ ); - var results = document.Select( selector ); - var expectOneOf = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expectOneOf = TestHelper.Parse( documentType, + """ [ [ 1, @@ -33,18 +35,20 @@ public void Test_existence__without_segments_1() 1 ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchAny( results, expectOneOf! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchAny( documentType, results, expectOneOf ); + Assert.IsTrue( match ); + } - [TestMethod( @"existence (2)" )] - public void Test_existence_2() - { - var selector = "$[?@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"existence (2)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_existence_2( Type documentType ) + { + const string selector = "$[?@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -56,27 +60,29 @@ public void Test_existence_2() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"existence, present with null (3)" )] - public void Test_existence__present_with_null_3() - { - var selector = "$[?@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"existence, present with null (3)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_existence__present_with_null_3( Type documentType ) + { + const string selector = "$[?@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": null, @@ -88,27 +94,29 @@ public void Test_existence__present_with_null_3() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": null, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals string, single quotes (4)" )] - public void Test_equals_string__single_quotes_4() - { - var selector = "$[?@.a=='b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals string, single quotes (4)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_string__single_quotes_4( Type documentType ) + { + const string selector = "$[?@.a=='b']"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -120,27 +128,29 @@ public void Test_equals_string__single_quotes_4() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals numeric string, single quotes (5)" )] - public void Test_equals_numeric_string__single_quotes_5() - { - var selector = "$[?@.a=='1']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals numeric string, single quotes (5)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_numeric_string__single_quotes_5( Type documentType ) + { + const string selector = "$[?@.a=='1']"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "1", @@ -152,27 +162,29 @@ public void Test_equals_numeric_string__single_quotes_5() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "1", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals string, double quotes (6)" )] - public void Test_equals_string__double_quotes_6() - { - var selector = "$[?@.a==\"b\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals string, double quotes (6)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_string__double_quotes_6( Type documentType ) + { + const string selector = "$[?@.a==\"b\"]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -184,27 +196,29 @@ public void Test_equals_string__double_quotes_6() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals numeric string, double quotes (7)" )] - public void Test_equals_numeric_string__double_quotes_7() - { - var selector = "$[?@.a==\"1\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals numeric string, double quotes (7)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_numeric_string__double_quotes_7( Type documentType ) + { + const string selector = "$[?@.a==\"1\"]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "1", @@ -216,27 +230,29 @@ public void Test_equals_numeric_string__double_quotes_7() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "1", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number (8)" )] - public void Test_equals_number_8() - { - var selector = "$[?@.a==1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number (8)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number_8( Type documentType ) + { + const string selector = "$[?@.a==1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -256,27 +272,29 @@ public void Test_equals_number_8() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals null (9)" )] - public void Test_equals_null_9() - { - var selector = "$[?@.a==null]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals null (9)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_null_9( Type documentType ) + { + const string selector = "$[?@.a==null]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": null, @@ -288,27 +306,29 @@ public void Test_equals_null_9() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": null, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals null, absent from data (10)" )] - public void Test_equals_null__absent_from_data_10() - { - var selector = "$[?@.a==null]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals null, absent from data (10)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_null__absent_from_data_10( Type documentType ) + { + const string selector = "$[?@.a==null]"; + var document = TestHelper.Parse( documentType, + """ [ { "d": "e" @@ -319,22 +339,24 @@ public void Test_equals_null__absent_from_data_10() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals true (11)" )] - public void Test_equals_true_11() - { - var selector = "$[?@.a==true]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals true (11)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_true_11( Type documentType ) + { + const string selector = "$[?@.a==true]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": true, @@ -346,27 +368,29 @@ public void Test_equals_true_11() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": true, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals false (12)" )] - public void Test_equals_false_12() - { - var selector = "$[?@.a==false]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals false (12)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_false_12( Type documentType ) + { + const string selector = "$[?@.a==false]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": false, @@ -378,27 +402,29 @@ public void Test_equals_false_12() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": false, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals self (13)" )] - public void Test_equals_self_13() - { - var selector = "$[?@==@]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals self (13)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_self_13( Type documentType ) + { + const string selector = "$[?@==@]"; + var document = TestHelper.Parse( documentType, + """ [ 1, null, @@ -411,9 +437,9 @@ public void Test_equals_self_13() ] ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1, null, @@ -425,18 +451,20 @@ public void Test_equals_self_13() false ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"deep equality, arrays (14)" )] - public void Test_deep_equality__arrays_14() - { - var selector = "$[?@.a==@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"deep equality, arrays (14)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_deep_equality__arrays_14( Type documentType ) + { + const string selector = "$[?@.a==@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": false, @@ -499,9 +527,9 @@ public void Test_deep_equality__arrays_14() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": [ @@ -522,18 +550,20 @@ public void Test_deep_equality__arrays_14() ] } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"deep equality, objects (15)" )] - public void Test_deep_equality__objects_15() - { - var selector = "$[?@.a==@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"deep equality, objects (15)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_deep_equality__objects_15( Type documentType ) + { + const string selector = "$[?@.a==@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": false, @@ -599,9 +629,9 @@ public void Test_deep_equality__objects_15() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": { @@ -632,18 +662,20 @@ public void Test_deep_equality__objects_15() } } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals string, single quotes (16)" )] - public void Test_not_equals_string__single_quotes_16() - { - var selector = "$[?@.a!='b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals string, single quotes (16)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_string__single_quotes_16( Type documentType ) + { + const string selector = "$[?@.a!='b']"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -655,27 +687,29 @@ public void Test_not_equals_string__single_quotes_16() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "c", "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals numeric string, single quotes (17)" )] - public void Test_not_equals_numeric_string__single_quotes_17() - { - var selector = "$[?@.a!='1']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals numeric string, single quotes (17)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_numeric_string__single_quotes_17( Type documentType ) + { + const string selector = "$[?@.a!='1']"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "1", @@ -687,27 +721,29 @@ public void Test_not_equals_numeric_string__single_quotes_17() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals string, single quotes, different type (18)" )] - public void Test_not_equals_string__single_quotes__different_type_18() - { - var selector = "$[?@.a!='b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals string, single quotes, different type (18)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_string__single_quotes__different_type_18( Type documentType ) + { + const string selector = "$[?@.a!='b']"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -719,27 +755,29 @@ public void Test_not_equals_string__single_quotes__different_type_18() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals string, double quotes (19)" )] - public void Test_not_equals_string__double_quotes_19() - { - var selector = "$[?@.a!=\"b\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals string, double quotes (19)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_string__double_quotes_19( Type documentType ) + { + const string selector = "$[?@.a!=\"b\"]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -751,27 +789,29 @@ public void Test_not_equals_string__double_quotes_19() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "c", "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals numeric string, double quotes (20)" )] - public void Test_not_equals_numeric_string__double_quotes_20() - { - var selector = "$[?@.a!=\"1\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals numeric string, double quotes (20)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_numeric_string__double_quotes_20( Type documentType ) + { + const string selector = "$[?@.a!=\"1\"]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "1", @@ -783,27 +823,29 @@ public void Test_not_equals_numeric_string__double_quotes_20() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals string, double quotes, different types (21)" )] - public void Test_not_equals_string__double_quotes__different_types_21() - { - var selector = "$[?@.a!=\"b\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals string, double quotes, different types (21)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_string__double_quotes__different_types_21( Type documentType ) + { + const string selector = "$[?@.a!=\"b\"]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -815,27 +857,29 @@ public void Test_not_equals_string__double_quotes__different_types_21() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals number (22)" )] - public void Test_not_equals_number_22() - { - var selector = "$[?@.a!=1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals number (22)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_number_22( Type documentType ) + { + const string selector = "$[?@.a!=1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -851,9 +895,9 @@ public void Test_not_equals_number_22() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 2, @@ -864,18 +908,20 @@ public void Test_not_equals_number_22() "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals number, different types (23)" )] - public void Test_not_equals_number__different_types_23() - { - var selector = "$[?@.a!=1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals number, different types (23)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_number__different_types_23( Type documentType ) + { + const string selector = "$[?@.a!=1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -887,27 +933,29 @@ public void Test_not_equals_number__different_types_23() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "c", "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals null (24)" )] - public void Test_not_equals_null_24() - { - var selector = "$[?@.a!=null]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals null (24)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_null_24( Type documentType ) + { + const string selector = "$[?@.a!=null]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": null, @@ -919,27 +967,29 @@ public void Test_not_equals_null_24() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "c", "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals null, absent from data (25)" )] - public void Test_not_equals_null__absent_from_data_25() - { - var selector = "$[?@.a!=null]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals null, absent from data (25)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_null__absent_from_data_25( Type documentType ) + { + const string selector = "$[?@.a!=null]"; + var document = TestHelper.Parse( documentType, + """ [ { "d": "e" @@ -950,9 +1000,9 @@ public void Test_not_equals_null__absent_from_data_25() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "d": "e" @@ -962,18 +1012,20 @@ public void Test_not_equals_null__absent_from_data_25() "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals true (26)" )] - public void Test_not_equals_true_26() - { - var selector = "$[?@.a!=true]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals true (26)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_true_26( Type documentType ) + { + const string selector = "$[?@.a!=true]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": true, @@ -985,27 +1037,29 @@ public void Test_not_equals_true_26() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "c", "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"not-equals false (27)" )] - public void Test_not_equals_false_27() - { - var selector = "$[?@.a!=false]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"not-equals false (27)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_equals_false_27( Type documentType ) + { + const string selector = "$[?@.a!=false]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": false, @@ -1017,27 +1071,29 @@ public void Test_not_equals_false_27() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "c", "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"less than string, single quotes (28)" )] - public void Test_less_than_string__single_quotes_28() - { - var selector = "$[?@.a<'c']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"less than string, single quotes (28)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_less_than_string__single_quotes_28( Type documentType ) + { + const string selector = "$[?@.a<'c']"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -1049,27 +1105,29 @@ public void Test_less_than_string__single_quotes_28() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"less than string, double quotes (29)" )] - public void Test_less_than_string__double_quotes_29() - { - var selector = "$[?@.a<\"c\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"less than string, double quotes (29)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_less_than_string__double_quotes_29( Type documentType ) + { + const string selector = "$[?@.a<\"c\"]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -1081,27 +1139,29 @@ public void Test_less_than_string__double_quotes_29() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"less than number (30)" )] - public void Test_less_than_number_30() - { - var selector = "$[?@.a<10]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"less than number (30)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_less_than_number_30( Type documentType ) + { + const string selector = "$[?@.a<10]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -1121,27 +1181,29 @@ public void Test_less_than_number_30() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"less than null (31)" )] - public void Test_less_than_null_31() - { - var selector = "$[?@.a( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"non-singular query in comparison, all children (65)" )] - public void Test_non_singular_query_in_comparison__all_children_65() - { - var selector = "$[?@[*]==0]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"non-singular query in comparison, all children (65)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_non_singular_query_in_comparison__all_children_65( Type documentType ) + { + const string selector = "$[?@[*]==0]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"non-singular query in comparison, descendants (66)" )] - public void Test_non_singular_query_in_comparison__descendants_66() - { - var selector = "$[?@..a==0]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"non-singular query in comparison, descendants (66)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_non_singular_query_in_comparison__descendants_66( Type documentType ) + { + const string selector = "$[?@..a==0]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"non-singular query in comparison, combined (67)" )] - public void Test_non_singular_query_in_comparison__combined_67() - { - var selector = "$[?@.a[*].a==0]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"non-singular query in comparison, combined (67)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_non_singular_query_in_comparison__combined_67( Type documentType ) + { + const string selector = "$[?@.a[*].a==0]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"nested (68)" )] - public void Test_nested_68() - { - var selector = "$[?@[?@>1]]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"nested (68)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_nested_68( Type documentType ) + { + const string selector = "$[?@[?@>1]]"; + var document = TestHelper.Parse( documentType, + """ [ [ 0 @@ -2354,9 +2490,9 @@ public void Test_nested_68() ] ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ [ 0, @@ -2367,38 +2503,42 @@ public void Test_nested_68() 42 ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"name segment on primitive, selects nothing (69)" )] - public void Test_name_segment_on_primitive__selects_nothing_69() - { - var selector = "$[?@.a == 1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"name segment on primitive, selects nothing (69)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_name_segment_on_primitive__selects_nothing_69( Type documentType ) + { + const string selector = "$[?@.a == 1]"; + var document = TestHelper.Parse( documentType, + """ { "a": 1 } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"name segment on array, selects nothing (70)" )] - public void Test_name_segment_on_array__selects_nothing_70() - { - var selector = "$[?@['0'] == 5]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"name segment on array, selects nothing (70)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_name_segment_on_array__selects_nothing_70( Type documentType ) + { + const string selector = "$[?@['0'] == 5]"; + var document = TestHelper.Parse( documentType, + """ [ [ 5, @@ -2406,314 +2546,378 @@ public void Test_name_segment_on_array__selects_nothing_70() ] ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"index segment on object, selects nothing (71)" )] - public void Test_index_segment_on_object__selects_nothing_71() - { - var selector = "$[?@[0] == 5]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"index segment on object, selects nothing (71)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_index_segment_on_object__selects_nothing_71( Type documentType ) + { + const string selector = "$[?@[0] == 5]"; + var document = TestHelper.Parse( documentType, + """ [ { "0": 5 } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"relative non-singular query, index, equal (72)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__index__equal_72( Type documentType ) + { + const string selector = "$[?(@[0, 0]==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, index, not equal (73)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__index__not_equal_73( Type documentType ) + { + const string selector = "$[?(@[0, 0]!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, index, less-or-equal (74)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__index__less_or_equal_74( Type documentType ) + { + const string selector = "$[?(@[0, 0]<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"relative non-singular query, index, equal (72)" )] - public void Test_relative_non_singular_query__index__equal_72() - { - var selector = "$[?(@[0, 0]==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, index, not equal (73)" )] - public void Test_relative_non_singular_query__index__not_equal_73() - { - var selector = "$[?(@[0, 0]!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, index, less-or-equal (74)" )] - public void Test_relative_non_singular_query__index__less_or_equal_74() - { - var selector = "$[?(@[0, 0]<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, name, equal (75)" )] - public void Test_relative_non_singular_query__name__equal_75() - { - var selector = "$[?(@['a', 'a']==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, name, not equal (76)" )] - public void Test_relative_non_singular_query__name__not_equal_76() - { - var selector = "$[?(@['a', 'a']!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, name, less-or-equal (77)" )] - public void Test_relative_non_singular_query__name__less_or_equal_77() - { - var selector = "$[?(@['a', 'a']<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, combined, equal (78)" )] - public void Test_relative_non_singular_query__combined__equal_78() - { - var selector = "$[?(@[0, '0']==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, combined, not equal (79)" )] - public void Test_relative_non_singular_query__combined__not_equal_79() - { - var selector = "$[?(@[0, '0']!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, combined, less-or-equal (80)" )] - public void Test_relative_non_singular_query__combined__less_or_equal_80() - { - var selector = "$[?(@[0, '0']<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, wildcard, equal (81)" )] - public void Test_relative_non_singular_query__wildcard__equal_81() - { - var selector = "$[?(@.*==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, wildcard, not equal (82)" )] - public void Test_relative_non_singular_query__wildcard__not_equal_82() - { - var selector = "$[?(@.*!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, wildcard, less-or-equal (83)" )] - public void Test_relative_non_singular_query__wildcard__less_or_equal_83() - { - var selector = "$[?(@.*<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, slice, equal (84)" )] - public void Test_relative_non_singular_query__slice__equal_84() - { - var selector = "$[?(@[0:0]==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, slice, not equal (85)" )] - public void Test_relative_non_singular_query__slice__not_equal_85() - { - var selector = "$[?(@[0:0]!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"relative non-singular query, slice, less-or-equal (86)" )] - public void Test_relative_non_singular_query__slice__less_or_equal_86() - { - var selector = "$[?(@[0:0]<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, index, equal (87)" )] - public void Test_absolute_non_singular_query__index__equal_87() - { - var selector = "$[?($[0, 0]==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, index, not equal (88)" )] - public void Test_absolute_non_singular_query__index__not_equal_88() - { - var selector = "$[?($[0, 0]!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, index, less-or-equal (89)" )] - public void Test_absolute_non_singular_query__index__less_or_equal_89() - { - var selector = "$[?($[0, 0]<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, name, equal (90)" )] - public void Test_absolute_non_singular_query__name__equal_90() - { - var selector = "$[?($['a', 'a']==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, name, not equal (91)" )] - public void Test_absolute_non_singular_query__name__not_equal_91() - { - var selector = "$[?($['a', 'a']!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, name, less-or-equal (92)" )] - public void Test_absolute_non_singular_query__name__less_or_equal_92() - { - var selector = "$[?($['a', 'a']<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, combined, equal (93)" )] - public void Test_absolute_non_singular_query__combined__equal_93() - { - var selector = "$[?($[0, '0']==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, combined, not equal (94)" )] - public void Test_absolute_non_singular_query__combined__not_equal_94() - { - var selector = "$[?($[0, '0']!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, combined, less-or-equal (95)" )] - public void Test_absolute_non_singular_query__combined__less_or_equal_95() - { - var selector = "$[?($[0, '0']<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, wildcard, equal (96)" )] - public void Test_absolute_non_singular_query__wildcard__equal_96() - { - var selector = "$[?($.*==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, wildcard, not equal (97)" )] - public void Test_absolute_non_singular_query__wildcard__not_equal_97() - { - var selector = "$[?($.*!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, wildcard, less-or-equal (98)" )] - public void Test_absolute_non_singular_query__wildcard__less_or_equal_98() - { - var selector = "$[?($.*<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, slice, equal (99)" )] - public void Test_absolute_non_singular_query__slice__equal_99() - { - var selector = "$[?($[0:0]==42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, slice, not equal (100)" )] - public void Test_absolute_non_singular_query__slice__not_equal_100() - { - var selector = "$[?($[0:0]!=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"absolute non-singular query, slice, less-or-equal (101)" )] - public void Test_absolute_non_singular_query__slice__less_or_equal_101() - { - var selector = "$[?($[0:0]<=42)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"multiple selectors (102)" )] - public void Test_multiple_selectors_102() - { - var selector = "$[?@.a,?@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"relative non-singular query, name, equal (75)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__name__equal_75( Type documentType ) + { + const string selector = "$[?(@['a', 'a']==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, name, not equal (76)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__name__not_equal_76( Type documentType ) + { + const string selector = "$[?(@['a', 'a']!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, name, less-or-equal (77)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__name__less_or_equal_77( Type documentType ) + { + const string selector = "$[?(@['a', 'a']<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, combined, equal (78)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__combined__equal_78( Type documentType ) + { + const string selector = "$[?(@[0, '0']==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, combined, not equal (79)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__combined__not_equal_79( Type documentType ) + { + const string selector = "$[?(@[0, '0']!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, combined, less-or-equal (80)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__combined__less_or_equal_80( Type documentType ) + { + const string selector = "$[?(@[0, '0']<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, wildcard, equal (81)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__wildcard__equal_81( Type documentType ) + { + const string selector = "$[?(@.*==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, wildcard, not equal (82)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__wildcard__not_equal_82( Type documentType ) + { + const string selector = "$[?(@.*!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, wildcard, less-or-equal (83)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__wildcard__less_or_equal_83( Type documentType ) + { + const string selector = "$[?(@.*<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, slice, equal (84)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__slice__equal_84( Type documentType ) + { + const string selector = "$[?(@[0:0]==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, slice, not equal (85)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__slice__not_equal_85( Type documentType ) + { + const string selector = "$[?(@[0:0]!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"relative non-singular query, slice, less-or-equal (86)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_relative_non_singular_query__slice__less_or_equal_86( Type documentType ) + { + const string selector = "$[?(@[0:0]<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, index, equal (87)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__index__equal_87( Type documentType ) + { + const string selector = "$[?($[0, 0]==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, index, not equal (88)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__index__not_equal_88( Type documentType ) + { + const string selector = "$[?($[0, 0]!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, index, less-or-equal (89)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__index__less_or_equal_89( Type documentType ) + { + const string selector = "$[?($[0, 0]<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, name, equal (90)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__name__equal_90( Type documentType ) + { + const string selector = "$[?($['a', 'a']==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, name, not equal (91)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__name__not_equal_91( Type documentType ) + { + const string selector = "$[?($['a', 'a']!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, name, less-or-equal (92)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__name__less_or_equal_92( Type documentType ) + { + const string selector = "$[?($['a', 'a']<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, combined, equal (93)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__combined__equal_93( Type documentType ) + { + const string selector = "$[?($[0, '0']==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, combined, not equal (94)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__combined__not_equal_94( Type documentType ) + { + const string selector = "$[?($[0, '0']!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, combined, less-or-equal (95)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__combined__less_or_equal_95( Type documentType ) + { + const string selector = "$[?($[0, '0']<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, wildcard, equal (96)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__wildcard__equal_96( Type documentType ) + { + const string selector = "$[?($.*==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, wildcard, not equal (97)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__wildcard__not_equal_97( Type documentType ) + { + const string selector = "$[?($.*!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, wildcard, less-or-equal (98)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__wildcard__less_or_equal_98( Type documentType ) + { + const string selector = "$[?($.*<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, slice, equal (99)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__slice__equal_99( Type documentType ) + { + const string selector = "$[?($[0:0]==42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, slice, not equal (100)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__slice__not_equal_100( Type documentType ) + { + const string selector = "$[?($[0:0]!=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"absolute non-singular query, slice, less-or-equal (101)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_absolute_non_singular_query__slice__less_or_equal_101( Type documentType ) + { + const string selector = "$[?($[0:0]<=42)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"multiple selectors (102)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors_102( Type documentType ) + { + const string selector = "$[?@.a,?@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2725,9 +2929,9 @@ public void Test_multiple_selectors_102() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2738,18 +2942,20 @@ public void Test_multiple_selectors_102() "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, comparison (103)" )] - public void Test_multiple_selectors__comparison_103() - { - var selector = "$[?@.a=='b',?@.b=='x']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, comparison (103)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__comparison_103( Type documentType ) + { + const string selector = "$[?@.a=='b',?@.b=='x']"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2761,27 +2967,29 @@ public void Test_multiple_selectors__comparison_103() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, overlapping (104)" )] - public void Test_multiple_selectors__overlapping_104() - { - var selector = "$[?@.a,?@.d]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, overlapping (104)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__overlapping_104( Type documentType ) + { + const string selector = "$[?@.a,?@.d]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2793,9 +3001,9 @@ public void Test_multiple_selectors__overlapping_104() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2810,18 +3018,20 @@ public void Test_multiple_selectors__overlapping_104() "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, filter and index (105)" )] - public void Test_multiple_selectors__filter_and_index_105() - { - var selector = "$[?@.a,1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, filter and index (105)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__filter_and_index_105( Type documentType ) + { + const string selector = "$[?@.a,1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2833,9 +3043,9 @@ public void Test_multiple_selectors__filter_and_index_105() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2846,18 +3056,20 @@ public void Test_multiple_selectors__filter_and_index_105() "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, filter and wildcard (106)" )] - public void Test_multiple_selectors__filter_and_wildcard_106() - { - var selector = "$[?@.a,*]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, filter and wildcard (106)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__filter_and_wildcard_106( Type documentType ) + { + const string selector = "$[?@.a,*]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2869,9 +3081,9 @@ public void Test_multiple_selectors__filter_and_wildcard_106() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2886,18 +3098,20 @@ public void Test_multiple_selectors__filter_and_wildcard_106() "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, filter and slice (107)" )] - public void Test_multiple_selectors__filter_and_slice_107() - { - var selector = "$[?@.a,1:]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, filter and slice (107)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__filter_and_slice_107( Type documentType ) + { + const string selector = "$[?@.a,1:]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2912,9 +3126,9 @@ public void Test_multiple_selectors__filter_and_slice_107() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2928,18 +3142,20 @@ public void Test_multiple_selectors__filter_and_slice_107() "g": "h" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"multiple selectors, comparison filter, index and slice (108)" )] - public void Test_multiple_selectors__comparison_filter__index_and_slice_108() - { - var selector = "$[1, ?@.a=='b', 1:]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"multiple selectors, comparison filter, index and slice (108)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_multiple_selectors__comparison_filter__index_and_slice_108( Type documentType ) + { + const string selector = "$[1, ?@.a=='b', 1:]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -2951,9 +3167,9 @@ public void Test_multiple_selectors__comparison_filter__index_and_slice_108() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "b": "c", @@ -2968,18 +3184,20 @@ public void Test_multiple_selectors__comparison_filter__index_and_slice_108() "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number, zero and negative zero (109)" )] - public void Test_equals_number__zero_and_negative_zero_109() - { - var selector = "$[?@.a==-0]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number, zero and negative zero (109)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__zero_and_negative_zero_109( Type documentType ) + { + const string selector = "$[?@.a==-0]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 0, @@ -2995,27 +3213,29 @@ public void Test_equals_number__zero_and_negative_zero_109() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 0, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number, with and without decimal fraction (110)" )] - public void Test_equals_number__with_and_without_decimal_fraction_110() - { - var selector = "$[?@.a==1.0]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number, with and without decimal fraction (110)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__with_and_without_decimal_fraction_110( Type documentType ) + { + const string selector = "$[?@.a==1.0]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3031,27 +3251,29 @@ public void Test_equals_number__with_and_without_decimal_fraction_110() } ] """ ); - var results = document.Select( selector ).ToArray(); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number, exponent (111)" )] - public void Test_equals_number__exponent_111() - { - var selector = "$[?@.a==1e2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number, exponent (111)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__exponent_111( Type documentType ) + { + const string selector = "$[?@.a==1e2]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 100, @@ -3067,27 +3289,29 @@ public void Test_equals_number__exponent_111() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 100, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number, positive exponent (112)" )] - public void Test_equals_number__positive_exponent_112() - { - var selector = "$[?@.a==1e+2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number, positive exponent (112)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__positive_exponent_112( Type documentType ) + { + const string selector = "$[?@.a==1e+2]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 100, @@ -3103,27 +3327,29 @@ public void Test_equals_number__positive_exponent_112() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 100, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number, negative exponent (113)" )] - public void Test_equals_number__negative_exponent_113() - { - var selector = "$[?@.a==1e-2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number, negative exponent (113)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__negative_exponent_113( Type documentType ) + { + const string selector = "$[?@.a==1e-2]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 0.01, @@ -3139,27 +3365,29 @@ public void Test_equals_number__negative_exponent_113() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 0.01, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number, decimal fraction (114)" )] - public void Test_equals_number__decimal_fraction_114() - { - var selector = "$[?@.a==1.1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number, decimal fraction (114)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__decimal_fraction_114( Type documentType ) + { + const string selector = "$[?@.a==1.1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1.1, @@ -3175,36 +3403,40 @@ public void Test_equals_number__decimal_fraction_114() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1.1, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number, decimal fraction, no fractional digit (115)" )] - public void Test_equals_number__decimal_fraction__no_fractional_digit_115() - { - var selector = "$[?@.a==1.]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"equals number, decimal fraction, no fractional digit (115)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__decimal_fraction__no_fractional_digit_115( Type documentType ) + { + const string selector = "$[?@.a==1.]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"equals number, decimal fraction, exponent (116)" )] - public void Test_equals_number__decimal_fraction__exponent_116() - { - var selector = "$[?@.a==1.1e2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number, decimal fraction, exponent (116)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__decimal_fraction__exponent_116( Type documentType ) + { + const string selector = "$[?@.a==1.1e2]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 110, @@ -3220,27 +3452,29 @@ public void Test_equals_number__decimal_fraction__exponent_116() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 110, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number, decimal fraction, positive exponent (117)" )] - public void Test_equals_number__decimal_fraction__positive_exponent_117() - { - var selector = "$[?@.a==1.1e+2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number, decimal fraction, positive exponent (117)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__decimal_fraction__positive_exponent_117( Type documentType ) + { + const string selector = "$[?@.a==1.1e+2]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 110, @@ -3256,27 +3490,29 @@ public void Test_equals_number__decimal_fraction__positive_exponent_117() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 110, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals number, decimal fraction, negative exponent (118)" )] - public void Test_equals_number__decimal_fraction__negative_exponent_118() - { - var selector = "$[?@.a==1.1e-2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals number, decimal fraction, negative exponent (118)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals_number__decimal_fraction__negative_exponent_118( Type documentType ) + { + const string selector = "$[?@.a==1.1e-2]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 0.011, @@ -3292,27 +3528,29 @@ public void Test_equals_number__decimal_fraction__negative_exponent_118() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 0.011, "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals, special nothing (119)" )] - public void Test_equals__special_nothing_119() - { - var selector = "$.values[?length(@.a) == value($..c)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals, special nothing (119)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals__special_nothing_119( Type documentType ) + { + const string selector = "$.values[?length(@.a) == value($..c)]"; + var document = TestHelper.Parse( documentType, + """ { "c": "cd", "values": [ @@ -3328,9 +3566,9 @@ public void Test_equals__special_nothing_119() ] } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "c": "d" @@ -3339,18 +3577,20 @@ public void Test_equals__special_nothing_119() "a": null } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals, empty node list and empty node list (120)" )] - public void Test_equals__empty_node_list_and_empty_node_list_120() - { - var selector = "$[?@.a == @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals, empty node list and empty node list (120)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals__empty_node_list_and_empty_node_list_120( Type documentType ) + { + const string selector = "$[?@.a == @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -3363,26 +3603,28 @@ public void Test_equals__empty_node_list_and_empty_node_list_120() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "c": 3 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"equals, empty node list and special nothing (121)" )] - public void Test_equals__empty_node_list_and_special_nothing_121() - { - var selector = "$[?@.a == length(@.b)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"equals, empty node list and special nothing (121)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_equals__empty_node_list_and_special_nothing_121( Type documentType ) + { + const string selector = "$[?@.a == length(@.b)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -3395,9 +3637,9 @@ public void Test_equals__empty_node_list_and_special_nothing_121() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "b": 2 @@ -3406,27 +3648,29 @@ public void Test_equals__empty_node_list_and_special_nothing_121() "c": 3 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"object data (122)" )] - public void Test_object_data_122() - { - var selector = "$[?@<3]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"object data (122)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_object_data_122( Type documentType ) + { + const string selector = "$[?@<3]"; + var document = TestHelper.Parse( documentType, + """ { "a": 1, "b": 2, "c": 3 } """ ); - var results = document.Select( selector ); - var expectOneOf = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expectOneOf = TestHelper.Parse( documentType, + """ [ [ 1, @@ -3437,18 +3681,20 @@ public void Test_object_data_122() 1 ] ] - """ ); + """ ).Root; - var match = TestHelper.MatchAny( results, expectOneOf! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchAny( documentType, results, expectOneOf ); + Assert.IsTrue( match ); + } - [TestMethod( @"and binds more tightly than or (123)" )] - public void Test_and_binds_more_tightly_than_or_123() - { - var selector = "$[?@.a || @.b && @.c]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"and binds more tightly than or (123)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_and_binds_more_tightly_than_or_123( Type documentType ) + { + const string selector = "$[?@.a || @.b && @.c]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -3470,9 +3716,9 @@ public void Test_and_binds_more_tightly_than_or_123() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -3487,18 +3733,20 @@ public void Test_and_binds_more_tightly_than_or_123() "c": 3 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"left to right evaluation (124)" )] - public void Test_left_to_right_evaluation_124() - { - var selector = "$[?@.a && @.b || @.c]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"left to right evaluation (124)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_left_to_right_evaluation_124( Type documentType ) + { + const string selector = "$[?@.a && @.b || @.c]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -3528,9 +3776,9 @@ public void Test_left_to_right_evaluation_124() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3553,18 +3801,20 @@ public void Test_left_to_right_evaluation_124() "c": 3 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"group terms, left (125)" )] - public void Test_group_terms__left_125() - { - var selector = "$[?(@.a || @.b) && @.c]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"group terms, left (125)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_group_terms__left_125( Type documentType ) + { + const string selector = "$[?(@.a || @.b) && @.c]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3594,9 +3844,9 @@ public void Test_group_terms__left_125() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3612,18 +3862,20 @@ public void Test_group_terms__left_125() "c": 3 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"group terms, right (126)" )] - public void Test_group_terms__right_126() - { - var selector = "$[?@.a && (@.b || @.c)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"group terms, right (126)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_group_terms__right_126( Type documentType ) + { + const string selector = "$[?@.a && (@.b || @.c)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -3649,9 +3901,9 @@ public void Test_group_terms__right_126() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3667,42 +3919,46 @@ public void Test_group_terms__right_126() "c": 3 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"string literal, single quote in double quotes (127)" )] - public void Test_string_literal__single_quote_in_double_quotes_127() - { - var selector = "$[?@ == \"quoted' literal\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"string literal, single quote in double quotes (127)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_string_literal__single_quote_in_double_quotes_127( Type documentType ) + { + const string selector = "$[?@ == \"quoted' literal\"]"; + var document = TestHelper.Parse( documentType, + """ [ "quoted' literal", "a", "quoted\\' literal" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "quoted' literal" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"string literal, double quote in single quotes (128)" )] - public void Test_string_literal__double_quote_in_single_quotes_128() - { - var selector = "$[?@ == 'quoted\" literal']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"string literal, double quote in single quotes (128)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_string_literal__double_quote_in_single_quotes_128( Type documentType ) + { + const string selector = "$[?@ == 'quoted\" literal']"; + var document = TestHelper.Parse( documentType, + """ [ "quoted\" literal", "a", @@ -3710,24 +3966,26 @@ public void Test_string_literal__double_quote_in_single_quotes_128() "'quoted\" literal'" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "quoted\" literal" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"string literal, escaped single quote in single quotes (129)" )] - public void Test_string_literal__escaped_single_quote_in_single_quotes_129() - { - var selector = "$[?@ == 'quoted\\' literal']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"string literal, escaped single quote in single quotes (129)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_string_literal__escaped_single_quote_in_single_quotes_129( Type documentType ) + { + const string selector = "$[?@ == 'quoted\\' literal']"; + var document = TestHelper.Parse( documentType, + """ [ "quoted' literal", "a", @@ -3735,24 +3993,26 @@ public void Test_string_literal__escaped_single_quote_in_single_quotes_129() "'quoted\" literal'" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "quoted' literal" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"string literal, escaped double quote in double quotes (130)" )] - public void Test_string_literal__escaped_double_quote_in_double_quotes_130() - { - var selector = "$[?@ == \"quoted\\\" literal\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"string literal, escaped double quote in double quotes (130)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_string_literal__escaped_double_quote_in_double_quotes_130( Type documentType ) + { + const string selector = "$[?@ == \"quoted\\\" literal\"]"; + var document = TestHelper.Parse( documentType, + """ [ "quoted\" literal", "a", @@ -3760,125 +4020,149 @@ public void Test_string_literal__escaped_double_quote_in_double_quotes_130() "'quoted\" literal'" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "quoted\" literal" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"literal true must be compared (131)" )] - public void Test_literal_true_must_be_compared_131() - { - var selector = "$[?true]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"literal true must be compared (131)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_literal_true_must_be_compared_131( Type documentType ) + { + const string selector = "$[?true]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"literal false must be compared (132)" )] - public void Test_literal_false_must_be_compared_132() - { - var selector = "$[?false]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"literal false must be compared (132)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_literal_false_must_be_compared_132( Type documentType ) + { + const string selector = "$[?false]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"literal string must be compared (133)" )] - public void Test_literal_string_must_be_compared_133() - { - var selector = "$[?'abc']"; - var document = JsonNode.Parse( "[0]" ); // Empty node + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"literal string must be compared (133)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_literal_string_must_be_compared_133( Type documentType ) + { + const string selector = "$[?'abc']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"literal int must be compared (134)" )] - public void Test_literal_int_must_be_compared_134() - { - var selector = "$[?2]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"literal int must be compared (134)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_literal_int_must_be_compared_134( Type documentType ) + { + const string selector = "$[?2]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"literal float must be compared (135)" )] - public void Test_literal_float_must_be_compared_135() - { - var selector = "$[?2.2]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"literal float must be compared (135)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_literal_float_must_be_compared_135( Type documentType ) + { + const string selector = "$[?2.2]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"literal null must be compared (136)" )] - public void Test_literal_null_must_be_compared_136() - { - var selector = "$[?null]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"literal null must be compared (136)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_literal_null_must_be_compared_136( Type documentType ) + { + const string selector = "$[?null]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"and, literals must be compared (137)" )] - public void Test_and__literals_must_be_compared_137() - { - var selector = "$[?true && false]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"and, literals must be compared (137)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_and__literals_must_be_compared_137( Type documentType ) + { + const string selector = "$[?true && false]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"or, literals must be compared (138)" )] - public void Test_or__literals_must_be_compared_138() - { - var selector = "$[?true || false]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"or, literals must be compared (138)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_or__literals_must_be_compared_138( Type documentType ) + { + const string selector = "$[?true || false]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"and, right hand literal must be compared (139)" )] - public void Test_and__right_hand_literal_must_be_compared_139() - { - var selector = "$[?true == false && false]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"and, right hand literal must be compared (139)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_and__right_hand_literal_must_be_compared_139( Type documentType ) + { + const string selector = "$[?true == false && false]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"or, right hand literal must be compared (140)" )] - public void Test_or__right_hand_literal_must_be_compared_140() - { - var selector = "$[?true == false || false]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"or, right hand literal must be compared (140)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_or__right_hand_literal_must_be_compared_140( Type documentType ) + { + const string selector = "$[?true == false || false]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - [TestMethod( @"and, left hand literal must be compared (141)" )] - public void Test_and__left_hand_literal_must_be_compared_141() - { - var selector = "$[?false && true == false]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"or, left hand literal must be compared (142)" )] - public void Test_or__left_hand_literal_must_be_compared_142() - { - var selector = "$[?false || true == false]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"and, left hand literal must be compared (141)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_and__left_hand_literal_must_be_compared_141( Type documentType ) + { + const string selector = "$[?false && true == false]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"or, left hand literal must be compared (142)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_or__left_hand_literal_must_be_compared_142( Type documentType ) + { + const string selector = "$[?false || true == false]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); } } + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs index 6def80e7..117fcc60 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs @@ -1,20 +1,22 @@ // This file was auto generated. +using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; +using Hyperbee.Json.Cts.TestSupport; -namespace Hyperbee.Json.Cts.Tests +namespace Hyperbee.Json.Cts.Tests; + +[TestClass] +public class CtsFunctionsTest { - [TestClass] - public class CtsFunctionsTest + [DataTestMethod( @"count, count function (1)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__count_function_1( Type documentType ) { - - [TestMethod( @"count, count function (1)" )] - public void Test_count__count_function_1() - { - var selector = "$[?count(@..*)>2]"; - var document = JsonNode.Parse( - """ + const string selector = "$[?count(@..*)>2]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": [ @@ -35,9 +37,9 @@ public void Test_count__count_function_1() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": [ @@ -53,18 +55,20 @@ public void Test_count__count_function_1() "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"count, single-node arg (2)" )] - public void Test_count__single_node_arg_2() - { - var selector = "$[?count(@.a)>1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"count, single-node arg (2)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__single_node_arg_2( Type documentType ) + { + const string selector = "$[?count(@.a)>1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": [ @@ -85,22 +89,24 @@ public void Test_count__single_node_arg_2() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"count, multiple-selector arg (3)" )] - public void Test_count__multiple_selector_arg_3() - { - var selector = "$[?count(@['a','d'])>1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"count, multiple-selector arg (3)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__multiple_selector_arg_3( Type documentType ) + { + const string selector = "$[?count(@['a','d'])>1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": [ @@ -121,9 +127,9 @@ public void Test_count__multiple_selector_arg_3() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": [ @@ -136,90 +142,108 @@ public void Test_count__multiple_selector_arg_3() "d": "f" } ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"count, non-query arg, number (4)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__non_query_arg__number_4( Type documentType ) + { + const string selector = "$[?count(1)>2]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"count, non-query arg, string (5)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__non_query_arg__string_5( Type documentType ) + { + const string selector = "$[?count('string')>2]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"count, non-query arg, true (6)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__non_query_arg__true_6( Type documentType ) + { + const string selector = "$[?count(true)>2]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"count, non-query arg, false (7)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__non_query_arg__false_7( Type documentType ) + { + const string selector = "$[?count(false)>2]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"count, non-query arg, null (8)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__non_query_arg__null_8( Type documentType ) + { + const string selector = "$[?count(null)>2]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"count, result must be compared (9)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__result_must_be_compared_9( Type documentType ) + { + const string selector = "$[?count(@..*)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"count, no params (10)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__no_params_10( Type documentType ) + { + const string selector = "$[?count()==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"count, too many params (11)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_count__too_many_params_11( Type documentType ) + { + const string selector = "$[?count(@.a,@.b)==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"count, non-query arg, number (4)" )] - public void Test_count__non_query_arg__number_4() - { - var selector = "$[?count(1)>2]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"count, non-query arg, string (5)" )] - public void Test_count__non_query_arg__string_5() - { - var selector = "$[?count('string')>2]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"count, non-query arg, true (6)" )] - public void Test_count__non_query_arg__true_6() - { - var selector = "$[?count(true)>2]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"count, non-query arg, false (7)" )] - public void Test_count__non_query_arg__false_7() - { - var selector = "$[?count(false)>2]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"count, non-query arg, null (8)" )] - public void Test_count__non_query_arg__null_8() - { - var selector = "$[?count(null)>2]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"count, result must be compared (9)" )] - public void Test_count__result_must_be_compared_9() - { - var selector = "$[?count(@..*)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"count, no params (10)" )] - public void Test_count__no_params_10() - { - var selector = "$[?count()==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"count, too many params (11)" )] - public void Test_count__too_many_params_11() - { - var selector = "$[?count(@.a,@.b)==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"length, string data (12)" )] - public void Test_length__string_data_12() - { - var selector = "$[?length(@.a)>=2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, string data (12)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__string_data_12( Type documentType ) + { + const string selector = "$[?length(@.a)>=2]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "ab" @@ -229,26 +253,28 @@ public void Test_length__string_data_12() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "ab" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"length, string data, unicode (13)" )] - public void Test_length__string_data__unicode_13() - { - var selector = "$[?length(@)==2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, string data, unicode (13)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__string_data__unicode_13( Type documentType ) + { + const string selector = "$[?length(@)==2]"; + var document = TestHelper.Parse( documentType, + """ [ "☺", "☺☺", @@ -261,26 +287,28 @@ public void Test_length__string_data__unicode_13() "形声字" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "☺☺", "жж", "阿美" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"length, array data (14)" )] - public void Test_length__array_data_14() - { - var selector = "$[?length(@.a)>=2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, array data (14)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__array_data_14( Type documentType ) + { + const string selector = "$[?length(@.a)>=2]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": [ @@ -296,9 +324,9 @@ public void Test_length__array_data_14() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": [ @@ -308,164 +336,184 @@ public void Test_length__array_data_14() ] } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"length, missing data (15)" )] - public void Test_length__missing_data_15() - { - var selector = "$[?length(@.a)>=2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, missing data (15)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__missing_data_15( Type documentType ) + { + const string selector = "$[?length(@.a)>=2]"; + var document = TestHelper.Parse( documentType, + """ [ { "d": "f" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"length, number arg (16)" )] - public void Test_length__number_arg_16() - { - var selector = "$[?length(1)>=2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, number arg (16)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__number_arg_16( Type documentType ) + { + const string selector = "$[?length(1)>=2]"; + var document = TestHelper.Parse( documentType, + """ [ { "d": "f" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"length, true arg (17)" )] - public void Test_length__true_arg_17() - { - var selector = "$[?length(true)>=2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, true arg (17)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__true_arg_17( Type documentType ) + { + const string selector = "$[?length(true)>=2]"; + var document = TestHelper.Parse( documentType, + """ [ { "d": "f" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"length, false arg (18)" )] - public void Test_length__false_arg_18() - { - var selector = "$[?length(false)>=2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, false arg (18)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__false_arg_18( Type documentType ) + { + const string selector = "$[?length(false)>=2]"; + var document = TestHelper.Parse( documentType, + """ [ { "d": "f" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"length, null arg (19)" )] - public void Test_length__null_arg_19() - { - var selector = "$[?length(null)>=2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, null arg (19)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__null_arg_19( Type documentType ) + { + const string selector = "$[?length(null)>=2]"; + var document = TestHelper.Parse( documentType, + """ [ { "d": "f" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"length, result must be compared (20)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__result_must_be_compared_20( Type documentType ) + { + const string selector = "$[?length(@.a)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"length, no params (21)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__no_params_21( Type documentType ) + { + const string selector = "$[?length()==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"length, too many params (22)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__too_many_params_22( Type documentType ) + { + const string selector = "$[?length(@.a,@.b)==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"length, result must be compared (20)" )] - public void Test_length__result_must_be_compared_20() - { - var selector = "$[?length(@.a)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"length, no params (21)" )] - public void Test_length__no_params_21() - { - var selector = "$[?length()==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"length, too many params (22)" )] - public void Test_length__too_many_params_22() - { - var selector = "$[?length(@.a,@.b)==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"length, non-singular query arg (23)" )] - public void Test_length__non_singular_query_arg_23() - { - var selector = "$[?length(@.*)<3]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"length, arg is a function expression (24)" )] - public void Test_length__arg_is_a_function_expression_24() - { - var selector = "$.values[?length(@.a)==length(value($..c))]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, non-singular query arg (23)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__non_singular_query_arg_23( Type documentType ) + { + const string selector = "$[?length(@.*)<3]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"length, arg is a function expression (24)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__arg_is_a_function_expression_24( Type documentType ) + { + const string selector = "$.values[?length(@.a)==length(value($..c))]"; + var document = TestHelper.Parse( documentType, + """ { "c": "cd", "values": [ @@ -478,26 +526,28 @@ public void Test_length__arg_is_a_function_expression_24() ] } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "ab" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"length, arg is special nothing (25)" )] - public void Test_length__arg_is_special_nothing_25() - { - var selector = "$[?length(value(@.a))>0]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"length, arg is special nothing (25)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_length__arg_is_special_nothing_25( Type documentType ) + { + const string selector = "$[?length(value(@.a))>0]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "ab" @@ -510,78 +560,84 @@ public void Test_length__arg_is_special_nothing_25() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "ab" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, found match (26)" )] - public void Test_match__found_match_26() - { - var selector = "$[?match(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, found match (26)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__found_match_26( Type documentType ) + { + const string selector = "$[?match(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "ab" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "ab" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, double quotes (27)" )] - public void Test_match__double_quotes_27() - { - var selector = "$[?match(@.a, \"a.*\")]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, double quotes (27)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__double_quotes_27( Type documentType ) + { + const string selector = "$[?match(@.a, \"a.*\")]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "ab" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "ab" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, regex from the document (28)" )] - public void Test_match__regex_from_the_document_28() - { - var selector = "$.values[?match(@, $.regex)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, regex from the document (28)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__regex_from_the_document_28( Type documentType ) + { + const string selector = "$.values[?match(@, $.regex)]"; + var document = TestHelper.Parse( documentType, + """ { "regex": "b.?b", "values": [ @@ -597,138 +653,150 @@ public void Test_match__regex_from_the_document_28() ] } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "bab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, don't select match (29)" )] - public void Test_match__don_t_select_match_29() - { - var selector = "$[?!match(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, don't select match (29)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__don_t_select_match_29( Type documentType ) + { + const string selector = "$[?!match(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "ab" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, not a match (30)" )] - public void Test_match__not_a_match_30() - { - var selector = "$[?match(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, not a match (30)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__not_a_match_30( Type documentType ) + { + const string selector = "$[?match(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, select non-match (31)" )] - public void Test_match__select_non_match_31() - { - var selector = "$[?!match(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, select non-match (31)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__select_non_match_31( Type documentType ) + { + const string selector = "$[?!match(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, non-string first arg (32)" )] - public void Test_match__non_string_first_arg_32() - { - var selector = "$[?match(1, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, non-string first arg (32)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__non_string_first_arg_32( Type documentType ) + { + const string selector = "$[?match(1, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, non-string second arg (33)" )] - public void Test_match__non_string_second_arg_33() - { - var selector = "$[?match(@.a, 1)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, non-string second arg (33)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__non_string_second_arg_33( Type documentType ) + { + const string selector = "$[?match(@.a, 1)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, filter, match function, unicode char class, uppercase (34)" )] - public void Test_match__filter__match_function__unicode_char_class__uppercase_34() - { - var selector = "$[?match(@, '\\\\p{Lu}')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, filter, match function, unicode char class, uppercase (34)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__filter__match_function__unicode_char_class__uppercase_34( Type documentType ) + { + const string selector = "$[?match(@, '\\\\p{Lu}')]"; + var document = TestHelper.Parse( documentType, + """ [ "ж", "Ж", @@ -739,24 +807,26 @@ public void Test_match__filter__match_function__unicode_char_class__uppercase_34 {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "Ж" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, filter, match function, unicode char class negated, uppercase (35)" )] - public void Test_match__filter__match_function__unicode_char_class_negated__uppercase_35() - { - var selector = "$[?match(@, '\\\\P{Lu}')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, filter, match function, unicode char class negated, uppercase (35)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__filter__match_function__unicode_char_class_negated__uppercase_35( Type documentType ) + { + const string selector = "$[?match(@, '\\\\P{Lu}')]"; + var document = TestHelper.Parse( documentType, + """ [ "ж", "Ж", @@ -766,25 +836,27 @@ public void Test_match__filter__match_function__unicode_char_class_negated__uppe {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ж", "1" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, filter, match function, unicode, surrogate pair (36)" )] - public void Test_match__filter__match_function__unicode__surrogate_pair_36() - { - var selector = "$[?match(@, 'a.b')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, filter, match function, unicode, surrogate pair (36)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__filter__match_function__unicode__surrogate_pair_36( Type documentType ) + { + const string selector = "$[?match(@, 'a.b')]"; + var document = TestHelper.Parse( documentType, + """ [ "a𐄁b", "ab", @@ -794,24 +866,26 @@ public void Test_match__filter__match_function__unicode__surrogate_pair_36() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "a𐄁b" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, dot matcher on \u2028 (37)" )] - public void Test_match__dot_matcher_on__u2028_37() - { - var selector = "$[?match(@, '.')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, dot matcher on \u2028 (37)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__dot_matcher_on__u2028_37( Type documentType ) + { + const string selector = "$[?match(@, '.')]"; + var document = TestHelper.Parse( documentType, + """ [ "\u2028", "\r", @@ -821,24 +895,26 @@ public void Test_match__dot_matcher_on__u2028_37() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "\u2028" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, dot matcher on \u2029 (38)" )] - public void Test_match__dot_matcher_on__u2029_38() - { - var selector = "$[?match(@, '.')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, dot matcher on \u2029 (38)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__dot_matcher_on__u2029_38( Type documentType ) + { + const string selector = "$[?match(@, '.')]"; + var document = TestHelper.Parse( documentType, + """ [ "\u2029", "\r", @@ -848,51 +924,59 @@ public void Test_match__dot_matcher_on__u2029_38() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "\u2029" ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"match, result cannot be compared (39)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__result_cannot_be_compared_39( Type documentType ) + { + const string selector = "$[?match(@.a, 'a.*')==true]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"match, too few params (40)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__too_few_params_40( Type documentType ) + { + const string selector = "$[?match(@.a)==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"match, too many params (41)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__too_many_params_41( Type documentType ) + { + const string selector = "$[?match(@.a,@.b,@.c)==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"match, result cannot be compared (39)" )] - public void Test_match__result_cannot_be_compared_39() - { - var selector = "$[?match(@.a, 'a.*')==true]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"match, too few params (40)" )] - public void Test_match__too_few_params_40() - { - var selector = "$[?match(@.a)==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"match, too many params (41)" )] - public void Test_match__too_many_params_41() - { - var selector = "$[?match(@.a,@.b,@.c)==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"match, arg is a function expression (42)" )] - public void Test_match__arg_is_a_function_expression_42() - { - var selector = "$.values[?match(@.a, value($..['regex']))]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, arg is a function expression (42)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__arg_is_a_function_expression_42( Type documentType ) + { + const string selector = "$.values[?match(@.a, value($..['regex']))]"; + var document = TestHelper.Parse( documentType, + """ { "regex": "a.*", "values": [ @@ -905,75 +989,81 @@ public void Test_match__arg_is_a_function_expression_42() ] } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "ab" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, dot in character class (43)" )] - public void Test_match__dot_in_character_class_43() - { - var selector = "$[?match(@, 'a[.b]c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, dot in character class (43)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__dot_in_character_class_43( Type documentType ) + { + const string selector = "$[?match(@, 'a[.b]c')]"; + var document = TestHelper.Parse( documentType, + """ [ "abc", "a.c", "axc" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "abc", "a.c" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, escaped dot (44)" )] - public void Test_match__escaped_dot_44() - { - var selector = "$[?match(@, 'a\\\\.c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, escaped dot (44)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__escaped_dot_44( Type documentType ) + { + const string selector = "$[?match(@, 'a\\\\.c')]"; + var document = TestHelper.Parse( documentType, + """ [ "abc", "a.c", "axc" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "a.c" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, escaped backslash before dot (45)" )] - public void Test_match__escaped_backslash_before_dot_45() - { - var selector = "$[?match(@, 'a\\\\\\\\.c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, escaped backslash before dot (45)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__escaped_backslash_before_dot_45( Type documentType ) + { + const string selector = "$[?match(@, 'a\\\\\\\\.c')]"; + var document = TestHelper.Parse( documentType, + """ [ "abc", "a.c", @@ -981,48 +1071,52 @@ public void Test_match__escaped_backslash_before_dot_45() "a\\\u2028c" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "a\\\u2028c" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, escaped left square bracket (46)" )] - public void Test_match__escaped_left_square_bracket_46() - { - var selector = "$[?match(@, 'a\\\\[.c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, escaped left square bracket (46)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__escaped_left_square_bracket_46( Type documentType ) + { + const string selector = "$[?match(@, 'a\\\\[.c')]"; + var document = TestHelper.Parse( documentType, + """ [ "abc", "a.c", "a[\u2028c" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "a[\u2028c" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, escaped right square bracket (47)" )] - public void Test_match__escaped_right_square_bracket_47() - { - var selector = "$[?match(@, 'a[\\\\].]c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, escaped right square bracket (47)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__escaped_right_square_bracket_47( Type documentType ) + { + const string selector = "$[?match(@, 'a[\\\\].]c')]"; + var document = TestHelper.Parse( documentType, + """ [ "abc", "a.c", @@ -1030,25 +1124,27 @@ public void Test_match__escaped_right_square_bracket_47() "a]c" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "a.c", "a]c" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, explicit caret (48)" )] - public void Test_match__explicit_caret_48() - { - var selector = "$[?match(@, '^ab.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, explicit caret (48)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__explicit_caret_48( Type documentType ) + { + const string selector = "$[?match(@, '^ab.*')]"; + var document = TestHelper.Parse( documentType, + """ [ "abc", "axc", @@ -1056,25 +1152,27 @@ public void Test_match__explicit_caret_48() "xab" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "abc", "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"match, explicit dollar (49)" )] - public void Test_match__explicit_dollar_49() - { - var selector = "$[?match(@, '.*bc$')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"match, explicit dollar (49)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_match__explicit_dollar_49( Type documentType ) + { + const string selector = "$[?match(@, '.*bc$')]"; + var document = TestHelper.Parse( documentType, + """ [ "abc", "axc", @@ -1082,128 +1180,138 @@ public void Test_match__explicit_dollar_49() "abcx" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "abc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, at the end (50)" )] - public void Test_search__at_the_end_50() - { - var selector = "$[?search(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, at the end (50)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__at_the_end_50( Type documentType ) + { + const string selector = "$[?search(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "the end is ab" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "the end is ab" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, double quotes (51)" )] - public void Test_search__double_quotes_51() - { - var selector = "$[?search(@.a, \"a.*\")]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, double quotes (51)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__double_quotes_51( Type documentType ) + { + const string selector = "$[?search(@.a, \"a.*\")]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "the end is ab" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "the end is ab" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, at the start (52)" )] - public void Test_search__at_the_start_52() - { - var selector = "$[?search(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, at the start (52)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__at_the_start_52( Type documentType ) + { + const string selector = "$[?search(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "ab is at the start" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "ab is at the start" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, in the middle (53)" )] - public void Test_search__in_the_middle_53() - { - var selector = "$[?search(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, in the middle (53)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__in_the_middle_53( Type documentType ) + { + const string selector = "$[?search(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "contains two matches" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "contains two matches" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, regex from the document (54)" )] - public void Test_search__regex_from_the_document_54() - { - var selector = "$.values[?search(@, $.regex)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, regex from the document (54)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__regex_from_the_document_54( Type documentType ) + { + const string selector = "$.values[?search(@, $.regex)]"; + var document = TestHelper.Parse( documentType, + """ { "regex": "b.?b", "values": [ @@ -1219,140 +1327,152 @@ public void Test_search__regex_from_the_document_54() ] } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "bab", "bba", "bbab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, don't select match (55)" )] - public void Test_search__don_t_select_match_55() - { - var selector = "$[?!search(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, don't select match (55)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__don_t_select_match_55( Type documentType ) + { + const string selector = "$[?!search(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "contains two matches" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, not a match (56)" )] - public void Test_search__not_a_match_56() - { - var selector = "$[?search(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, not a match (56)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__not_a_match_56( Type documentType ) + { + const string selector = "$[?search(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, select non-match (57)" )] - public void Test_search__select_non_match_57() - { - var selector = "$[?!search(@.a, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, select non-match (57)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__select_non_match_57( Type documentType ) + { + const string selector = "$[?!search(@.a, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, non-string first arg (58)" )] - public void Test_search__non_string_first_arg_58() - { - var selector = "$[?search(1, 'a.*')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, non-string first arg (58)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__non_string_first_arg_58( Type documentType ) + { + const string selector = "$[?search(1, 'a.*')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, non-string second arg (59)" )] - public void Test_search__non_string_second_arg_59() - { - var selector = "$[?search(@.a, 1)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, non-string second arg (59)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__non_string_second_arg_59( Type documentType ) + { + const string selector = "$[?search(@.a, 1)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "bc" } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, filter, search function, unicode char class, uppercase (60)" )] - public void Test_search__filter__search_function__unicode_char_class__uppercase_60() - { - var selector = "$[?search(@, '\\\\p{Lu}')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, filter, search function, unicode char class, uppercase (60)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__filter__search_function__unicode_char_class__uppercase_60( Type documentType ) + { + const string selector = "$[?search(@, '\\\\p{Lu}')]"; + var document = TestHelper.Parse( documentType, + """ [ "ж", "Ж", @@ -1363,25 +1483,27 @@ public void Test_search__filter__search_function__unicode_char_class__uppercase_ {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "Ж", "жЖ" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, filter, search function, unicode char class negated, uppercase (61)" )] - public void Test_search__filter__search_function__unicode_char_class_negated__uppercase_61() - { - var selector = "$[?search(@, '\\\\P{Lu}')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, filter, search function, unicode char class negated, uppercase (61)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__filter__search_function__unicode_char_class_negated__uppercase_61( Type documentType ) + { + const string selector = "$[?search(@, '\\\\P{Lu}')]"; + var document = TestHelper.Parse( documentType, + """ [ "ж", "Ж", @@ -1391,25 +1513,27 @@ public void Test_search__filter__search_function__unicode_char_class_negated__up {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ж", "1" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, filter, search function, unicode, surrogate pair (62)" )] - public void Test_search__filter__search_function__unicode__surrogate_pair_62() - { - var selector = "$[?search(@, 'a.b')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, filter, search function, unicode, surrogate pair (62)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__filter__search_function__unicode__surrogate_pair_62( Type documentType ) + { + const string selector = "$[?search(@, 'a.b')]"; + var document = TestHelper.Parse( documentType, + """ [ "a𐄁bc", "abc", @@ -1419,24 +1543,26 @@ public void Test_search__filter__search_function__unicode__surrogate_pair_62() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "a𐄁bc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, dot matcher on \u2028 (63)" )] - public void Test_search__dot_matcher_on__u2028_63() - { - var selector = "$[?search(@, '.')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, dot matcher on \u2028 (63)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__dot_matcher_on__u2028_63( Type documentType ) + { + const string selector = "$[?search(@, '.')]"; + var document = TestHelper.Parse( documentType, + """ [ "\u2028", "\r\u2028\n", @@ -1447,25 +1573,27 @@ public void Test_search__dot_matcher_on__u2028_63() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "\u2028", "\r\u2028\n" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, dot matcher on \u2029 (64)" )] - public void Test_search__dot_matcher_on__u2029_64() - { - var selector = "$[?search(@, '.')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, dot matcher on \u2029 (64)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__dot_matcher_on__u2029_64( Type documentType ) + { + const string selector = "$[?search(@, '.')]"; + var document = TestHelper.Parse( documentType, + """ [ "\u2029", "\r\u2029\n", @@ -1476,52 +1604,60 @@ public void Test_search__dot_matcher_on__u2029_64() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "\u2029", "\r\u2029\n" ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"search, result cannot be compared (65)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__result_cannot_be_compared_65( Type documentType ) + { + const string selector = "$[?search(@.a, 'a.*')==true]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"search, too few params (66)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__too_few_params_66( Type documentType ) + { + const string selector = "$[?search(@.a)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"search, too many params (67)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__too_many_params_67( Type documentType ) + { + const string selector = "$[?search(@.a,@.b,@.c)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"search, result cannot be compared (65)" )] - public void Test_search__result_cannot_be_compared_65() - { - var selector = "$[?search(@.a, 'a.*')==true]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"search, too few params (66)" )] - public void Test_search__too_few_params_66() - { - var selector = "$[?search(@.a)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"search, too many params (67)" )] - public void Test_search__too_many_params_67() - { - var selector = "$[?search(@.a,@.b,@.c)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"search, arg is a function expression (68)" )] - public void Test_search__arg_is_a_function_expression_68() - { - var selector = "$.values[?search(@, value($..['regex']))]"; - var document = JsonNode.Parse( - """ + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"search, arg is a function expression (68)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__arg_is_a_function_expression_68( Type documentType ) + { + const string selector = "$.values[?search(@, value($..['regex']))]"; + var document = TestHelper.Parse( documentType, + """ { "regex": "b.?b", "values": [ @@ -1537,75 +1673,81 @@ public void Test_search__arg_is_a_function_expression_68() ] } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "bab", "bba", "bbab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, dot in character class (69)" )] - public void Test_search__dot_in_character_class_69() - { - var selector = "$[?search(@, 'a[.b]c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, dot in character class (69)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__dot_in_character_class_69( Type documentType ) + { + const string selector = "$[?search(@, 'a[.b]c')]"; + var document = TestHelper.Parse( documentType, + """ [ "x abc y", "x a.c y", "x axc y" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "x abc y", "x a.c y" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, escaped dot (70)" )] - public void Test_search__escaped_dot_70() - { - var selector = "$[?search(@, 'a\\\\.c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, escaped dot (70)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__escaped_dot_70( Type documentType ) + { + const string selector = "$[?search(@, 'a\\\\.c')]"; + var document = TestHelper.Parse( documentType, + """ [ "x abc y", "x a.c y", "x axc y" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "x a.c y" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, escaped backslash before dot (71)" )] - public void Test_search__escaped_backslash_before_dot_71() - { - var selector = "$[?search(@, 'a\\\\\\\\.c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, escaped backslash before dot (71)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__escaped_backslash_before_dot_71( Type documentType ) + { + const string selector = "$[?search(@, 'a\\\\\\\\.c')]"; + var document = TestHelper.Parse( documentType, + """ [ "x abc y", "x a.c y", @@ -1613,48 +1755,52 @@ public void Test_search__escaped_backslash_before_dot_71() "x a\\\u2028c y" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "x a\\\u2028c y" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, escaped left square bracket (72)" )] - public void Test_search__escaped_left_square_bracket_72() - { - var selector = "$[?search(@, 'a\\\\[.c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, escaped left square bracket (72)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__escaped_left_square_bracket_72( Type documentType ) + { + const string selector = "$[?search(@, 'a\\\\[.c')]"; + var document = TestHelper.Parse( documentType, + """ [ "x abc y", "x a.c y", "x a[\u2028c y" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "x a[\u2028c y" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"search, escaped right square bracket (73)" )] - public void Test_search__escaped_right_square_bracket_73() - { - var selector = "$[?search(@, 'a[\\\\].]c')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"search, escaped right square bracket (73)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_search__escaped_right_square_bracket_73( Type documentType ) + { + const string selector = "$[?search(@, 'a[\\\\].]c')]"; + var document = TestHelper.Parse( documentType, + """ [ "x abc y", "x a.c y", @@ -1662,25 +1808,27 @@ public void Test_search__escaped_right_square_bracket_73() "x a]c y" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "x a.c y", "x a]c y" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"value, single-value nodelist (74)" )] - public void Test_value__single_value_nodelist_74() - { - var selector = "$[?value(@.*)==4]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"value, single-value nodelist (74)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_value__single_value_nodelist_74( Type documentType ) + { + const string selector = "$[?value(@.*)==4]"; + var document = TestHelper.Parse( documentType, + """ [ [ 4 @@ -1697,9 +1845,9 @@ public void Test_value__single_value_nodelist_74() 4 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ [ 4 @@ -1708,18 +1856,20 @@ public void Test_value__single_value_nodelist_74() "foo": 4 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"value, multi-value nodelist (75)" )] - public void Test_value__multi_value_nodelist_75() - { - var selector = "$[?value(@.*)==4]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"value, multi-value nodelist (75)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_value__multi_value_nodelist_75( Type documentType ) + { + const string selector = "$[?value(@.*)==4]"; + var document = TestHelper.Parse( documentType, + """ [ [ 4, @@ -1731,42 +1881,48 @@ public void Test_value__multi_value_nodelist_75() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"value, too few params (76)" )] - public void Test_value__too_few_params_76() - { - var selector = "$[?value()==4]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"value, too few params (76)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_value__too_few_params_76( Type documentType ) + { + const string selector = "$[?value()==4]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"value, too many params (77)" )] - public void Test_value__too_many_params_77() - { - var selector = "$[?value(@.a,@.b)==4]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"value, too many params (77)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_value__too_many_params_77( Type documentType ) + { + const string selector = "$[?value(@.a,@.b)==4]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"value, result must be compared (78)" )] - public void Test_value__result_must_be_compared_78() - { - var selector = "$[?value(@.a)]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"value, result must be compared (78)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_value__result_must_be_compared_78( Type documentType ) + { + const string selector = "$[?value(@.a)]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); } } + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs index 7a63537e..231296dd 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs @@ -1,203 +1,225 @@ // This file was auto generated. +using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; +using Hyperbee.Json.Cts.TestSupport; -namespace Hyperbee.Json.Cts.Tests +namespace Hyperbee.Json.Cts.Tests; + +[TestClass] +public class CtsIndexSelectorTest { - [TestClass] - public class CtsIndexSelectorTest + [DataTestMethod( @"first element (1)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_first_element_1( Type documentType ) { - - [TestMethod( @"first element (1)" )] - public void Test_first_element_1() - { - var selector = "$[0]"; - var document = JsonNode.Parse( - """ + const string selector = "$[0]"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "first" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"second element (2)" )] - public void Test_second_element_2() - { - var selector = "$[1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"second element (2)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_second_element_2( Type documentType ) + { + const string selector = "$[1]"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "second" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"out of bound (3)" )] - public void Test_out_of_bound_3() - { - var selector = "$[2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"out of bound (3)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_out_of_bound_3( Type documentType ) + { + const string selector = "$[2]"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"overflowing index (4)" )] - public void Test_overflowing_index_4() - { - var selector = "$[231584178474632390847141970017375815706539969331281128078915168015826259279872]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"not actually an index, overflowing index leads into general text (5)" )] - public void Test_not_actually_an_index__overflowing_index_leads_into_general_text_5() - { - var selector = "$[231584178474632390847141970017375815706539969331281128078915168SomeRandomText]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"negative (6)" )] - public void Test_negative_6() - { - var selector = "$[-1]"; - var document = JsonNode.Parse( - """ + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"overflowing index (4)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_overflowing_index_4( Type documentType ) + { + const string selector = "$[231584178474632390847141970017375815706539969331281128078915168015826259279872]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"not actually an index, overflowing index leads into general text (5)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_not_actually_an_index__overflowing_index_leads_into_general_text_5( Type documentType ) + { + const string selector = "$[231584178474632390847141970017375815706539969331281128078915168SomeRandomText]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"negative (6)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_6( Type documentType ) + { + const string selector = "$[-1]"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "second" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"more negative (7)" )] - public void Test_more_negative_7() - { - var selector = "$[-2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"more negative (7)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_more_negative_7( Type documentType ) + { + const string selector = "$[-2]"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "first" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative out of bound (8)" )] - public void Test_negative_out_of_bound_8() - { - var selector = "$[-3]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative out of bound (8)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_out_of_bound_8( Type documentType ) + { + const string selector = "$[-3]"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"on object (9)" )] - public void Test_on_object_9() - { - var selector = "$[0]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"on object (9)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_on_object_9( Type documentType ) + { + const string selector = "$[0]"; + var document = TestHelper.Parse( documentType, + """ { "foo": 1 } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"leading 0 (10)" )] - public void Test_leading_0_10() - { - var selector = "$[01]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"leading 0 (10)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_leading_0_10( Type documentType ) + { + const string selector = "$[01]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"leading -0 (11)" )] - public void Test_leading__0_11() - { - var selector = "$[-01]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"leading -0 (11)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_leading__0_11( Type documentType ) + { + const string selector = "$[-01]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); } } + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs index efca01e3..b56cca90 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs @@ -1,1393 +1,1601 @@ // This file was auto generated. +using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; +using Hyperbee.Json.Cts.TestSupport; -namespace Hyperbee.Json.Cts.Tests +namespace Hyperbee.Json.Cts.Tests; + +[TestClass] +public class CtsNameSelectorTest { - [TestClass] - public class CtsNameSelectorTest + [DataTestMethod( @"double quotes (1)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes_1( Type documentType ) { - - [TestMethod( @"double quotes (1)" )] - public void Test_double_quotes_1() - { - var selector = "$[\"a\"]"; - var document = JsonNode.Parse( - """ + const string selector = "$[\"a\"]"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, absent data (2)" )] - public void Test_double_quotes__absent_data_2() - { - var selector = "$[\"c\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, absent data (2)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__absent_data_2( Type documentType ) + { + const string selector = "$[\"c\"]"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, array data (3)" )] - public void Test_double_quotes__array_data_3() - { - var selector = "$[\"a\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, array data (3)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__array_data_3( Type documentType ) + { + const string selector = "$[\"a\"]"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"double quotes, embedded U+0000 (4)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0000_4( Type documentType ) + { + const string selector = "$[\"\u0000\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0001 (5)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0001_5( Type documentType ) + { + const string selector = "$[\"\u0001\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0002 (6)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0002_6( Type documentType ) + { + const string selector = "$[\"\u0002\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0003 (7)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0003_7( Type documentType ) + { + const string selector = "$[\"\u0003\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0004 (8)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0004_8( Type documentType ) + { + const string selector = "$[\"\u0004\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0005 (9)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0005_9( Type documentType ) + { + const string selector = "$[\"\u0005\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0006 (10)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0006_10( Type documentType ) + { + const string selector = "$[\"\u0006\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0007 (11)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0007_11( Type documentType ) + { + const string selector = "$[\"\u0007\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0008 (12)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0008_12( Type documentType ) + { + const string selector = "$[\"\b\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0009 (13)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0009_13( Type documentType ) + { + const string selector = "$[\"\t\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+000A (14)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_000A_14( Type documentType ) + { + const string selector = "$[\"\n\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+000B (15)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_000B_15( Type documentType ) + { + const string selector = "$[\"\u000b\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+000C (16)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_000C_16( Type documentType ) + { + const string selector = "$[\"\f\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+000D (17)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_000D_17( Type documentType ) + { + const string selector = "$[\"\r\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+000E (18)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_000E_18( Type documentType ) + { + const string selector = "$[\"\u000e\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+000F (19)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_000F_19( Type documentType ) + { + const string selector = "$[\"\u000f\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0010 (20)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0010_20( Type documentType ) + { + const string selector = "$[\"\u0010\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0011 (21)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0011_21( Type documentType ) + { + const string selector = "$[\"\u0011\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0012 (22)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0012_22( Type documentType ) + { + const string selector = "$[\"\u0012\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0013 (23)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0013_23( Type documentType ) + { + const string selector = "$[\"\u0013\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0014 (24)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0014_24( Type documentType ) + { + const string selector = "$[\"\u0014\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0015 (25)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0015_25( Type documentType ) + { + const string selector = "$[\"\u0015\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"double quotes, embedded U+0000 (4)" )] - public void Test_double_quotes__embedded_U_0000_4() - { - var selector = "$[\"\u0000\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0001 (5)" )] - public void Test_double_quotes__embedded_U_0001_5() - { - var selector = "$[\"\u0001\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0002 (6)" )] - public void Test_double_quotes__embedded_U_0002_6() - { - var selector = "$[\"\u0002\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0003 (7)" )] - public void Test_double_quotes__embedded_U_0003_7() - { - var selector = "$[\"\u0003\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0004 (8)" )] - public void Test_double_quotes__embedded_U_0004_8() - { - var selector = "$[\"\u0004\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0005 (9)" )] - public void Test_double_quotes__embedded_U_0005_9() - { - var selector = "$[\"\u0005\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0006 (10)" )] - public void Test_double_quotes__embedded_U_0006_10() - { - var selector = "$[\"\u0006\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0007 (11)" )] - public void Test_double_quotes__embedded_U_0007_11() - { - var selector = "$[\"\u0007\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0008 (12)" )] - public void Test_double_quotes__embedded_U_0008_12() - { - var selector = "$[\"\b\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0009 (13)" )] - public void Test_double_quotes__embedded_U_0009_13() - { - var selector = "$[\"\t\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+000A (14)" )] - public void Test_double_quotes__embedded_U_000A_14() - { - var selector = "$[\"\n\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+000B (15)" )] - public void Test_double_quotes__embedded_U_000B_15() - { - var selector = "$[\"\u000b\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+000C (16)" )] - public void Test_double_quotes__embedded_U_000C_16() - { - var selector = "$[\"\f\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+000D (17)" )] - public void Test_double_quotes__embedded_U_000D_17() - { - var selector = "$[\"\r\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+000E (18)" )] - public void Test_double_quotes__embedded_U_000E_18() - { - var selector = "$[\"\u000e\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+000F (19)" )] - public void Test_double_quotes__embedded_U_000F_19() - { - var selector = "$[\"\u000f\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0010 (20)" )] - public void Test_double_quotes__embedded_U_0010_20() - { - var selector = "$[\"\u0010\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0011 (21)" )] - public void Test_double_quotes__embedded_U_0011_21() - { - var selector = "$[\"\u0011\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0012 (22)" )] - public void Test_double_quotes__embedded_U_0012_22() - { - var selector = "$[\"\u0012\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0013 (23)" )] - public void Test_double_quotes__embedded_U_0013_23() - { - var selector = "$[\"\u0013\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0014 (24)" )] - public void Test_double_quotes__embedded_U_0014_24() - { - var selector = "$[\"\u0014\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0015 (25)" )] - public void Test_double_quotes__embedded_U_0015_25() - { - var selector = "$[\"\u0015\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0016 (26)" )] - public void Test_double_quotes__embedded_U_0016_26() - { - var selector = "$[\"\u0016\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0017 (27)" )] - public void Test_double_quotes__embedded_U_0017_27() - { - var selector = "$[\"\u0017\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0018 (28)" )] - public void Test_double_quotes__embedded_U_0018_28() - { - var selector = "$[\"\u0018\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0019 (29)" )] - public void Test_double_quotes__embedded_U_0019_29() - { - var selector = "$[\"\u0019\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+001A (30)" )] - public void Test_double_quotes__embedded_U_001A_30() - { - var selector = "$[\"\u001a\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+001B (31)" )] - public void Test_double_quotes__embedded_U_001B_31() - { - var selector = "$[\"\u001b\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+001C (32)" )] - public void Test_double_quotes__embedded_U_001C_32() - { - var selector = "$[\"\u001c\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+001D (33)" )] - public void Test_double_quotes__embedded_U_001D_33() - { - var selector = "$[\"\u001d\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+001E (34)" )] - public void Test_double_quotes__embedded_U_001E_34() - { - var selector = "$[\"\u001e\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+001F (35)" )] - public void Test_double_quotes__embedded_U_001F_35() - { - var selector = "$[\"\u001f\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded U+0020 (36)" )] - public void Test_double_quotes__embedded_U_0020_36() - { - var selector = "$[\" \"]"; - var document = JsonNode.Parse( - """ + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0016 (26)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0016_26( Type documentType ) + { + const string selector = "$[\"\u0016\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0017 (27)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0017_27( Type documentType ) + { + const string selector = "$[\"\u0017\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0018 (28)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0018_28( Type documentType ) + { + const string selector = "$[\"\u0018\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0019 (29)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0019_29( Type documentType ) + { + const string selector = "$[\"\u0019\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+001A (30)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_001A_30( Type documentType ) + { + const string selector = "$[\"\u001a\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+001B (31)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_001B_31( Type documentType ) + { + const string selector = "$[\"\u001b\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+001C (32)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_001C_32( Type documentType ) + { + const string selector = "$[\"\u001c\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+001D (33)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_001D_33( Type documentType ) + { + const string selector = "$[\"\u001d\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+001E (34)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_001E_34( Type documentType ) + { + const string selector = "$[\"\u001e\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+001F (35)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_001F_35( Type documentType ) + { + const string selector = "$[\"\u001f\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded U+0020 (36)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_U_0020_36( Type documentType ) + { + const string selector = "$[\" \"]"; + var document = TestHelper.Parse( documentType, + """ { " ": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped double quote (37)" )] - public void Test_double_quotes__escaped_double_quote_37() - { - var selector = "$[\"\\\"\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped double quote (37)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped_double_quote_37( Type documentType ) + { + const string selector = "$[\"\\\"\"]"; + var document = TestHelper.Parse( documentType, + """ { "\"": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped reverse solidus (38)" )] - public void Test_double_quotes__escaped_reverse_solidus_38() - { - var selector = "$[\"\\\\\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped reverse solidus (38)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped_reverse_solidus_38( Type documentType ) + { + const string selector = "$[\"\\\\\"]"; + var document = TestHelper.Parse( documentType, + """ { "\\": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped solidus (39)" )] - public void Test_double_quotes__escaped_solidus_39() - { - var selector = "$[\"\\/\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped solidus (39)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped_solidus_39( Type documentType ) + { + const string selector = "$[\"\\/\"]"; + var document = TestHelper.Parse( documentType, + """ { "/": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped backspace (40)" )] - public void Test_double_quotes__escaped_backspace_40() - { - var selector = "$[\"\\b\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped backspace (40)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped_backspace_40( Type documentType ) + { + const string selector = "$[\"\\b\"]"; + var document = TestHelper.Parse( documentType, + """ { "\b": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped form feed (41)" )] - public void Test_double_quotes__escaped_form_feed_41() - { - var selector = "$[\"\\f\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped form feed (41)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped_form_feed_41( Type documentType ) + { + const string selector = "$[\"\\f\"]"; + var document = TestHelper.Parse( documentType, + """ { "\f": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped line feed (42)" )] - public void Test_double_quotes__escaped_line_feed_42() - { - var selector = "$[\"\\n\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped line feed (42)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped_line_feed_42( Type documentType ) + { + const string selector = "$[\"\\n\"]"; + var document = TestHelper.Parse( documentType, + """ { "\n": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped carriage return (43)" )] - public void Test_double_quotes__escaped_carriage_return_43() - { - var selector = "$[\"\\r\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped carriage return (43)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped_carriage_return_43( Type documentType ) + { + const string selector = "$[\"\\r\"]"; + var document = TestHelper.Parse( documentType, + """ { "\r": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped tab (44)" )] - public void Test_double_quotes__escaped_tab_44() - { - var selector = "$[\"\\t\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped tab (44)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped_tab_44( Type documentType ) + { + const string selector = "$[\"\\t\"]"; + var document = TestHelper.Parse( documentType, + """ { "\t": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped ☺, upper case hex (45)" )] - public void Test_double_quotes__escaped____upper_case_hex_45() - { - var selector = "$[\"\\u263A\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped ☺, upper case hex (45)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped____upper_case_hex_45( Type documentType ) + { + const string selector = "$[\"\\u263A\"]"; + var document = TestHelper.Parse( documentType, + """ { "☺": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, escaped ☺, lower case hex (46)" )] - public void Test_double_quotes__escaped____lower_case_hex_46() - { - var selector = "$[\"\\u263a\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, escaped ☺, lower case hex (46)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__escaped____lower_case_hex_46( Type documentType ) + { + const string selector = "$[\"\\u263a\"]"; + var document = TestHelper.Parse( documentType, + """ { "☺": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, surrogate pair 𝄞 (47)" )] - public void Test_double_quotes__surrogate_pair____47() - { - var selector = "$[\"\\uD834\\uDD1E\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, surrogate pair 𝄞 (47)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__surrogate_pair____47( Type documentType ) + { + const string selector = "$[\"\\uD834\\uDD1E\"]"; + var document = TestHelper.Parse( documentType, + """ { "𝄞": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"double quotes, surrogate pair 😀 (48)" )] - public void Test_double_quotes__surrogate_pair____48() - { - var selector = "$[\"\\uD83D\\uDE00\"]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"double quotes, surrogate pair 😀 (48)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__surrogate_pair____48( Type documentType ) + { + const string selector = "$[\"\\uD83D\\uDE00\"]"; + var document = TestHelper.Parse( documentType, + """ { "😀": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"double quotes, invalid escaped single quote (49)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__invalid_escaped_single_quote_49( Type documentType ) + { + const string selector = "$[\"\\'\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, embedded double quote (50)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__embedded_double_quote_50( Type documentType ) + { + const string selector = "$[\"\"\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, incomplete escape (51)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__incomplete_escape_51( Type documentType ) + { + const string selector = "$[\"\\\"]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"double quotes, invalid escaped single quote (49)" )] - public void Test_double_quotes__invalid_escaped_single_quote_49() - { - var selector = "$[\"\\'\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, embedded double quote (50)" )] - public void Test_double_quotes__embedded_double_quote_50() - { - var selector = "$[\"\"\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, incomplete escape (51)" )] - public void Test_double_quotes__incomplete_escape_51() - { - var selector = "$[\"\\\"]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes (52)" )] - public void Test_single_quotes_52() - { - var selector = "$['a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes (52)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes_52( Type documentType ) + { + const string selector = "$['a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, absent data (53)" )] - public void Test_single_quotes__absent_data_53() - { - var selector = "$['c']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, absent data (53)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__absent_data_53( Type documentType ) + { + const string selector = "$['c']"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, array data (54)" )] - public void Test_single_quotes__array_data_54() - { - var selector = "$['a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, array data (54)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__array_data_54( Type documentType ) + { + const string selector = "$['a']"; + var document = TestHelper.Parse( documentType, + """ [ "first", "second" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"single quotes, embedded U+0000 (55)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0000_55( Type documentType ) + { + const string selector = "$['\u0000']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0001 (56)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0001_56( Type documentType ) + { + const string selector = "$['\u0001']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0002 (57)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0002_57( Type documentType ) + { + const string selector = "$['\u0002']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0003 (58)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0003_58( Type documentType ) + { + const string selector = "$['\u0003']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0004 (59)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0004_59( Type documentType ) + { + const string selector = "$['\u0004']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0005 (60)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0005_60( Type documentType ) + { + const string selector = "$['\u0005']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0006 (61)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0006_61( Type documentType ) + { + const string selector = "$['\u0006']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0007 (62)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0007_62( Type documentType ) + { + const string selector = "$['\u0007']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0008 (63)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0008_63( Type documentType ) + { + const string selector = "$['\b']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0009 (64)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0009_64( Type documentType ) + { + const string selector = "$['\t']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+000A (65)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_000A_65( Type documentType ) + { + const string selector = "$['\n']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+000B (66)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_000B_66( Type documentType ) + { + const string selector = "$['\u000b']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+000C (67)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_000C_67( Type documentType ) + { + const string selector = "$['\f']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+000D (68)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_000D_68( Type documentType ) + { + const string selector = "$['\r']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+000E (69)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_000E_69( Type documentType ) + { + const string selector = "$['\u000e']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+000F (70)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_000F_70( Type documentType ) + { + const string selector = "$['\u000f']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0010 (71)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0010_71( Type documentType ) + { + const string selector = "$['\u0010']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0011 (72)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0011_72( Type documentType ) + { + const string selector = "$['\u0011']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0012 (73)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0012_73( Type documentType ) + { + const string selector = "$['\u0012']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0013 (74)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0013_74( Type documentType ) + { + const string selector = "$['\u0013']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0014 (75)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0014_75( Type documentType ) + { + const string selector = "$['\u0014']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0015 (76)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0015_76( Type documentType ) + { + const string selector = "$['\u0015']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0016 (77)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0016_77( Type documentType ) + { + const string selector = "$['\u0016']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"single quotes, embedded U+0000 (55)" )] - public void Test_single_quotes__embedded_U_0000_55() - { - var selector = "$['\u0000']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0001 (56)" )] - public void Test_single_quotes__embedded_U_0001_56() - { - var selector = "$['\u0001']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0002 (57)" )] - public void Test_single_quotes__embedded_U_0002_57() - { - var selector = "$['\u0002']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0003 (58)" )] - public void Test_single_quotes__embedded_U_0003_58() - { - var selector = "$['\u0003']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0004 (59)" )] - public void Test_single_quotes__embedded_U_0004_59() - { - var selector = "$['\u0004']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0005 (60)" )] - public void Test_single_quotes__embedded_U_0005_60() - { - var selector = "$['\u0005']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0006 (61)" )] - public void Test_single_quotes__embedded_U_0006_61() - { - var selector = "$['\u0006']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0007 (62)" )] - public void Test_single_quotes__embedded_U_0007_62() - { - var selector = "$['\u0007']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0008 (63)" )] - public void Test_single_quotes__embedded_U_0008_63() - { - var selector = "$['\b']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0009 (64)" )] - public void Test_single_quotes__embedded_U_0009_64() - { - var selector = "$['\t']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+000A (65)" )] - public void Test_single_quotes__embedded_U_000A_65() - { - var selector = "$['\n']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+000B (66)" )] - public void Test_single_quotes__embedded_U_000B_66() - { - var selector = "$['\u000b']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+000C (67)" )] - public void Test_single_quotes__embedded_U_000C_67() - { - var selector = "$['\f']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+000D (68)" )] - public void Test_single_quotes__embedded_U_000D_68() - { - var selector = "$['\r']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+000E (69)" )] - public void Test_single_quotes__embedded_U_000E_69() - { - var selector = "$['\u000e']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+000F (70)" )] - public void Test_single_quotes__embedded_U_000F_70() - { - var selector = "$['\u000f']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0010 (71)" )] - public void Test_single_quotes__embedded_U_0010_71() - { - var selector = "$['\u0010']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0011 (72)" )] - public void Test_single_quotes__embedded_U_0011_72() - { - var selector = "$['\u0011']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0012 (73)" )] - public void Test_single_quotes__embedded_U_0012_73() - { - var selector = "$['\u0012']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0013 (74)" )] - public void Test_single_quotes__embedded_U_0013_74() - { - var selector = "$['\u0013']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0014 (75)" )] - public void Test_single_quotes__embedded_U_0014_75() - { - var selector = "$['\u0014']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0015 (76)" )] - public void Test_single_quotes__embedded_U_0015_76() - { - var selector = "$['\u0015']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0016 (77)" )] - public void Test_single_quotes__embedded_U_0016_77() - { - var selector = "$['\u0016']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0017 (78)" )] - public void Test_single_quotes__embedded_U_0017_78() - { - var selector = "$['\u0017']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0018 (79)" )] - public void Test_single_quotes__embedded_U_0018_79() - { - var selector = "$['\u0018']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0019 (80)" )] - public void Test_single_quotes__embedded_U_0019_80() - { - var selector = "$['\u0019']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+001A (81)" )] - public void Test_single_quotes__embedded_U_001A_81() - { - var selector = "$['\u001a']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+001B (82)" )] - public void Test_single_quotes__embedded_U_001B_82() - { - var selector = "$['\u001b']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+001C (83)" )] - public void Test_single_quotes__embedded_U_001C_83() - { - var selector = "$['\u001c']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+001D (84)" )] - public void Test_single_quotes__embedded_U_001D_84() - { - var selector = "$['\u001d']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+001E (85)" )] - public void Test_single_quotes__embedded_U_001E_85() - { - var selector = "$['\u001e']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+001F (86)" )] - public void Test_single_quotes__embedded_U_001F_86() - { - var selector = "$['\u001f']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded U+0020 (87)" )] - public void Test_single_quotes__embedded_U_0020_87() - { - var selector = "$[' ']"; - var document = JsonNode.Parse( - """ + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0017 (78)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0017_78( Type documentType ) + { + const string selector = "$['\u0017']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0018 (79)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0018_79( Type documentType ) + { + const string selector = "$['\u0018']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0019 (80)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0019_80( Type documentType ) + { + const string selector = "$['\u0019']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+001A (81)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_001A_81( Type documentType ) + { + const string selector = "$['\u001a']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+001B (82)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_001B_82( Type documentType ) + { + const string selector = "$['\u001b']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+001C (83)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_001C_83( Type documentType ) + { + const string selector = "$['\u001c']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+001D (84)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_001D_84( Type documentType ) + { + const string selector = "$['\u001d']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+001E (85)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_001E_85( Type documentType ) + { + const string selector = "$['\u001e']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+001F (86)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_001F_86( Type documentType ) + { + const string selector = "$['\u001f']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded U+0020 (87)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_U_0020_87( Type documentType ) + { + const string selector = "$[' ']"; + var document = TestHelper.Parse( documentType, + """ { " ": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped single quote (88)" )] - public void Test_single_quotes__escaped_single_quote_88() - { - var selector = "$['\\'']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped single quote (88)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped_single_quote_88( Type documentType ) + { + const string selector = "$['\\'']"; + var document = TestHelper.Parse( documentType, + """ { "'": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped reverse solidus (89)" )] - public void Test_single_quotes__escaped_reverse_solidus_89() - { - var selector = "$['\\\\']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped reverse solidus (89)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped_reverse_solidus_89( Type documentType ) + { + const string selector = "$['\\\\']"; + var document = TestHelper.Parse( documentType, + """ { "\\": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped solidus (90)" )] - public void Test_single_quotes__escaped_solidus_90() - { - var selector = "$['\\/']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped solidus (90)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped_solidus_90( Type documentType ) + { + const string selector = "$['\\/']"; + var document = TestHelper.Parse( documentType, + """ { "/": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped backspace (91)" )] - public void Test_single_quotes__escaped_backspace_91() - { - var selector = "$['\\b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped backspace (91)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped_backspace_91( Type documentType ) + { + const string selector = "$['\\b']"; + var document = TestHelper.Parse( documentType, + """ { "\b": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped form feed (92)" )] - public void Test_single_quotes__escaped_form_feed_92() - { - var selector = "$['\\f']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped form feed (92)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped_form_feed_92( Type documentType ) + { + const string selector = "$['\\f']"; + var document = TestHelper.Parse( documentType, + """ { "\f": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped line feed (93)" )] - public void Test_single_quotes__escaped_line_feed_93() - { - var selector = "$['\\n']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped line feed (93)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped_line_feed_93( Type documentType ) + { + const string selector = "$['\\n']"; + var document = TestHelper.Parse( documentType, + """ { "\n": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped carriage return (94)" )] - public void Test_single_quotes__escaped_carriage_return_94() - { - var selector = "$['\\r']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped carriage return (94)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped_carriage_return_94( Type documentType ) + { + const string selector = "$['\\r']"; + var document = TestHelper.Parse( documentType, + """ { "\r": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped tab (95)" )] - public void Test_single_quotes__escaped_tab_95() - { - var selector = "$['\\t']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped tab (95)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped_tab_95( Type documentType ) + { + const string selector = "$['\\t']"; + var document = TestHelper.Parse( documentType, + """ { "\t": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped ☺, upper case hex (96)" )] - public void Test_single_quotes__escaped____upper_case_hex_96() - { - var selector = "$['\\u263A']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped ☺, upper case hex (96)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped____upper_case_hex_96( Type documentType ) + { + const string selector = "$['\\u263A']"; + var document = TestHelper.Parse( documentType, + """ { "☺": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, escaped ☺, lower case hex (97)" )] - public void Test_single_quotes__escaped____lower_case_hex_97() - { - var selector = "$['\\u263a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, escaped ☺, lower case hex (97)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__escaped____lower_case_hex_97( Type documentType ) + { + const string selector = "$['\\u263a']"; + var document = TestHelper.Parse( documentType, + """ { "☺": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, surrogate pair 𝄞 (98)" )] - public void Test_single_quotes__surrogate_pair____98() - { - var selector = "$['\\uD834\\uDD1E']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, surrogate pair 𝄞 (98)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__surrogate_pair____98( Type documentType ) + { + const string selector = "$['\\uD834\\uDD1E']"; + var document = TestHelper.Parse( documentType, + """ { "𝄞": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, surrogate pair 😀 (99)" )] - public void Test_single_quotes__surrogate_pair____99() - { - var selector = "$['\\uD83D\\uDE00']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, surrogate pair 😀 (99)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__surrogate_pair____99( Type documentType ) + { + const string selector = "$['\\uD83D\\uDE00']"; + var document = TestHelper.Parse( documentType, + """ { "😀": "A" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "A" ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"single quotes, invalid escaped double quote (100)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__invalid_escaped_double_quote_100( Type documentType ) + { + const string selector = "$['\\\"']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, embedded single quote (101)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__embedded_single_quote_101( Type documentType ) + { + const string selector = "$[''']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"single quotes, invalid escaped double quote (100)" )] - public void Test_single_quotes__invalid_escaped_double_quote_100() - { - var selector = "$['\\\"']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, embedded single quote (101)" )] - public void Test_single_quotes__embedded_single_quote_101() - { - var selector = "$[''']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"single quotes, incomplete escape (102)" )] - public void Test_single_quotes__incomplete_escape_102() - { - var selector = "$['\\']"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"double quotes, empty (103)" )] - public void Test_double_quotes__empty_103() - { - var selector = "$[\"\"]"; - var document = JsonNode.Parse( - """ + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"single quotes, incomplete escape (102)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__incomplete_escape_102( Type documentType ) + { + const string selector = "$['\\']"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"double quotes, empty (103)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_double_quotes__empty_103( Type documentType ) + { + const string selector = "$[\"\"]"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B", "": "C" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "C" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"single quotes, empty (104)" )] - public void Test_single_quotes__empty_104() - { - var selector = "$['']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"single quotes, empty (104)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_single_quotes__empty_104( Type documentType ) + { + const string selector = "$['']"; + var document = TestHelper.Parse( documentType, + """ { "a": "A", "b": "B", "": "C" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "C" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); } } + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs index 8c588d50..45ad0f96 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs @@ -1,20 +1,22 @@ // This file was auto generated. +using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; +using Hyperbee.Json.Cts.TestSupport; -namespace Hyperbee.Json.Cts.Tests +namespace Hyperbee.Json.Cts.Tests; + +[TestClass] +public class CtsSliceSelectorTest { - [TestClass] - public class CtsSliceSelectorTest + [DataTestMethod( @"slice selector (1)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice_selector_1( Type documentType ) { - - [TestMethod( @"slice selector (1)" )] - public void Test_slice_selector_1() - { - var selector = "$[1:3]"; - var document = JsonNode.Parse( - """ + const string selector = "$[1:3]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -28,25 +30,27 @@ public void Test_slice_selector_1() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1, 2 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice selector with step (2)" )] - public void Test_slice_selector_with_step_2() - { - var selector = "$[1:6:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice selector with step (2)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice_selector_with_step_2( Type documentType ) + { + const string selector = "$[1:6:2]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -60,26 +64,28 @@ public void Test_slice_selector_with_step_2() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1, 3, 5 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice selector with everything omitted, short form (3)" )] - public void Test_slice_selector_with_everything_omitted__short_form_3() - { - var selector = "$[:]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice selector with everything omitted, short form (3)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice_selector_with_everything_omitted__short_form_3( Type documentType ) + { + const string selector = "$[:]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -87,27 +93,29 @@ public void Test_slice_selector_with_everything_omitted__short_form_3() 3 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 1, 2, 3 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice selector with everything omitted, long form (4)" )] - public void Test_slice_selector_with_everything_omitted__long_form_4() - { - var selector = "$[::]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice selector with everything omitted, long form (4)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice_selector_with_everything_omitted__long_form_4( Type documentType ) + { + const string selector = "$[::]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -115,27 +123,29 @@ public void Test_slice_selector_with_everything_omitted__long_form_4() 3 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 1, 2, 3 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice selector with start omitted (5)" )] - public void Test_slice_selector_with_start_omitted_5() - { - var selector = "$[:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice selector with start omitted (5)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice_selector_with_start_omitted_5( Type documentType ) + { + const string selector = "$[:2]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -149,25 +159,27 @@ public void Test_slice_selector_with_start_omitted_5() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice selector with start and end omitted (6)" )] - public void Test_slice_selector_with_start_and_end_omitted_6() - { - var selector = "$[::2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice selector with start and end omitted (6)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice_selector_with_start_and_end_omitted_6( Type documentType ) + { + const string selector = "$[::2]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -181,9 +193,9 @@ public void Test_slice_selector_with_start_and_end_omitted_6() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 2, @@ -191,18 +203,20 @@ public void Test_slice_selector_with_start_and_end_omitted_6() 6, 8 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative step with default start and end (7)" )] - public void Test_negative_step_with_default_start_and_end_7() - { - var selector = "$[::-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative step with default start and end (7)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_step_with_default_start_and_end_7( Type documentType ) + { + const string selector = "$[::-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -210,27 +224,29 @@ public void Test_negative_step_with_default_start_and_end_7() 3 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 3, 2, 1, 0 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative step with default start (8)" )] - public void Test_negative_step_with_default_start_8() - { - var selector = "$[:0:-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative step with default start (8)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_step_with_default_start_8( Type documentType ) + { + const string selector = "$[:0:-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -238,26 +254,28 @@ public void Test_negative_step_with_default_start_8() 3 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 3, 2, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative step with default end (9)" )] - public void Test_negative_step_with_default_end_9() - { - var selector = "$[2::-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative step with default end (9)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_step_with_default_end_9( Type documentType ) + { + const string selector = "$[2::-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -265,26 +283,28 @@ public void Test_negative_step_with_default_end_9() 3 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 1, 0 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"larger negative step (10)" )] - public void Test_larger_negative_step_10() - { - var selector = "$[::-2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"larger negative step (10)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_larger_negative_step_10( Type documentType ) + { + const string selector = "$[::-2]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -292,25 +312,27 @@ public void Test_larger_negative_step_10() 3 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 3, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative range with default step (11)" )] - public void Test_negative_range_with_default_step_11() - { - var selector = "$[-1:-3]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative range with default step (11)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_range_with_default_step_11( Type documentType ) + { + const string selector = "$[-1:-3]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -324,22 +346,24 @@ public void Test_negative_range_with_default_step_11() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative range with negative step (12)" )] - public void Test_negative_range_with_negative_step_12() - { - var selector = "$[-1:-3:-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative range with negative step (12)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_range_with_negative_step_12( Type documentType ) + { + const string selector = "$[-1:-3:-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -353,25 +377,27 @@ public void Test_negative_range_with_negative_step_12() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 9, 8 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative range with larger negative step (13)" )] - public void Test_negative_range_with_larger_negative_step_13() - { - var selector = "$[-1:-6:-2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative range with larger negative step (13)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_range_with_larger_negative_step_13( Type documentType ) + { + const string selector = "$[-1:-6:-2]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -385,26 +411,28 @@ public void Test_negative_range_with_larger_negative_step_13() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 9, 7, 5 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"larger negative range with larger negative step (14)" )] - public void Test_larger_negative_range_with_larger_negative_step_14() - { - var selector = "$[-1:-7:-2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"larger negative range with larger negative step (14)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_larger_negative_range_with_larger_negative_step_14( Type documentType ) + { + const string selector = "$[-1:-7:-2]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -418,26 +446,28 @@ public void Test_larger_negative_range_with_larger_negative_step_14() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 9, 7, 5 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative from, positive to (15)" )] - public void Test_negative_from__positive_to_15() - { - var selector = "$[-5:7]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative from, positive to (15)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_from__positive_to_15( Type documentType ) + { + const string selector = "$[-5:7]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -451,25 +481,27 @@ public void Test_negative_from__positive_to_15() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 5, 6 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative from (16)" )] - public void Test_negative_from_16() - { - var selector = "$[-2:]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative from (16)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_from_16( Type documentType ) + { + const string selector = "$[-2:]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -483,25 +515,27 @@ public void Test_negative_from_16() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 8, 9 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"positive from, negative to (17)" )] - public void Test_positive_from__negative_to_17() - { - var selector = "$[1:-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"positive from, negative to (17)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_positive_from__negative_to_17( Type documentType ) + { + const string selector = "$[1:-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -515,9 +549,9 @@ public void Test_positive_from__negative_to_17() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -528,18 +562,20 @@ public void Test_positive_from__negative_to_17() 7, 8 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative from, positive to, negative step (18)" )] - public void Test_negative_from__positive_to__negative_step_18() - { - var selector = "$[-1:1:-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative from, positive to, negative step (18)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_from__positive_to__negative_step_18( Type documentType ) + { + const string selector = "$[-1:1:-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -553,9 +589,9 @@ public void Test_negative_from__positive_to__negative_step_18() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 9, 8, @@ -566,18 +602,20 @@ public void Test_negative_from__positive_to__negative_step_18() 3, 2 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"positive from, negative to, negative step (19)" )] - public void Test_positive_from__negative_to__negative_step_19() - { - var selector = "$[7:-5:-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"positive from, negative to, negative step (19)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_positive_from__negative_to__negative_step_19( Type documentType ) + { + const string selector = "$[7:-5:-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -591,43 +629,49 @@ public void Test_positive_from__negative_to__negative_step_19() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 7, 6 ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } + + [DataTestMethod( @"too many colons (20)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_too_many_colons_20( Type documentType ) + { + const string selector = "$[1:2:3:4]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"too many colons (20)" )] - public void Test_too_many_colons_20() - { - var selector = "$[1:2:3:4]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"non-integer array index (21)" )] - public void Test_non_integer_array_index_21() - { - var selector = "$[1:2:a]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"zero step (22)" )] - public void Test_zero_step_22() - { - var selector = "$[1:2:0]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"non-integer array index (21)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_non_integer_array_index_21( Type documentType ) + { + const string selector = "$[1:2:a]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"zero step (22)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_zero_step_22( Type documentType ) + { + const string selector = "$[1:2:0]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -641,22 +685,24 @@ public void Test_zero_step_22() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"empty range (23)" )] - public void Test_empty_range_23() - { - var selector = "$[2:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"empty range (23)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_empty_range_23( Type documentType ) + { + const string selector = "$[2:2]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -670,58 +716,64 @@ public void Test_empty_range_23() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice selector with everything omitted with empty array (24)" )] - public void Test_slice_selector_with_everything_omitted_with_empty_array_24() - { - var selector = "$[:]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice selector with everything omitted with empty array (24)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice_selector_with_everything_omitted_with_empty_array_24( Type documentType ) + { + const string selector = "$[:]"; + var document = TestHelper.Parse( documentType, + """ [] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"negative step with empty array (25)" )] - public void Test_negative_step_with_empty_array_25() - { - var selector = "$[::-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"negative step with empty array (25)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_negative_step_with_empty_array_25( Type documentType ) + { + const string selector = "$[::-1]"; + var document = TestHelper.Parse( documentType, + """ [] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"maximal range with positive step (26)" )] - public void Test_maximal_range_with_positive_step_26() - { - var selector = "$[0:10]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"maximal range with positive step (26)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_maximal_range_with_positive_step_26( Type documentType ) + { + const string selector = "$[0:10]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -735,9 +787,9 @@ public void Test_maximal_range_with_positive_step_26() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -750,18 +802,20 @@ public void Test_maximal_range_with_positive_step_26() 8, 9 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"maximal range with negative step (27)" )] - public void Test_maximal_range_with_negative_step_27() - { - var selector = "$[9:0:-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"maximal range with negative step (27)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_maximal_range_with_negative_step_27( Type documentType ) + { + const string selector = "$[9:0:-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -775,9 +829,9 @@ public void Test_maximal_range_with_negative_step_27() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 9, 8, @@ -789,18 +843,20 @@ public void Test_maximal_range_with_negative_step_27() 2, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"excessively large to value (28)" )] - public void Test_excessively_large_to_value_28() - { - var selector = "$[2:113667776004]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"excessively large to value (28)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_excessively_large_to_value_28( Type documentType ) + { + const string selector = "$[2:113667776004]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -814,9 +870,9 @@ public void Test_excessively_large_to_value_28() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 3, @@ -827,18 +883,20 @@ public void Test_excessively_large_to_value_28() 8, 9 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"excessively small from value (29)" )] - public void Test_excessively_small_from_value_29() - { - var selector = "$[-113667776004:1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"excessively small from value (29)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_excessively_small_from_value_29( Type documentType ) + { + const string selector = "$[-113667776004:1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -852,24 +910,26 @@ public void Test_excessively_small_from_value_29() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 0 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"excessively large from value with negative step (30)" )] - public void Test_excessively_large_from_value_with_negative_step_30() - { - var selector = "$[113667776004:0:-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"excessively large from value with negative step (30)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_excessively_large_from_value_with_negative_step_30( Type documentType ) + { + const string selector = "$[113667776004:0:-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -883,9 +943,9 @@ public void Test_excessively_large_from_value_with_negative_step_30() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 9, 8, @@ -897,18 +957,20 @@ public void Test_excessively_large_from_value_with_negative_step_30() 2, 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"excessively small to value with negative step (31)" )] - public void Test_excessively_small_to_value_with_negative_step_31() - { - var selector = "$[3:-113667776004:-1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"excessively small to value with negative step (31)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_excessively_small_to_value_with_negative_step_31( Type documentType ) + { + const string selector = "$[3:-113667776004:-1]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -922,27 +984,29 @@ public void Test_excessively_small_to_value_with_negative_step_31() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 3, 2, 1, 0 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"excessively large step (32)" )] - public void Test_excessively_large_step_32() - { - var selector = "$[1:10:113667776004]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"excessively large step (32)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_excessively_large_step_32( Type documentType ) + { + const string selector = "$[1:10:113667776004]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -956,24 +1020,26 @@ public void Test_excessively_large_step_32() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 1 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"excessively small step (33)" )] - public void Test_excessively_small_step_33() - { - var selector = "$[-1:-10:-113667776004]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"excessively small step (33)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_excessively_small_step_33( Type documentType ) + { + const string selector = "$[-1:-10:-113667776004]"; + var document = TestHelper.Parse( documentType, + """ [ 0, 1, @@ -987,71 +1053,83 @@ public void Test_excessively_small_step_33() 9 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 9 ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"overflowing to value (34)" )] - public void Test_overflowing_to_value_34() - { - var selector = "$[2:231584178474632390847141970017375815706539969331281128078915168015826259279872]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"underflowing from value (35)" )] - public void Test_underflowing_from_value_35() - { - var selector = "$[-231584178474632390847141970017375815706539969331281128078915168015826259279872:1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"overflowing from value with negative step (36)" )] - public void Test_overflowing_from_value_with_negative_step_36() - { - var selector = "$[231584178474632390847141970017375815706539969331281128078915168015826259279872:0:-1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"underflowing to value with negative step (37)" )] - public void Test_underflowing_to_value_with_negative_step_37() - { - var selector = "$[3:-231584178474632390847141970017375815706539969331281128078915168015826259279872:-1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"overflowing step (38)" )] - public void Test_overflowing_step_38() - { - var selector = "$[1:10:231584178474632390847141970017375815706539969331281128078915168015826259279872]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"underflowing step (39)" )] - public void Test_underflowing_step_39() - { - var selector = "$[-1:-10:-231584178474632390847141970017375815706539969331281128078915168015826259279872]"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + [DataTestMethod( @"overflowing to value (34)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_overflowing_to_value_34( Type documentType ) + { + const string selector = "$[2:231584178474632390847141970017375815706539969331281128078915168015826259279872]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"underflowing from value (35)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_underflowing_from_value_35( Type documentType ) + { + const string selector = "$[-231584178474632390847141970017375815706539969331281128078915168015826259279872:1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"overflowing from value with negative step (36)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_overflowing_from_value_with_negative_step_36( Type documentType ) + { + const string selector = "$[231584178474632390847141970017375815706539969331281128078915168015826259279872:0:-1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"underflowing to value with negative step (37)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_underflowing_to_value_with_negative_step_37( Type documentType ) + { + const string selector = "$[3:-231584178474632390847141970017375815706539969331281128078915168015826259279872:-1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"overflowing step (38)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_overflowing_step_38( Type documentType ) + { + const string selector = "$[1:10:231584178474632390847141970017375815706539969331281128078915168015826259279872]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"underflowing step (39)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_underflowing_step_39( Type documentType ) + { + const string selector = "$[-1:-10:-231584178474632390847141970017375815706539969331281128078915168015826259279872]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); } } + diff --git a/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs index c892887d..98503bad 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs @@ -1,20 +1,22 @@ // This file was auto generated. +using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; +using Hyperbee.Json.Cts.TestSupport; -namespace Hyperbee.Json.Cts.Tests +namespace Hyperbee.Json.Cts.Tests; + +[TestClass] +public class CtsWhitespaceTest { - [TestClass] - public class CtsWhitespaceTest + [DataTestMethod( @"filter, space between question mark and expression (1)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__space_between_question_mark_and_expression_1( Type documentType ) { - - [TestMethod( @"filter, space between question mark and expression (1)" )] - public void Test_filter__space_between_question_mark_and_expression_1() - { - var selector = "$[? @.a]"; - var document = JsonNode.Parse( - """ + const string selector = "$[? @.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -26,27 +28,29 @@ public void Test_filter__space_between_question_mark_and_expression_1() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, newline between question mark and expression (2)" )] - public void Test_filter__newline_between_question_mark_and_expression_2() - { - var selector = "$[?\n@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, newline between question mark and expression (2)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__newline_between_question_mark_and_expression_2( Type documentType ) + { + const string selector = "$[?\n@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -58,27 +62,29 @@ public void Test_filter__newline_between_question_mark_and_expression_2() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, tab between question mark and expression (3)" )] - public void Test_filter__tab_between_question_mark_and_expression_3() - { - var selector = "$[?\t@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, tab between question mark and expression (3)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__tab_between_question_mark_and_expression_3( Type documentType ) + { + const string selector = "$[?\t@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -90,27 +96,29 @@ public void Test_filter__tab_between_question_mark_and_expression_3() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, return between question mark and expression (4)" )] - public void Test_filter__return_between_question_mark_and_expression_4() - { - var selector = "$[?\r@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, return between question mark and expression (4)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__return_between_question_mark_and_expression_4( Type documentType ) + { + const string selector = "$[?\r@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -122,27 +130,29 @@ public void Test_filter__return_between_question_mark_and_expression_4() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, space between question mark and parenthesized expression (5)" )] - public void Test_filter__space_between_question_mark_and_parenthesized_expression_5() - { - var selector = "$[? (@.a)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, space between question mark and parenthesized expression (5)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__space_between_question_mark_and_parenthesized_expression_5( Type documentType ) + { + const string selector = "$[? (@.a)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -154,27 +164,29 @@ public void Test_filter__space_between_question_mark_and_parenthesized_expressio } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, newline between question mark and parenthesized expression (6)" )] - public void Test_filter__newline_between_question_mark_and_parenthesized_expression_6() - { - var selector = "$[?\n(@.a)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, newline between question mark and parenthesized expression (6)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__newline_between_question_mark_and_parenthesized_expression_6( Type documentType ) + { + const string selector = "$[?\n(@.a)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -186,27 +198,29 @@ public void Test_filter__newline_between_question_mark_and_parenthesized_express } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, tab between question mark and parenthesized expression (7)" )] - public void Test_filter__tab_between_question_mark_and_parenthesized_expression_7() - { - var selector = "$[?\t(@.a)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, tab between question mark and parenthesized expression (7)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__tab_between_question_mark_and_parenthesized_expression_7( Type documentType ) + { + const string selector = "$[?\t(@.a)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -218,27 +232,29 @@ public void Test_filter__tab_between_question_mark_and_parenthesized_expression_ } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, return between question mark and parenthesized expression (8)" )] - public void Test_filter__return_between_question_mark_and_parenthesized_expression_8() - { - var selector = "$[?\r(@.a)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, return between question mark and parenthesized expression (8)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__return_between_question_mark_and_parenthesized_expression_8( Type documentType ) + { + const string selector = "$[?\r(@.a)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -250,27 +266,29 @@ public void Test_filter__return_between_question_mark_and_parenthesized_expressi } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, space between parenthesized expression and bracket (9)" )] - public void Test_filter__space_between_parenthesized_expression_and_bracket_9() - { - var selector = "$[?(@.a) ]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, space between parenthesized expression and bracket (9)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__space_between_parenthesized_expression_and_bracket_9( Type documentType ) + { + const string selector = "$[?(@.a) ]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -282,27 +300,29 @@ public void Test_filter__space_between_parenthesized_expression_and_bracket_9() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, newline between parenthesized expression and bracket (10)" )] - public void Test_filter__newline_between_parenthesized_expression_and_bracket_10() - { - var selector = "$[?(@.a)\n]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, newline between parenthesized expression and bracket (10)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__newline_between_parenthesized_expression_and_bracket_10( Type documentType ) + { + const string selector = "$[?(@.a)\n]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -314,27 +334,29 @@ public void Test_filter__newline_between_parenthesized_expression_and_bracket_10 } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, tab between parenthesized expression and bracket (11)" )] - public void Test_filter__tab_between_parenthesized_expression_and_bracket_11() - { - var selector = "$[?(@.a)\t]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, tab between parenthesized expression and bracket (11)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__tab_between_parenthesized_expression_and_bracket_11( Type documentType ) + { + const string selector = "$[?(@.a)\t]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -346,27 +368,29 @@ public void Test_filter__tab_between_parenthesized_expression_and_bracket_11() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, return between parenthesized expression and bracket (12)" )] - public void Test_filter__return_between_parenthesized_expression_and_bracket_12() - { - var selector = "$[?(@.a)\r]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, return between parenthesized expression and bracket (12)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__return_between_parenthesized_expression_and_bracket_12( Type documentType ) + { + const string selector = "$[?(@.a)\r]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -378,27 +402,29 @@ public void Test_filter__return_between_parenthesized_expression_and_bracket_12( } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, space between bracket and question mark (13)" )] - public void Test_filter__space_between_bracket_and_question_mark_13() - { - var selector = "$[ ?@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, space between bracket and question mark (13)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__space_between_bracket_and_question_mark_13( Type documentType ) + { + const string selector = "$[ ?@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -410,27 +436,29 @@ public void Test_filter__space_between_bracket_and_question_mark_13() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, newline between bracket and question mark (14)" )] - public void Test_filter__newline_between_bracket_and_question_mark_14() - { - var selector = "$[\n?@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, newline between bracket and question mark (14)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__newline_between_bracket_and_question_mark_14( Type documentType ) + { + const string selector = "$[\n?@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -442,27 +470,29 @@ public void Test_filter__newline_between_bracket_and_question_mark_14() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, tab between bracket and question mark (15)" )] - public void Test_filter__tab_between_bracket_and_question_mark_15() - { - var selector = "$[\t?@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, tab between bracket and question mark (15)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__tab_between_bracket_and_question_mark_15( Type documentType ) + { + const string selector = "$[\t?@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -474,27 +504,29 @@ public void Test_filter__tab_between_bracket_and_question_mark_15() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"filter, return between bracket and question mark (16)" )] - public void Test_filter__return_between_bracket_and_question_mark_16() - { - var selector = "$[\r?@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"filter, return between bracket and question mark (16)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_filter__return_between_bracket_and_question_mark_16( Type documentType ) + { + const string selector = "$[\r?@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "b", @@ -506,63 +538,73 @@ public void Test_filter__return_between_bracket_and_question_mark_16() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "b", "d": "e" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, space between function name and parenthesis (17)" )] - public void Test_functions__space_between_function_name_and_parenthesis_17() - { - var selector = "$[?count (@.*)==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"functions, space between function name and parenthesis (17)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__space_between_function_name_and_parenthesis_17( Type documentType ) + { + const string selector = "$[?count (@.*)==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"functions, newline between function name and parenthesis (18)" )] - public void Test_functions__newline_between_function_name_and_parenthesis_18() - { - var selector = "$[?count\n(@.*)==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"functions, newline between function name and parenthesis (18)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__newline_between_function_name_and_parenthesis_18( Type documentType ) + { + const string selector = "$[?count\n(@.*)==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"functions, tab between function name and parenthesis (19)" )] - public void Test_functions__tab_between_function_name_and_parenthesis_19() - { - var selector = "$[?count\t(@.*)==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"functions, tab between function name and parenthesis (19)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__tab_between_function_name_and_parenthesis_19( Type documentType ) + { + const string selector = "$[?count\t(@.*)==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"functions, return between function name and parenthesis (20)" )] - public void Test_functions__return_between_function_name_and_parenthesis_20() - { - var selector = "$[?count\r(@.*)==1]"; - var document = JsonNode.Parse( "[0]" ); // Empty node + [DataTestMethod( @"functions, return between function name and parenthesis (20)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__return_between_function_name_and_parenthesis_20( Type documentType ) + { + const string selector = "$[?count\r(@.*)==1]"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } - [TestMethod( @"functions, space between parenthesis and arg (21)" )] - public void Test_functions__space_between_parenthesis_and_arg_21() - { - var selector = "$[?count( @.*)==1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, space between parenthesis and arg (21)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__space_between_parenthesis_and_arg_21( Type documentType ) + { + const string selector = "$[?count( @.*)==1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -576,9 +618,9 @@ public void Test_functions__space_between_parenthesis_and_arg_21() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -587,18 +629,20 @@ public void Test_functions__space_between_parenthesis_and_arg_21() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, newline between parenthesis and arg (22)" )] - public void Test_functions__newline_between_parenthesis_and_arg_22() - { - var selector = "$[?count(\n@.*)==1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, newline between parenthesis and arg (22)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__newline_between_parenthesis_and_arg_22( Type documentType ) + { + const string selector = "$[?count(\n@.*)==1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -612,9 +656,9 @@ public void Test_functions__newline_between_parenthesis_and_arg_22() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -623,18 +667,20 @@ public void Test_functions__newline_between_parenthesis_and_arg_22() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, tab between parenthesis and arg (23)" )] - public void Test_functions__tab_between_parenthesis_and_arg_23() - { - var selector = "$[?count(\t@.*)==1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, tab between parenthesis and arg (23)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__tab_between_parenthesis_and_arg_23( Type documentType ) + { + const string selector = "$[?count(\t@.*)==1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -648,9 +694,9 @@ public void Test_functions__tab_between_parenthesis_and_arg_23() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -659,18 +705,20 @@ public void Test_functions__tab_between_parenthesis_and_arg_23() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, return between parenthesis and arg (24)" )] - public void Test_functions__return_between_parenthesis_and_arg_24() - { - var selector = "$[?count(\r@.*)==1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, return between parenthesis and arg (24)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__return_between_parenthesis_and_arg_24( Type documentType ) + { + const string selector = "$[?count(\r@.*)==1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -684,9 +732,9 @@ public void Test_functions__return_between_parenthesis_and_arg_24() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -695,202 +743,220 @@ public void Test_functions__return_between_parenthesis_and_arg_24() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, space between arg and comma (25)" )] - public void Test_functions__space_between_arg_and_comma_25() - { - var selector = "$[?search(@ ,'[a-z]+')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, space between arg and comma (25)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__space_between_arg_and_comma_25( Type documentType ) + { + const string selector = "$[?search(@ ,'[a-z]+')]"; + var document = TestHelper.Parse( documentType, + """ [ "foo", "123" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, newline between arg and comma (26)" )] - public void Test_functions__newline_between_arg_and_comma_26() - { - var selector = "$[?search(@\n,'[a-z]+')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, newline between arg and comma (26)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__newline_between_arg_and_comma_26( Type documentType ) + { + const string selector = "$[?search(@\n,'[a-z]+')]"; + var document = TestHelper.Parse( documentType, + """ [ "foo", "123" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, tab between arg and comma (27)" )] - public void Test_functions__tab_between_arg_and_comma_27() - { - var selector = "$[?search(@\t,'[a-z]+')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, tab between arg and comma (27)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__tab_between_arg_and_comma_27( Type documentType ) + { + const string selector = "$[?search(@\t,'[a-z]+')]"; + var document = TestHelper.Parse( documentType, + """ [ "foo", "123" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, return between arg and comma (28)" )] - public void Test_functions__return_between_arg_and_comma_28() - { - var selector = "$[?search(@\r,'[a-z]+')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, return between arg and comma (28)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__return_between_arg_and_comma_28( Type documentType ) + { + const string selector = "$[?search(@\r,'[a-z]+')]"; + var document = TestHelper.Parse( documentType, + """ [ "foo", "123" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, space between comma and arg (29)" )] - public void Test_functions__space_between_comma_and_arg_29() - { - var selector = "$[?search(@, '[a-z]+')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, space between comma and arg (29)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__space_between_comma_and_arg_29( Type documentType ) + { + const string selector = "$[?search(@, '[a-z]+')]"; + var document = TestHelper.Parse( documentType, + """ [ "foo", "123" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, newline between comma and arg (30)" )] - public void Test_functions__newline_between_comma_and_arg_30() - { - var selector = "$[?search(@,\n'[a-z]+')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, newline between comma and arg (30)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__newline_between_comma_and_arg_30( Type documentType ) + { + const string selector = "$[?search(@,\n'[a-z]+')]"; + var document = TestHelper.Parse( documentType, + """ [ "foo", "123" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, tab between comma and arg (31)" )] - public void Test_functions__tab_between_comma_and_arg_31() - { - var selector = "$[?search(@,\t'[a-z]+')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, tab between comma and arg (31)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__tab_between_comma_and_arg_31( Type documentType ) + { + const string selector = "$[?search(@,\t'[a-z]+')]"; + var document = TestHelper.Parse( documentType, + """ [ "foo", "123" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, return between comma and arg (32)" )] - public void Test_functions__return_between_comma_and_arg_32() - { - var selector = "$[?search(@,\r'[a-z]+')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, return between comma and arg (32)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__return_between_comma_and_arg_32( Type documentType ) + { + const string selector = "$[?search(@,\r'[a-z]+')]"; + var document = TestHelper.Parse( documentType, + """ [ "foo", "123" ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, space between arg and parenthesis (33)" )] - public void Test_functions__space_between_arg_and_parenthesis_33() - { - var selector = "$[?count(@.* )==1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, space between arg and parenthesis (33)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__space_between_arg_and_parenthesis_33( Type documentType ) + { + const string selector = "$[?count(@.* )==1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -904,9 +970,9 @@ public void Test_functions__space_between_arg_and_parenthesis_33() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -915,18 +981,20 @@ public void Test_functions__space_between_arg_and_parenthesis_33() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, newline between arg and parenthesis (34)" )] - public void Test_functions__newline_between_arg_and_parenthesis_34() - { - var selector = "$[?count(@.*\n)==1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, newline between arg and parenthesis (34)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__newline_between_arg_and_parenthesis_34( Type documentType ) + { + const string selector = "$[?count(@.*\n)==1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -940,9 +1008,9 @@ public void Test_functions__newline_between_arg_and_parenthesis_34() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -951,18 +1019,20 @@ public void Test_functions__newline_between_arg_and_parenthesis_34() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, tab between arg and parenthesis (35)" )] - public void Test_functions__tab_between_arg_and_parenthesis_35() - { - var selector = "$[?count(@.*\t)==1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, tab between arg and parenthesis (35)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__tab_between_arg_and_parenthesis_35( Type documentType ) + { + const string selector = "$[?count(@.*\t)==1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -976,9 +1046,9 @@ public void Test_functions__tab_between_arg_and_parenthesis_35() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -987,18 +1057,20 @@ public void Test_functions__tab_between_arg_and_parenthesis_35() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, return between arg and parenthesis (36)" )] - public void Test_functions__return_between_arg_and_parenthesis_36() - { - var selector = "$[?count(@.*\r)==1]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, return between arg and parenthesis (36)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__return_between_arg_and_parenthesis_36( Type documentType ) + { + const string selector = "$[?count(@.*\r)==1]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1012,9 +1084,9 @@ public void Test_functions__return_between_arg_and_parenthesis_36() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1023,18 +1095,20 @@ public void Test_functions__return_between_arg_and_parenthesis_36() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, spaces in a relative singular selector (37)" )] - public void Test_functions__spaces_in_a_relative_singular_selector_37() - { - var selector = "$[?length(@ .a .b) == 3]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, spaces in a relative singular selector (37)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__spaces_in_a_relative_singular_selector_37( Type documentType ) + { + const string selector = "$[?length(@ .a .b) == 3]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": { @@ -1044,9 +1118,9 @@ public void Test_functions__spaces_in_a_relative_singular_selector_37() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": { @@ -1054,18 +1128,20 @@ public void Test_functions__spaces_in_a_relative_singular_selector_37() } } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, newlines in a relative singular selector (38)" )] - public void Test_functions__newlines_in_a_relative_singular_selector_38() - { - var selector = "$[?length(@\n.a\n.b) == 3]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, newlines in a relative singular selector (38)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__newlines_in_a_relative_singular_selector_38( Type documentType ) + { + const string selector = "$[?length(@\n.a\n.b) == 3]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": { @@ -1075,9 +1151,9 @@ public void Test_functions__newlines_in_a_relative_singular_selector_38() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": { @@ -1085,18 +1161,20 @@ public void Test_functions__newlines_in_a_relative_singular_selector_38() } } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, tabs in a relative singular selector (39)" )] - public void Test_functions__tabs_in_a_relative_singular_selector_39() - { - var selector = "$[?length(@\t.a\t.b) == 3]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, tabs in a relative singular selector (39)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__tabs_in_a_relative_singular_selector_39( Type documentType ) + { + const string selector = "$[?length(@\t.a\t.b) == 3]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": { @@ -1106,9 +1184,9 @@ public void Test_functions__tabs_in_a_relative_singular_selector_39() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": { @@ -1116,18 +1194,20 @@ public void Test_functions__tabs_in_a_relative_singular_selector_39() } } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, returns in a relative singular selector (40)" )] - public void Test_functions__returns_in_a_relative_singular_selector_40() - { - var selector = "$[?length(@\r.a\r.b) == 3]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, returns in a relative singular selector (40)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__returns_in_a_relative_singular_selector_40( Type documentType ) + { + const string selector = "$[?length(@\r.a\r.b) == 3]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": { @@ -1137,9 +1217,9 @@ public void Test_functions__returns_in_a_relative_singular_selector_40() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": { @@ -1147,18 +1227,20 @@ public void Test_functions__returns_in_a_relative_singular_selector_40() } } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, spaces in an absolute singular selector (41)" )] - public void Test_functions__spaces_in_an_absolute_singular_selector_41() - { - var selector = "$..[?length(@)==length($ [0] .a)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, spaces in an absolute singular selector (41)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__spaces_in_an_absolute_singular_selector_41( Type documentType ) + { + const string selector = "$..[?length(@)==length($ [0] .a)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "foo" @@ -1166,24 +1248,26 @@ public void Test_functions__spaces_in_an_absolute_singular_selector_41() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, newlines in an absolute singular selector (42)" )] - public void Test_functions__newlines_in_an_absolute_singular_selector_42() - { - var selector = "$..[?length(@)==length($\n[0]\n.a)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, newlines in an absolute singular selector (42)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__newlines_in_an_absolute_singular_selector_42( Type documentType ) + { + const string selector = "$..[?length(@)==length($\n[0]\n.a)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "foo" @@ -1191,24 +1275,26 @@ public void Test_functions__newlines_in_an_absolute_singular_selector_42() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, tabs in an absolute singular selector (43)" )] - public void Test_functions__tabs_in_an_absolute_singular_selector_43() - { - var selector = "$..[?length(@)==length($\t[0]\t.a)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, tabs in an absolute singular selector (43)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__tabs_in_an_absolute_singular_selector_43( Type documentType ) + { + const string selector = "$..[?length(@)==length($\t[0]\t.a)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "foo" @@ -1216,24 +1302,26 @@ public void Test_functions__tabs_in_an_absolute_singular_selector_43() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"functions, returns in an absolute singular selector (44)" )] - public void Test_functions__returns_in_an_absolute_singular_selector_44() - { - var selector = "$..[?length(@)==length($\r[0]\r.a)]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"functions, returns in an absolute singular selector (44)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_functions__returns_in_an_absolute_singular_selector_44( Type documentType ) + { + const string selector = "$..[?length(@)==length($\r[0]\r.a)]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "foo" @@ -1241,24 +1329,26 @@ public void Test_functions__returns_in_an_absolute_singular_selector_44() {} ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "foo" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space before || (45)" )] - public void Test_operators__space_before____45() - { - var selector = "$[?@.a ||@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space before || (45)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_before____45( Type documentType ) + { + const string selector = "$[?@.a ||@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1271,9 +1361,9 @@ public void Test_operators__space_before____45() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1282,18 +1372,20 @@ public void Test_operators__space_before____45() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline before || (46)" )] - public void Test_operators__newline_before____46() - { - var selector = "$[?@.a\n||@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline before || (46)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_before____46( Type documentType ) + { + const string selector = "$[?@.a\n||@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1306,9 +1398,9 @@ public void Test_operators__newline_before____46() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1317,18 +1409,20 @@ public void Test_operators__newline_before____46() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab before || (47)" )] - public void Test_operators__tab_before____47() - { - var selector = "$[?@.a\t||@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab before || (47)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_before____47( Type documentType ) + { + const string selector = "$[?@.a\t||@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1341,9 +1435,9 @@ public void Test_operators__tab_before____47() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1352,18 +1446,20 @@ public void Test_operators__tab_before____47() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return before || (48)" )] - public void Test_operators__return_before____48() - { - var selector = "$[?@.a\r||@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return before || (48)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_before____48( Type documentType ) + { + const string selector = "$[?@.a\r||@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1376,9 +1472,9 @@ public void Test_operators__return_before____48() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1387,18 +1483,20 @@ public void Test_operators__return_before____48() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space after || (49)" )] - public void Test_operators__space_after____49() - { - var selector = "$[?@.a|| @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space after || (49)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_after____49( Type documentType ) + { + const string selector = "$[?@.a|| @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1411,9 +1509,9 @@ public void Test_operators__space_after____49() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1422,18 +1520,20 @@ public void Test_operators__space_after____49() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline after || (50)" )] - public void Test_operators__newline_after____50() - { - var selector = "$[?@.a||\n@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline after || (50)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_after____50( Type documentType ) + { + const string selector = "$[?@.a||\n@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1446,9 +1546,9 @@ public void Test_operators__newline_after____50() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1457,18 +1557,20 @@ public void Test_operators__newline_after____50() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab after || (51)" )] - public void Test_operators__tab_after____51() - { - var selector = "$[?@.a||\t@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab after || (51)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_after____51( Type documentType ) + { + const string selector = "$[?@.a||\t@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1481,9 +1583,9 @@ public void Test_operators__tab_after____51() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1492,18 +1594,20 @@ public void Test_operators__tab_after____51() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return after || (52)" )] - public void Test_operators__return_after____52() - { - var selector = "$[?@.a||\r@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return after || (52)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_after____52( Type documentType ) + { + const string selector = "$[?@.a||\r@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1516,9 +1620,9 @@ public void Test_operators__return_after____52() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1527,18 +1631,20 @@ public void Test_operators__return_after____52() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space before && (53)" )] - public void Test_operators__space_before____53() - { - var selector = "$[?@.a &&@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space before && (53)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_before____53( Type documentType ) + { + const string selector = "$[?@.a &&@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1552,27 +1658,29 @@ public void Test_operators__space_before____53() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline before && (54)" )] - public void Test_operators__newline_before____54() - { - var selector = "$[?@.a\n&&@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline before && (54)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_before____54( Type documentType ) + { + const string selector = "$[?@.a\n&&@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1586,27 +1694,29 @@ public void Test_operators__newline_before____54() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab before && (55)" )] - public void Test_operators__tab_before____55() - { - var selector = "$[?@.a\t&&@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab before && (55)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_before____55( Type documentType ) + { + const string selector = "$[?@.a\t&&@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1620,27 +1730,29 @@ public void Test_operators__tab_before____55() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return before && (56)" )] - public void Test_operators__return_before____56() - { - var selector = "$[?@.a\r&&@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return before && (56)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_before____56( Type documentType ) + { + const string selector = "$[?@.a\r&&@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1654,27 +1766,29 @@ public void Test_operators__return_before____56() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space after && (57)" )] - public void Test_operators__space_after____57() - { - var selector = "$[?@.a&& @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space after && (57)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_after____57( Type documentType ) + { + const string selector = "$[?@.a&& @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1688,27 +1802,29 @@ public void Test_operators__space_after____57() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline after && (58)" )] - public void Test_operators__newline_after____58() - { - var selector = "$[?@.a&& @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline after && (58)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_after____58( Type documentType ) + { + const string selector = "$[?@.a&& @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1722,27 +1838,29 @@ public void Test_operators__newline_after____58() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab after && (59)" )] - public void Test_operators__tab_after____59() - { - var selector = "$[?@.a&& @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab after && (59)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_after____59( Type documentType ) + { + const string selector = "$[?@.a&& @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1756,27 +1874,29 @@ public void Test_operators__tab_after____59() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return after && (60)" )] - public void Test_operators__return_after____60() - { - var selector = "$[?@.a&& @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return after && (60)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_after____60( Type documentType ) + { + const string selector = "$[?@.a&& @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1 @@ -1790,27 +1910,29 @@ public void Test_operators__return_after____60() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space before == (61)" )] - public void Test_operators__space_before____61() - { - var selector = "$[?@.a ==@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space before == (61)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_before____61( Type documentType ) + { + const string selector = "$[?@.a ==@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -1822,27 +1944,29 @@ public void Test_operators__space_before____61() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 1 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline before == (62)" )] - public void Test_operators__newline_before____62() - { - var selector = "$[?@.a\n==@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline before == (62)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_before____62( Type documentType ) + { + const string selector = "$[?@.a\n==@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -1854,27 +1978,29 @@ public void Test_operators__newline_before____62() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 1 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab before == (63)" )] - public void Test_operators__tab_before____63() - { - var selector = "$[?@.a\t==@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab before == (63)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_before____63( Type documentType ) + { + const string selector = "$[?@.a\t==@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -1886,27 +2012,29 @@ public void Test_operators__tab_before____63() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 1 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return before == (64)" )] - public void Test_operators__return_before____64() - { - var selector = "$[?@.a\r==@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return before == (64)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_before____64( Type documentType ) + { + const string selector = "$[?@.a\r==@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -1918,27 +2046,29 @@ public void Test_operators__return_before____64() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 1 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space after == (65)" )] - public void Test_operators__space_after____65() - { - var selector = "$[?@.a== @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space after == (65)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_after____65( Type documentType ) + { + const string selector = "$[?@.a== @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -1950,27 +2080,29 @@ public void Test_operators__space_after____65() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 1 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline after == (66)" )] - public void Test_operators__newline_after____66() - { - var selector = "$[?@.a==\n@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline after == (66)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_after____66( Type documentType ) + { + const string selector = "$[?@.a==\n@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -1982,27 +2114,29 @@ public void Test_operators__newline_after____66() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 1 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab after == (67)" )] - public void Test_operators__tab_after____67() - { - var selector = "$[?@.a==\t@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab after == (67)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_after____67( Type documentType ) + { + const string selector = "$[?@.a==\t@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2014,27 +2148,29 @@ public void Test_operators__tab_after____67() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 1 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return after == (68)" )] - public void Test_operators__return_after____68() - { - var selector = "$[?@.a==\r@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return after == (68)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_after____68( Type documentType ) + { + const string selector = "$[?@.a==\r@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2046,27 +2182,29 @@ public void Test_operators__return_after____68() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 1 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space before != (69)" )] - public void Test_operators__space_before____69() - { - var selector = "$[?@.a !=@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space before != (69)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_before____69( Type documentType ) + { + const string selector = "$[?@.a !=@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2078,27 +2216,29 @@ public void Test_operators__space_before____69() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline before != (70)" )] - public void Test_operators__newline_before____70() - { - var selector = "$[?@.a\n!=@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline before != (70)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_before____70( Type documentType ) + { + const string selector = "$[?@.a\n!=@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2110,27 +2250,29 @@ public void Test_operators__newline_before____70() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab before != (71)" )] - public void Test_operators__tab_before____71() - { - var selector = "$[?@.a\t!=@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab before != (71)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_before____71( Type documentType ) + { + const string selector = "$[?@.a\t!=@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2142,27 +2284,29 @@ public void Test_operators__tab_before____71() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return before != (72)" )] - public void Test_operators__return_before____72() - { - var selector = "$[?@.a\r!=@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return before != (72)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_before____72( Type documentType ) + { + const string selector = "$[?@.a\r!=@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2174,27 +2318,29 @@ public void Test_operators__return_before____72() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space after != (73)" )] - public void Test_operators__space_after____73() - { - var selector = "$[?@.a!= @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space after != (73)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_after____73( Type documentType ) + { + const string selector = "$[?@.a!= @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2206,27 +2352,29 @@ public void Test_operators__space_after____73() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline after != (74)" )] - public void Test_operators__newline_after____74() - { - var selector = "$[?@.a!=\n@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline after != (74)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_after____74( Type documentType ) + { + const string selector = "$[?@.a!=\n@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2238,27 +2386,29 @@ public void Test_operators__newline_after____74() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab after != (75)" )] - public void Test_operators__tab_after____75() - { - var selector = "$[?@.a!=\t@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab after != (75)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_after____75( Type documentType ) + { + const string selector = "$[?@.a!=\t@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2270,27 +2420,29 @@ public void Test_operators__tab_after____75() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return after != (76)" )] - public void Test_operators__return_after____76() - { - var selector = "$[?@.a!=\r@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return after != (76)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_after____76( Type documentType ) + { + const string selector = "$[?@.a!=\r@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2302,27 +2454,29 @@ public void Test_operators__return_after____76() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space before < (77)" )] - public void Test_operators__space_before___77() - { - var selector = "$[?@.a <@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space before < (77)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_before___77( Type documentType ) + { + const string selector = "$[?@.a <@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2334,27 +2488,29 @@ public void Test_operators__space_before___77() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline before < (78)" )] - public void Test_operators__newline_before___78() - { - var selector = "$[?@.a\n<@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline before < (78)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_before___78( Type documentType ) + { + const string selector = "$[?@.a\n<@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2366,27 +2522,29 @@ public void Test_operators__newline_before___78() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab before < (79)" )] - public void Test_operators__tab_before___79() - { - var selector = "$[?@.a\t<@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab before < (79)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_before___79( Type documentType ) + { + const string selector = "$[?@.a\t<@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2398,27 +2556,29 @@ public void Test_operators__tab_before___79() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return before < (80)" )] - public void Test_operators__return_before___80() - { - var selector = "$[?@.a\r<@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return before < (80)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_before___80( Type documentType ) + { + const string selector = "$[?@.a\r<@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2430,27 +2590,29 @@ public void Test_operators__return_before___80() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space after < (81)" )] - public void Test_operators__space_after___81() - { - var selector = "$[?@.a< @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space after < (81)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_after___81( Type documentType ) + { + const string selector = "$[?@.a< @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2462,27 +2624,29 @@ public void Test_operators__space_after___81() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline after < (82)" )] - public void Test_operators__newline_after___82() - { - var selector = "$[?@.a<\n@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline after < (82)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_after___82( Type documentType ) + { + const string selector = "$[?@.a<\n@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2494,27 +2658,29 @@ public void Test_operators__newline_after___82() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab after < (83)" )] - public void Test_operators__tab_after___83() - { - var selector = "$[?@.a<\t@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab after < (83)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_after___83( Type documentType ) + { + const string selector = "$[?@.a<\t@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2526,27 +2692,29 @@ public void Test_operators__tab_after___83() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return after < (84)" )] - public void Test_operators__return_after___84() - { - var selector = "$[?@.a<\r@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return after < (84)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_after___84( Type documentType ) + { + const string selector = "$[?@.a<\r@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2558,27 +2726,29 @@ public void Test_operators__return_after___84() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space before > (85)" )] - public void Test_operators__space_before___85() - { - var selector = "$[?@.b >@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space before > (85)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_before___85( Type documentType ) + { + const string selector = "$[?@.b >@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2590,27 +2760,29 @@ public void Test_operators__space_before___85() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline before > (86)" )] - public void Test_operators__newline_before___86() - { - var selector = "$[?@.b\n>@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline before > (86)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_before___86( Type documentType ) + { + const string selector = "$[?@.b\n>@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2622,27 +2794,29 @@ public void Test_operators__newline_before___86() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab before > (87)" )] - public void Test_operators__tab_before___87() - { - var selector = "$[?@.b\t>@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab before > (87)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_before___87( Type documentType ) + { + const string selector = "$[?@.b\t>@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2654,27 +2828,29 @@ public void Test_operators__tab_before___87() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return before > (88)" )] - public void Test_operators__return_before___88() - { - var selector = "$[?@.b\r>@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return before > (88)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_before___88( Type documentType ) + { + const string selector = "$[?@.b\r>@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2686,27 +2862,29 @@ public void Test_operators__return_before___88() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space after > (89)" )] - public void Test_operators__space_after___89() - { - var selector = "$[?@.b> @.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space after > (89)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_after___89( Type documentType ) + { + const string selector = "$[?@.b> @.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2718,27 +2896,29 @@ public void Test_operators__space_after___89() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline after > (90)" )] - public void Test_operators__newline_after___90() - { - var selector = "$[?@.b>\n@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline after > (90)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_after___90( Type documentType ) + { + const string selector = "$[?@.b>\n@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2750,27 +2930,29 @@ public void Test_operators__newline_after___90() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab after > (91)" )] - public void Test_operators__tab_after___91() - { - var selector = "$[?@.b>\t@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab after > (91)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_after___91( Type documentType ) + { + const string selector = "$[?@.b>\t@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2782,27 +2964,29 @@ public void Test_operators__tab_after___91() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return after > (92)" )] - public void Test_operators__return_after___92() - { - var selector = "$[?@.b>\r@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return after > (92)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_after___92( Type documentType ) + { + const string selector = "$[?@.b>\r@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2814,27 +2998,29 @@ public void Test_operators__return_after___92() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space before <= (93)" )] - public void Test_operators__space_before____93() - { - var selector = "$[?@.a <=@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space before <= (93)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_before____93( Type documentType ) + { + const string selector = "$[?@.a <=@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2850,9 +3036,9 @@ public void Test_operators__space_before____93() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2863,18 +3049,20 @@ public void Test_operators__space_before____93() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline before <= (94)" )] - public void Test_operators__newline_before____94() - { - var selector = "$[?@.a\n<=@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline before <= (94)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_before____94( Type documentType ) + { + const string selector = "$[?@.a\n<=@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2890,9 +3078,9 @@ public void Test_operators__newline_before____94() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2903,18 +3091,20 @@ public void Test_operators__newline_before____94() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab before <= (95)" )] - public void Test_operators__tab_before____95() - { - var selector = "$[?@.a\t<=@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab before <= (95)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_before____95( Type documentType ) + { + const string selector = "$[?@.a\t<=@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2930,9 +3120,9 @@ public void Test_operators__tab_before____95() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2943,18 +3133,20 @@ public void Test_operators__tab_before____95() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return before <= (96)" )] - public void Test_operators__return_before____96() - { - var selector = "$[?@.a\r<=@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return before <= (96)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_before____96( Type documentType ) + { + const string selector = "$[?@.a\r<=@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2970,9 +3162,9 @@ public void Test_operators__return_before____96() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -2983,18 +3175,20 @@ public void Test_operators__return_before____96() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space after <= (97)" )] - public void Test_operators__space_after____97() - { - var selector = "$[?@.a<= @.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space after <= (97)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_after____97( Type documentType ) + { + const string selector = "$[?@.a<= @.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3010,9 +3204,9 @@ public void Test_operators__space_after____97() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3023,18 +3217,20 @@ public void Test_operators__space_after____97() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline after <= (98)" )] - public void Test_operators__newline_after____98() - { - var selector = "$[?@.a<=\n@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline after <= (98)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_after____98( Type documentType ) + { + const string selector = "$[?@.a<=\n@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3050,9 +3246,9 @@ public void Test_operators__newline_after____98() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3063,18 +3259,20 @@ public void Test_operators__newline_after____98() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab after <= (99)" )] - public void Test_operators__tab_after____99() - { - var selector = "$[?@.a<=\t@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab after <= (99)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_after____99( Type documentType ) + { + const string selector = "$[?@.a<=\t@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3090,9 +3288,9 @@ public void Test_operators__tab_after____99() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3103,18 +3301,20 @@ public void Test_operators__tab_after____99() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return after <= (100)" )] - public void Test_operators__return_after____100() - { - var selector = "$[?@.a<=\r@.b]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return after <= (100)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_after____100( Type documentType ) + { + const string selector = "$[?@.a<=\r@.b]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3130,9 +3330,9 @@ public void Test_operators__return_after____100() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3143,18 +3343,20 @@ public void Test_operators__return_after____100() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space before >= (101)" )] - public void Test_operators__space_before____101() - { - var selector = "$[?@.b >=@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space before >= (101)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_before____101( Type documentType ) + { + const string selector = "$[?@.b >=@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3170,9 +3372,9 @@ public void Test_operators__space_before____101() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3183,18 +3385,20 @@ public void Test_operators__space_before____101() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline before >= (102)" )] - public void Test_operators__newline_before____102() - { - var selector = "$[?@.b\n>=@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline before >= (102)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_before____102( Type documentType ) + { + const string selector = "$[?@.b\n>=@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3210,9 +3414,9 @@ public void Test_operators__newline_before____102() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3223,18 +3427,20 @@ public void Test_operators__newline_before____102() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab before >= (103)" )] - public void Test_operators__tab_before____103() - { - var selector = "$[?@.b\t>=@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab before >= (103)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_before____103( Type documentType ) + { + const string selector = "$[?@.b\t>=@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3250,9 +3456,9 @@ public void Test_operators__tab_before____103() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3263,18 +3469,20 @@ public void Test_operators__tab_before____103() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return before >= (104)" )] - public void Test_operators__return_before____104() - { - var selector = "$[?@.b\r>=@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return before >= (104)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_before____104( Type documentType ) + { + const string selector = "$[?@.b\r>=@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3290,9 +3498,9 @@ public void Test_operators__return_before____104() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3303,18 +3511,20 @@ public void Test_operators__return_before____104() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space after >= (105)" )] - public void Test_operators__space_after____105() - { - var selector = "$[?@.b>= @.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space after >= (105)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_after____105( Type documentType ) + { + const string selector = "$[?@.b>= @.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3330,9 +3540,9 @@ public void Test_operators__space_after____105() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3343,18 +3553,20 @@ public void Test_operators__space_after____105() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline after >= (106)" )] - public void Test_operators__newline_after____106() - { - var selector = "$[?@.b>=\n@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline after >= (106)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_after____106( Type documentType ) + { + const string selector = "$[?@.b>=\n@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3370,9 +3582,9 @@ public void Test_operators__newline_after____106() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3383,18 +3595,20 @@ public void Test_operators__newline_after____106() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab after >= (107)" )] - public void Test_operators__tab_after____107() - { - var selector = "$[?@.b>=\t@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab after >= (107)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_after____107( Type documentType ) + { + const string selector = "$[?@.b>=\t@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3410,9 +3624,9 @@ public void Test_operators__tab_after____107() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3423,18 +3637,20 @@ public void Test_operators__tab_after____107() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return after >= (108)" )] - public void Test_operators__return_after____108() - { - var selector = "$[?@.b>=\r@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return after >= (108)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_after____108( Type documentType ) + { + const string selector = "$[?@.b>=\r@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3450,9 +3666,9 @@ public void Test_operators__return_after____108() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": 1, @@ -3463,18 +3679,20 @@ public void Test_operators__return_after____108() "b": 2 } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space between logical not and test expression (109)" )] - public void Test_operators__space_between_logical_not_and_test_expression_109() - { - var selector = "$[?! @.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space between logical not and test expression (109)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_between_logical_not_and_test_expression_109( Type documentType ) + { + const string selector = "$[?! @.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3489,26 +3707,28 @@ public void Test_operators__space_between_logical_not_and_test_expression_109() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline between logical not and test expression (110)" )] - public void Test_operators__newline_between_logical_not_and_test_expression_110() - { - var selector = "$[?!\n@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline between logical not and test expression (110)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_between_logical_not_and_test_expression_110( Type documentType ) + { + const string selector = "$[?!\n@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3523,26 +3743,28 @@ public void Test_operators__newline_between_logical_not_and_test_expression_110( } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab between logical not and test expression (111)" )] - public void Test_operators__tab_between_logical_not_and_test_expression_111() - { - var selector = "$[?!\t@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab between logical not and test expression (111)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_between_logical_not_and_test_expression_111( Type documentType ) + { + const string selector = "$[?!\t@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3557,26 +3779,28 @@ public void Test_operators__tab_between_logical_not_and_test_expression_111() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return between logical not and test expression (112)" )] - public void Test_operators__return_between_logical_not_and_test_expression_112() - { - var selector = "$[?!\r@.a]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return between logical not and test expression (112)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_between_logical_not_and_test_expression_112( Type documentType ) + { + const string selector = "$[?!\r@.a]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3591,26 +3815,28 @@ public void Test_operators__return_between_logical_not_and_test_expression_112() } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, space between logical not and parenthesized expression (113)" )] - public void Test_operators__space_between_logical_not_and_parenthesized_expression_113() - { - var selector = "$[?! (@.a=='b')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, space between logical not and parenthesized expression (113)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__space_between_logical_not_and_parenthesized_expression_113( Type documentType ) + { + const string selector = "$[?! (@.a=='b')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3626,9 +3852,9 @@ public void Test_operators__space_between_logical_not_and_parenthesized_expressi } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3639,18 +3865,20 @@ public void Test_operators__space_between_logical_not_and_parenthesized_expressi "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, newline between logical not and parenthesized expression (114)" )] - public void Test_operators__newline_between_logical_not_and_parenthesized_expression_114() - { - var selector = "$[?!\n(@.a=='b')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, newline between logical not and parenthesized expression (114)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__newline_between_logical_not_and_parenthesized_expression_114( Type documentType ) + { + const string selector = "$[?!\n(@.a=='b')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3666,9 +3894,9 @@ public void Test_operators__newline_between_logical_not_and_parenthesized_expres } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3679,18 +3907,20 @@ public void Test_operators__newline_between_logical_not_and_parenthesized_expres "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, tab between logical not and parenthesized expression (115)" )] - public void Test_operators__tab_between_logical_not_and_parenthesized_expression_115() - { - var selector = "$[?!\t(@.a=='b')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, tab between logical not and parenthesized expression (115)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__tab_between_logical_not_and_parenthesized_expression_115( Type documentType ) + { + const string selector = "$[?!\t(@.a=='b')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3706,9 +3936,9 @@ public void Test_operators__tab_between_logical_not_and_parenthesized_expression } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3719,18 +3949,20 @@ public void Test_operators__tab_between_logical_not_and_parenthesized_expression "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"operators, return between logical not and parenthesized expression (116)" )] - public void Test_operators__return_between_logical_not_and_parenthesized_expression_116() - { - var selector = "$[?!\r(@.a=='b')]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"operators, return between logical not and parenthesized expression (116)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_operators__return_between_logical_not_and_parenthesized_expression_116( Type documentType ) + { + const string selector = "$[?!\r(@.a=='b')]"; + var document = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3746,9 +3978,9 @@ public void Test_operators__return_between_logical_not_and_parenthesized_express } ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ { "a": "a", @@ -3759,730 +3991,804 @@ public void Test_operators__return_between_logical_not_and_parenthesized_express "d": "f" } ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, space between root and bracket (117)" )] - public void Test_selectors__space_between_root_and_bracket_117() - { - var selector = "$ ['a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, space between root and bracket (117)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__space_between_root_and_bracket_117( Type documentType ) + { + const string selector = "$ ['a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, newline between root and bracket (118)" )] - public void Test_selectors__newline_between_root_and_bracket_118() - { - var selector = "$\n['a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, newline between root and bracket (118)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__newline_between_root_and_bracket_118( Type documentType ) + { + const string selector = "$\n['a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, tab between root and bracket (119)" )] - public void Test_selectors__tab_between_root_and_bracket_119() - { - var selector = "$\t['a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, tab between root and bracket (119)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__tab_between_root_and_bracket_119( Type documentType ) + { + const string selector = "$\t['a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, return between root and bracket (120)" )] - public void Test_selectors__return_between_root_and_bracket_120() - { - var selector = "$\r['a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, return between root and bracket (120)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__return_between_root_and_bracket_120( Type documentType ) + { + const string selector = "$\r['a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, space between bracket and bracket (121)" )] - public void Test_selectors__space_between_bracket_and_bracket_121() - { - var selector = "$['a'] ['b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, space between bracket and bracket (121)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__space_between_bracket_and_bracket_121( Type documentType ) + { + const string selector = "$['a'] ['b']"; + var document = TestHelper.Parse( documentType, + """ { "a": { "b": "ab" } } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, newline between root and bracket (122)" )] - public void Test_selectors__newline_between_root_and_bracket_122() - { - var selector = "$['a'] \n['b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, newline between root and bracket (122)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__newline_between_root_and_bracket_122( Type documentType ) + { + const string selector = "$['a'] \n['b']"; + var document = TestHelper.Parse( documentType, + """ { "a": { "b": "ab" } } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, tab between root and bracket (123)" )] - public void Test_selectors__tab_between_root_and_bracket_123() - { - var selector = "$['a'] \t['b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, tab between root and bracket (123)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__tab_between_root_and_bracket_123( Type documentType ) + { + const string selector = "$['a'] \t['b']"; + var document = TestHelper.Parse( documentType, + """ { "a": { "b": "ab" } } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, return between root and bracket (124)" )] - public void Test_selectors__return_between_root_and_bracket_124() - { - var selector = "$['a'] \r['b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, return between root and bracket (124)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__return_between_root_and_bracket_124( Type documentType ) + { + const string selector = "$['a'] \r['b']"; + var document = TestHelper.Parse( documentType, + """ { "a": { "b": "ab" } } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, space between root and dot (125)" )] - public void Test_selectors__space_between_root_and_dot_125() - { - var selector = "$ .a"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, space between root and dot (125)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__space_between_root_and_dot_125( Type documentType ) + { + const string selector = "$ .a"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, newline between root and dot (126)" )] - public void Test_selectors__newline_between_root_and_dot_126() - { - var selector = "$\n.a"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, newline between root and dot (126)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__newline_between_root_and_dot_126( Type documentType ) + { + const string selector = "$\n.a"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, tab between root and dot (127)" )] - public void Test_selectors__tab_between_root_and_dot_127() - { - var selector = "$\t.a"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, tab between root and dot (127)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__tab_between_root_and_dot_127( Type documentType ) + { + const string selector = "$\t.a"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, return between root and dot (128)" )] - public void Test_selectors__return_between_root_and_dot_128() - { - var selector = "$\r.a"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, return between root and dot (128)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__return_between_root_and_dot_128( Type documentType ) + { + const string selector = "$\r.a"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; + + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } - - [TestMethod( @"selectors, space between dot and name (129)" )] - public void Test_selectors__space_between_dot_and_name_129() - { - var selector = "$. a"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"selectors, newline between dot and name (130)" )] - public void Test_selectors__newline_between_dot_and_name_130() - { - var selector = "$.\na"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"selectors, tab between dot and name (131)" )] - public void Test_selectors__tab_between_dot_and_name_131() - { - var selector = "$.\ta"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"selectors, return between dot and name (132)" )] - public void Test_selectors__return_between_dot_and_name_132() - { - var selector = "$.\ra"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"selectors, space between recursive descent and name (133)" )] - public void Test_selectors__space_between_recursive_descent_and_name_133() - { - var selector = "$.. a"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"selectors, newline between recursive descent and name (134)" )] - public void Test_selectors__newline_between_recursive_descent_and_name_134() - { - var selector = "$..\na"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"selectors, tab between recursive descent and name (135)" )] - public void Test_selectors__tab_between_recursive_descent_and_name_135() - { - var selector = "$..\ta"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"selectors, return between recursive descent and name (136)" )] - public void Test_selectors__return_between_recursive_descent_and_name_136() - { - var selector = "$..\ra"; - var document = JsonNode.Parse( "[0]" ); // Empty node - - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - } - - [TestMethod( @"selectors, space between bracket and selector (137)" )] - public void Test_selectors__space_between_bracket_and_selector_137() - { - var selector = "$[ 'a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, space between dot and name (129)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__space_between_dot_and_name_129( Type documentType ) + { + const string selector = "$. a"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"selectors, newline between dot and name (130)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__newline_between_dot_and_name_130( Type documentType ) + { + const string selector = "$.\na"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"selectors, tab between dot and name (131)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__tab_between_dot_and_name_131( Type documentType ) + { + const string selector = "$.\ta"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"selectors, return between dot and name (132)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__return_between_dot_and_name_132( Type documentType ) + { + const string selector = "$.\ra"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"selectors, space between recursive descent and name (133)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__space_between_recursive_descent_and_name_133( Type documentType ) + { + const string selector = "$.. a"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"selectors, newline between recursive descent and name (134)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__newline_between_recursive_descent_and_name_134( Type documentType ) + { + const string selector = "$..\na"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"selectors, tab between recursive descent and name (135)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__tab_between_recursive_descent_and_name_135( Type documentType ) + { + const string selector = "$..\ta"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"selectors, return between recursive descent and name (136)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__return_between_recursive_descent_and_name_136( Type documentType ) + { + const string selector = "$..\ra"; + var document = TestHelper.Parse( documentType, "[0]" ); // Empty node + + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + } + + [DataTestMethod( @"selectors, space between bracket and selector (137)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__space_between_bracket_and_selector_137( Type documentType ) + { + const string selector = "$[ 'a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, newline between bracket and selector (138)" )] - public void Test_selectors__newline_between_bracket_and_selector_138() - { - var selector = "$[\n'a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, newline between bracket and selector (138)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__newline_between_bracket_and_selector_138( Type documentType ) + { + const string selector = "$[\n'a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, tab between bracket and selector (139)" )] - public void Test_selectors__tab_between_bracket_and_selector_139() - { - var selector = "$[\t'a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, tab between bracket and selector (139)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__tab_between_bracket_and_selector_139( Type documentType ) + { + const string selector = "$[\t'a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, return between bracket and selector (140)" )] - public void Test_selectors__return_between_bracket_and_selector_140() - { - var selector = "$[\r'a']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, return between bracket and selector (140)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__return_between_bracket_and_selector_140( Type documentType ) + { + const string selector = "$[\r'a']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, space between selector and bracket (141)" )] - public void Test_selectors__space_between_selector_and_bracket_141() - { - var selector = "$['a' ]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, space between selector and bracket (141)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__space_between_selector_and_bracket_141( Type documentType ) + { + const string selector = "$['a' ]"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, newline between selector and bracket (142)" )] - public void Test_selectors__newline_between_selector_and_bracket_142() - { - var selector = "$['a'\n]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, newline between selector and bracket (142)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__newline_between_selector_and_bracket_142( Type documentType ) + { + const string selector = "$['a'\n]"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, tab between selector and bracket (143)" )] - public void Test_selectors__tab_between_selector_and_bracket_143() - { - var selector = "$['a'\t]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, tab between selector and bracket (143)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__tab_between_selector_and_bracket_143( Type documentType ) + { + const string selector = "$['a'\t]"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, return between selector and bracket (144)" )] - public void Test_selectors__return_between_selector_and_bracket_144() - { - var selector = "$['a'\r]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, return between selector and bracket (144)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__return_between_selector_and_bracket_144( Type documentType ) + { + const string selector = "$['a'\r]"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, space between selector and comma (145)" )] - public void Test_selectors__space_between_selector_and_comma_145() - { - var selector = "$['a' ,'b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, space between selector and comma (145)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__space_between_selector_and_comma_145( Type documentType ) + { + const string selector = "$['a' ,'b']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab", "b": "bc" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab", "bc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, newline between selector and comma (146)" )] - public void Test_selectors__newline_between_selector_and_comma_146() - { - var selector = "$['a'\n,'b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, newline between selector and comma (146)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__newline_between_selector_and_comma_146( Type documentType ) + { + const string selector = "$['a'\n,'b']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab", "b": "bc" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab", "bc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, tab between selector and comma (147)" )] - public void Test_selectors__tab_between_selector_and_comma_147() - { - var selector = "$['a'\t,'b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, tab between selector and comma (147)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__tab_between_selector_and_comma_147( Type documentType ) + { + const string selector = "$['a'\t,'b']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab", "b": "bc" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab", "bc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, return between selector and comma (148)" )] - public void Test_selectors__return_between_selector_and_comma_148() - { - var selector = "$['a'\r,'b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, return between selector and comma (148)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__return_between_selector_and_comma_148( Type documentType ) + { + const string selector = "$['a'\r,'b']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab", "b": "bc" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab", "bc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, space between comma and selector (149)" )] - public void Test_selectors__space_between_comma_and_selector_149() - { - var selector = "$['a', 'b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, space between comma and selector (149)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__space_between_comma_and_selector_149( Type documentType ) + { + const string selector = "$['a', 'b']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab", "b": "bc" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab", "bc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, newline between comma and selector (150)" )] - public void Test_selectors__newline_between_comma_and_selector_150() - { - var selector = "$['a',\n'b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, newline between comma and selector (150)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__newline_between_comma_and_selector_150( Type documentType ) + { + const string selector = "$['a',\n'b']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab", "b": "bc" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab", "bc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, tab between comma and selector (151)" )] - public void Test_selectors__tab_between_comma_and_selector_151() - { - var selector = "$['a',\t'b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, tab between comma and selector (151)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__tab_between_comma_and_selector_151( Type documentType ) + { + const string selector = "$['a',\t'b']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab", "b": "bc" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab", "bc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"selectors, return between comma and selector (152)" )] - public void Test_selectors__return_between_comma_and_selector_152() - { - var selector = "$['a',\r'b']"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"selectors, return between comma and selector (152)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_selectors__return_between_comma_and_selector_152( Type documentType ) + { + const string selector = "$['a',\r'b']"; + var document = TestHelper.Parse( documentType, + """ { "a": "ab", "b": "bc" } """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ "ab", "bc" ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, space between start and colon (153)" )] - public void Test_slice__space_between_start_and_colon_153() - { - var selector = "$[1 :5:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, space between start and colon (153)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__space_between_start_and_colon_153( Type documentType ) + { + const string selector = "$[1 :5:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4492,25 +4798,27 @@ public void Test_slice__space_between_start_and_colon_153() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, newline between start and colon (154)" )] - public void Test_slice__newline_between_start_and_colon_154() - { - var selector = "$[1\n:5:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, newline between start and colon (154)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__newline_between_start_and_colon_154( Type documentType ) + { + const string selector = "$[1\n:5:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4520,25 +4828,27 @@ public void Test_slice__newline_between_start_and_colon_154() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, tab between start and colon (155)" )] - public void Test_slice__tab_between_start_and_colon_155() - { - var selector = "$[1\t:5:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, tab between start and colon (155)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__tab_between_start_and_colon_155( Type documentType ) + { + const string selector = "$[1\t:5:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4548,25 +4858,27 @@ public void Test_slice__tab_between_start_and_colon_155() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, return between start and colon (156)" )] - public void Test_slice__return_between_start_and_colon_156() - { - var selector = "$[1\r:5:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, return between start and colon (156)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__return_between_start_and_colon_156( Type documentType ) + { + const string selector = "$[1\r:5:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4576,25 +4888,27 @@ public void Test_slice__return_between_start_and_colon_156() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, space between colon and end (157)" )] - public void Test_slice__space_between_colon_and_end_157() - { - var selector = "$[1: 5:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, space between colon and end (157)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__space_between_colon_and_end_157( Type documentType ) + { + const string selector = "$[1: 5:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4604,25 +4918,27 @@ public void Test_slice__space_between_colon_and_end_157() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, newline between colon and end (158)" )] - public void Test_slice__newline_between_colon_and_end_158() - { - var selector = "$[1:\n5:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, newline between colon and end (158)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__newline_between_colon_and_end_158( Type documentType ) + { + const string selector = "$[1:\n5:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4632,25 +4948,27 @@ public void Test_slice__newline_between_colon_and_end_158() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, tab between colon and end (159)" )] - public void Test_slice__tab_between_colon_and_end_159() - { - var selector = "$[1:\t5:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, tab between colon and end (159)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__tab_between_colon_and_end_159( Type documentType ) + { + const string selector = "$[1:\t5:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4660,25 +4978,27 @@ public void Test_slice__tab_between_colon_and_end_159() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, return between colon and end (160)" )] - public void Test_slice__return_between_colon_and_end_160() - { - var selector = "$[1:\r5:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, return between colon and end (160)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__return_between_colon_and_end_160( Type documentType ) + { + const string selector = "$[1:\r5:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4688,25 +5008,27 @@ public void Test_slice__return_between_colon_and_end_160() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, space between end and colon (161)" )] - public void Test_slice__space_between_end_and_colon_161() - { - var selector = "$[1:5 :2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, space between end and colon (161)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__space_between_end_and_colon_161( Type documentType ) + { + const string selector = "$[1:5 :2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4716,25 +5038,27 @@ public void Test_slice__space_between_end_and_colon_161() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, newline between end and colon (162)" )] - public void Test_slice__newline_between_end_and_colon_162() - { - var selector = "$[1:5\n:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, newline between end and colon (162)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__newline_between_end_and_colon_162( Type documentType ) + { + const string selector = "$[1:5\n:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4744,25 +5068,27 @@ public void Test_slice__newline_between_end_and_colon_162() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, tab between end and colon (163)" )] - public void Test_slice__tab_between_end_and_colon_163() - { - var selector = "$[1:5\t:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, tab between end and colon (163)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__tab_between_end_and_colon_163( Type documentType ) + { + const string selector = "$[1:5\t:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4772,25 +5098,27 @@ public void Test_slice__tab_between_end_and_colon_163() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, return between end and colon (164)" )] - public void Test_slice__return_between_end_and_colon_164() - { - var selector = "$[1:5\r:2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, return between end and colon (164)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__return_between_end_and_colon_164( Type documentType ) + { + const string selector = "$[1:5\r:2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4800,25 +5128,27 @@ public void Test_slice__return_between_end_and_colon_164() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, space between colon and step (165)" )] - public void Test_slice__space_between_colon_and_step_165() - { - var selector = "$[1:5: 2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, space between colon and step (165)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__space_between_colon_and_step_165( Type documentType ) + { + const string selector = "$[1:5: 2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4828,25 +5158,27 @@ public void Test_slice__space_between_colon_and_step_165() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, newline between colon and step (166)" )] - public void Test_slice__newline_between_colon_and_step_166() - { - var selector = "$[1:5:\n2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, newline between colon and step (166)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__newline_between_colon_and_step_166( Type documentType ) + { + const string selector = "$[1:5:\n2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4856,25 +5188,27 @@ public void Test_slice__newline_between_colon_and_step_166() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, tab between colon and step (167)" )] - public void Test_slice__tab_between_colon_and_step_167() - { - var selector = "$[1:5:\t2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, tab between colon and step (167)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__tab_between_colon_and_step_167( Type documentType ) + { + const string selector = "$[1:5:\t2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4884,25 +5218,27 @@ public void Test_slice__tab_between_colon_and_step_167() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); + } - [TestMethod( @"slice, return between colon and step (168)" )] - public void Test_slice__return_between_colon_and_step_168() - { - var selector = "$[1:5:\r2]"; - var document = JsonNode.Parse( - """ + [DataTestMethod( @"slice, return between colon and step (168)" )] + [DataRow( typeof( JsonNode ) )] + [DataRow( typeof( JsonElement ) )] + public void Test_slice__return_between_colon_and_step_168( Type documentType ) + { + const string selector = "$[1:5:\r2]"; + var document = TestHelper.Parse( documentType, + """ [ 1, 2, @@ -4912,18 +5248,18 @@ public void Test_slice__return_between_colon_and_step_168() 6 ] """ ); - var results = document.Select( selector ); - var expect = JsonNode.Parse( - """ + var results = document.Select( selector ); + var expect = TestHelper.Parse( documentType, + """ [ 2, 4 ] - """ ); + """ ).Root; - var match = TestHelper.MatchOne( results, expect! ); - Assert.IsTrue( match ); - } + var match = TestHelper.MatchOne( documentType, results, expect ); + Assert.IsTrue( match ); } } + diff --git a/test/Hyperbee.Json.Cts/generate_tests.ps1 b/test/Hyperbee.Json.Cts/generate_tests.ps1 index f98bd5ed..edbe38da 100644 --- a/test/Hyperbee.Json.Cts/generate_tests.ps1 +++ b/test/Hyperbee.Json.Cts/generate_tests.ps1 @@ -174,14 +174,15 @@ function Get-UnitTestContent { $unitTestContent = @" // This file was auto generated. +using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; +using Hyperbee.Json.Cts.TestSupport; -namespace Hyperbee.Json.Cts.Tests +namespace Hyperbee.Json.Cts.Tests; + +[TestClass] +public class $className { - [TestClass] - public class $className - {`r`n "@ $testNumber = 0 @@ -211,47 +212,49 @@ namespace Hyperbee.Json.Cts.Tests # Replace placeholders in the template with actual test case data $unitTestContent += @" - [TestMethod( @`"$name ($testNumber)`" )] - public void Test`_$methodName`_$testNumber() - { - var selector = `"$selector`";`r`n + [DataTestMethod( @`"$name ($testNumber)`" )] + [DataRow( typeof(JsonNode) )] + [DataRow(typeof(JsonElement))] + public void Test`_$methodName`_$testNumber( Type documentType ) + { + const string selector = `"$selector`";`r`n "@ if ($invalidSelector) { $unitTestContent += @" - var document = JsonNode.Parse( `"[0]`" ); // Empty node + var document = TestHelper.Parse( documentType, `"[0]`" ); // Empty node - AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); - }`r`n + AssertExtensions.ThrowsAny( () => { _ = document.Select( selector ).ToArray(); } ); + }`r`n "@ } else { $unitTestContent += @" - var document = JsonNode.Parse( - `"`"`"$document`"`"`"); - var results = document.Select(selector);`r`n + var document = TestHelper.Parse( documentType, + `"`"`"$document`"`"`"); + var results = document.Select(selector);`r`n "@ if ($null -ne $result) { $unitTestContent += @" - var expect = JsonNode.Parse( - `"`"`"$result`"`"`"); + var expect = TestHelper.Parse( documentType, + `"`"`"$result`"`"`").Root; - var match = TestHelper.MatchOne(results, expect!); - Assert.IsTrue(match); - }`r`n + var match = TestHelper.MatchOne(documentType, results, expect); + Assert.IsTrue(match); + }`r`n "@ } elseif ($null -ne $results) { $unitTestContent += @" - var expectOneOf = JsonNode.Parse( - `"`"`"$results`"`"`"); + var expectOneOf = TestHelper.Parse( documentType, + `"`"`"$results`"`"`").Root; - var match = TestHelper.MatchAny(results, expectOneOf!); - Assert.IsTrue(match); - }`r`n + var match = TestHelper.MatchAny(documentType, results, expectOneOf); + Assert.IsTrue(match); + }`r`n "@ } else { $unitTestContent += @" - Assert.Fail(`"missing results`"); - }`r`n + Assert.Fail(`"missing results`"); + }`r`n "@ } } @@ -259,8 +262,8 @@ namespace Hyperbee.Json.Cts.Tests # Close the class and namespace $unitTestContent += @" - } -}`r`n +} +`r`n "@ return $unitTestContent diff --git a/test/Hyperbee.Json.Tests/Hyperbee.Json.Tests.csproj b/test/Hyperbee.Json.Tests/Hyperbee.Json.Tests.csproj index 98dc3fa6..8e5a9b87 100644 --- a/test/Hyperbee.Json.Tests/Hyperbee.Json.Tests.csproj +++ b/test/Hyperbee.Json.Tests/Hyperbee.Json.Tests.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathArrayTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathArrayTests.cs index 7e87894b..de49693d 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathArrayTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathArrayTests.cs @@ -26,7 +26,7 @@ public void ArraySlice( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); var expected = new[] @@ -54,7 +54,7 @@ public void ArraySliceOnExactMatch( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -85,7 +85,7 @@ public void ArraySliceOnNonOverlappingArray( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -110,7 +110,7 @@ public void ArraySliceOnObject( string query, Type sourceType ) "1:3": "nice" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -132,7 +132,7 @@ public void ArraySliceOnPartiallyOverlappingArray( string query, Type sourceType "third" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -160,7 +160,7 @@ public void ArraySliceWithLargeNumberForEnd( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -190,7 +190,7 @@ public void ArraySliceWithLargeNumberForEndAndNegativeStep( string query, Type s "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -219,7 +219,7 @@ public void ArraySliceWithLargeNumberForStart( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -248,7 +248,7 @@ public void ArraySliceWithLargeNumberForStartAndNegativeStep( string query, Type "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -277,7 +277,7 @@ public void ArraySliceWithNegativeStartAndEndAndRangeOfNegative1( string query, "nice" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -302,7 +302,7 @@ public void ArraySliceWithNegativeStartAndEndAndRangeOf0( string query, Type sou "nice" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -327,7 +327,7 @@ public void ArraySliceWithNegativeStartAndEndAndRangeOf1( string query, Type sou "nice" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -357,7 +357,7 @@ public void ArraySliceWithNegativeStartAndPositiveEndAndRangeOfNegative1( string "nice" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -382,7 +382,7 @@ public void ArraySliceWithNegativeStartAndPositiveEndAndRangeOf0( string query, "nice" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -407,7 +407,7 @@ public void ArraySliceWithNegativeStartAndPositiveEndAndRangeOf1( string query, "nice" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -435,7 +435,7 @@ public void ArraySliceWithNegativeStep( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -464,7 +464,7 @@ public void ArraySliceWithNegativeStepAndStartGreaterThanEnd( string query, Type "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -489,7 +489,7 @@ public void ArraySliceWithNegativeStepOnPartiallyOverlappingArray( string query, "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -517,7 +517,7 @@ public void ArraySliceWithNegativeStepOnly( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -546,7 +546,7 @@ public void ArraySliceWithOpenEnd( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -577,7 +577,7 @@ public void ArraySliceWithOpenEndAndNegativeStep( string query, Type sourceType "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -607,7 +607,7 @@ public void ArraySliceWithOpenStart( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -632,7 +632,7 @@ public void ArraySliceWithOpenStartAndEndAndStepEmpty( string query, Type source "second" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -657,7 +657,7 @@ public void ArraySliceWithOpenStartAndEndOnObject( string query, Type sourceType "more": "string" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -682,7 +682,7 @@ public void ArraySliceWithOpenStartAndNegativeStep( string query, Type sourceTyp "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -711,7 +711,7 @@ public void ArraySliceWithPositiveStartAndNegativeEndAndRangeOfNegative1( string "nice" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -736,7 +736,7 @@ public void ArraySliceWithPositiveStartAndNegativeEndAndRangeOf0( string query, "nice" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -761,7 +761,7 @@ public void ArraySliceWithPositiveStartAndNegativeEndAndRangeOf1( string query, "nice" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -787,7 +787,7 @@ public void ArraySliceWithRangeOfNegative1( string query, Type sourceType ) "forth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -808,7 +808,7 @@ public void ArraySliceWithRangeOf0( string query, Type sourceType ) "second" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -829,7 +829,7 @@ public void ArraySliceWithRangeOf1( string query, Type sourceType ) "second" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -854,7 +854,7 @@ public void ArraySliceWithStartNegative1AndOpenEnd( string query, Type sourceTyp "third" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -879,7 +879,7 @@ public void ArraySliceWithStartMinus2AndOpenEnd( string query, Type sourceType ) "third" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -905,7 +905,7 @@ public void ArraySliceWithStartLargeNegativeNumberAndOpenEndOnShortArray( string "third" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -934,7 +934,7 @@ public void ArraySliceWithStep( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -963,7 +963,7 @@ public void ArraySliceWithStep0( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -987,7 +987,7 @@ public void ArraySliceWithStep1( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -1016,7 +1016,7 @@ public void ArraySliceWithStepButEndNotAligned( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -1044,7 +1044,7 @@ public void ArraySliceWithStepEmpty( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathBookstoreTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathBookstoreTests.cs index 5c778a30..d12dfbdb 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathBookstoreTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathBookstoreTests.cs @@ -15,7 +15,7 @@ public class JsonPathBookstoreTests : JsonTestBase [DataRow( "$", typeof( JsonNode ) )] public void TheRootOfEverything( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ); var expected = new[] { @@ -30,7 +30,7 @@ public void TheRootOfEverything( string query, Type sourceType ) [DataRow( "$.store.book[*].author", typeof( JsonNode ) )] public void TheAuthorsOfAllBooksInTheStore( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ); var expected = new[] { @@ -48,7 +48,7 @@ public void TheAuthorsOfAllBooksInTheStore( string query, Type sourceType ) [DataRow( "$..author", typeof( JsonNode ) )] public void AllAuthors( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ).ToList(); var expected = new[] { @@ -66,7 +66,7 @@ public void AllAuthors( string query, Type sourceType ) [DataRow( "$.store.*", typeof( JsonNode ) )] public void AllThingsInStoreWhichAreSomeBooksAndOneRedBicycle( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ); var expected = new[] { @@ -82,7 +82,7 @@ public void AllThingsInStoreWhichAreSomeBooksAndOneRedBicycle( string query, Typ [DataRow( "$.store..price", typeof( JsonNode ) )] public void ThePriceOfEverythingInTheStore( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ); var expected = new[] { @@ -101,7 +101,7 @@ public void ThePriceOfEverythingInTheStore( string query, Type sourceType ) [DataRow( "$..book[2]", typeof( JsonNode ) )] public void TheThirdBook( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var match = source.Select( query ).ToList(); var expected = source.FromJsonPathPointer( "$.store.book[2]" ); @@ -114,7 +114,7 @@ public void TheThirdBook( string query, Type sourceType ) [DataRow( "$..book[-1:]", typeof( JsonNode ) )] public void TheLastBookInOrder( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var match = source.Select( query ).Single(); var expected = source.FromJsonPathPointer( "$.store.book[3]" ); @@ -130,7 +130,7 @@ public void TheLastBookInOrder( string query, Type sourceType ) [DataRow( "$.store.book[0,1]", typeof( JsonNode ) )] public void TheFirstTwoBooks( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ); var expected = new[] { @@ -146,7 +146,7 @@ public void TheFirstTwoBooks( string query, Type sourceType ) [DataRow( "$..book['category','author']", typeof( JsonNode ) )] public void TheCategoriesAndAuthorsOfAllBooks( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ); var expected = new[] { @@ -170,7 +170,7 @@ public void TheCategoriesAndAuthorsOfAllBooks( string query, Type sourceType ) [DataRow( "$..book[?(@.isbn)]", typeof( JsonNode ) )] public void FilterAllBooksWithIsbnNumber( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ); var expected = new[] { @@ -188,7 +188,7 @@ public void FilterAllBooksWithIsbnNumber( string query, Type sourceType ) [DataRow( "$..book[?@.price<10]", typeof( JsonNode ) )] public void FilterAllBooksCheaperThan10( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ); var expected = new[] { @@ -204,7 +204,7 @@ public void FilterAllBooksCheaperThan10( string query, Type sourceType ) [DataRow( "$..*", typeof( JsonNode ) )] public void AllMembersOfJsonStructure( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var matches = source.Select( query ); var expected = new[] { @@ -247,7 +247,7 @@ public void AllMembersOfJsonStructure( string query, Type sourceType ) [DataRow( @"$..book[?@.price == 8.99 && @.category == ""fiction""]", typeof( JsonNode ) )] public void FilterAllBooksUsingLogicalAndInScript( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var match = source.Select( query ).Single(); var expected = source.FromJsonPathPointer( "$.store.book[2]" ); @@ -260,7 +260,7 @@ public void FilterAllBooksUsingLogicalAndInScript( string query, Type sourceType [DataRow( @"$..book[?@.price == 8.99 && (@.category == ""fiction"")]", typeof( JsonNode ) )] public void FilterWithUnevenParentheses( string query, Type sourceType ) { - var source = GetDocumentFromResource( sourceType ); + var source = GetDocumentAdapter( sourceType ); var match = source.Select( query ).Single(); var expected = source.FromJsonPathPointer( "$.store.book[2]" ); diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathBracketNotationTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathBracketNotationTests.cs index eee78768..25ecd658 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathBracketNotationTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathBracketNotationTests.cs @@ -22,7 +22,7 @@ public void BracketNotation( string query, Type sourceType ) "key": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -65,7 +65,7 @@ public void BracketNotationAfterRecursiveDescent( string query, Type sourceType } ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -92,7 +92,7 @@ public void BracketNotationOnObjectWithoutKey( string query, Type sourceType ) "key": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -112,7 +112,7 @@ public void BracketNotationWithNFCPathOnNFDKey( string query, Type sourceType ) "u\u0308": 42 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -140,11 +140,11 @@ public void BracketNotationWithDot( string query, Type sourceType ) "two.some": "42" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "42" ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "42" ); } [DataTestMethod] @@ -159,11 +159,11 @@ public void BracketNotationWithDoubleQuotes( string query, Type sourceType ) "key": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "value" ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "value" ); } [DataTestMethod] @@ -180,7 +180,7 @@ public void BracketNotationWithEmptyPath( string query, Type sourceType ) "\"\"": 222 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); Assert.ThrowsException( () => { @@ -202,11 +202,11 @@ public void BracketNotationWithEmptyString( string query, Type sourceType ) "\"\"": 222 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetInt32( matches[0] ) == 42 ); + Assert.IsTrue( TestHelper.GetInt32( matches[0] ) == 42 ); } [DataTestMethod] @@ -223,11 +223,11 @@ public void BracketNotationWithEmptyStringDoubleQuoted( string query, Type sourc "\"\"": 222 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetInt32( matches[0] ) == 42 ); + Assert.IsTrue( TestHelper.GetInt32( matches[0] ) == 42 ); } [DataTestMethod] @@ -242,7 +242,7 @@ public void BracketNotationWithNegativeNumberOnShortArray( string query, Type so "one element" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); @@ -265,12 +265,12 @@ public void BracketNotationWithNumber( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "third" ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "third" ); } [DataTestMethod] @@ -287,12 +287,12 @@ public void BracketNotationWithNumberNegative1( string query, Type sourceType ) "third" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "third" ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "third" ); } [DataTestMethod] @@ -303,7 +303,7 @@ public void BracketNotationWithNumberNegative1OnEmptyArray( string query, Type s // consensus: [] const string json = "[]"; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -327,12 +327,12 @@ public void BracketNotationWithNumber0( string query, Type sourceType ) "fifth" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "first" ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "first" ); } [DataTestMethod] @@ -348,7 +348,7 @@ public void BracketNotationWithNumberAfterDotNotationWithWildcardOnNestedArraysW [2, 3] ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -371,7 +371,7 @@ public void BracketNotationWithNumberOnObject( string query, Type sourceType ) "0": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -391,7 +391,7 @@ public void BracketNotationWithNumberOnShortArray( string query, Type sourceType "one element" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -412,12 +412,12 @@ public void BracketNotationWithQuotedArraySliceLiteral( string query, Type sourc "another": "entry" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "value" ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "value" ); } [DataTestMethod] @@ -432,12 +432,12 @@ public void BracketNotationWithQuotedClosingBracketLiteral( string query, Type s "]": 42 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetInt32( matches[0] ) == 42 ); + Assert.IsTrue( TestHelper.GetInt32( matches[0] ) == 42 ); } [DataTestMethod] @@ -453,12 +453,12 @@ public void BracketNotationWithQuotedCurrentObjectLiteral( string query, Type so "another": "entry" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "value" ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "value" ); } [DataTestMethod] @@ -474,7 +474,7 @@ public void BracketNotationWithQuotedDotLiteral( string query, Type sourceType ) "another": "entry" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -499,7 +499,7 @@ public void BracketNotationWithQuotedDotWildcard( string query, Type sourceType "": 10 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -523,7 +523,7 @@ public void BracketNotationWithQuotedEscapedBackslash( string query, Type source "\\": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -547,12 +547,12 @@ public void BracketNotationWithQuotedEscapedSingleQuote( string query, Type sour "'": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "value" ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "value" ); } [DataTestMethod] @@ -567,7 +567,7 @@ public void BracketNotationWithQuotedNumberOnObject( string query, Type sourceTy "0": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -591,7 +591,7 @@ public void BracketNotationWithQuotedRootLiteral( string query, Type sourceType "another": "entry" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -615,12 +615,12 @@ public void BracketNotationWithQuotedSpecialCharactersCombined( string query, Ty ":@.\"$,*'\\": 42 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); Assert.IsTrue( matches.Count == 1 ); - Assert.IsTrue( JsonValueHelper.GetInt32( matches[0] ) == 42 ); + Assert.IsTrue( TestHelper.GetInt32( matches[0] ) == 42 ); } [DataTestMethod] @@ -635,7 +635,7 @@ public void BracketNotationWithQuotedStringAndUnescapedSingleQuote( string query "single'quote": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); Assert.ThrowsException( () => { @@ -656,7 +656,7 @@ public void BracketNotationWithQuotedUnionLiteral( string query, Type sourceType "another": "entry" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -680,7 +680,7 @@ public void BracketNotationWithQuotedWildcardLiteral( string query, Type sourceT "another": "entry" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -703,7 +703,7 @@ public void BracketNotationWithQuotedWildcardLiteralOnObjectWithoutKey( string q "another": "entry" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -731,7 +731,7 @@ public void BracketNotationWithSpaces( string query, Type sourceType ) "\"a\"": 9 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -756,7 +756,7 @@ public void BracketNotationWithStringIncludingDotWildcard( string query, Type so "mice": 100 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -787,7 +787,7 @@ public void BracketNotationWithTwoLiteralsSeparatedByDot( string query, Type sou "two'.'some": "43" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); Assert.ThrowsException( () => { @@ -814,7 +814,7 @@ public void BracketNotationWithTwoLiteralsSeparatedByDotWithoutQuotes( string qu "two.some": "42" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); Assert.ThrowsException( () => { @@ -836,7 +836,7 @@ public void BracketNotationWithWildcardAfterArraySlice( string query, Type sourc [0, 0] ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); var expected = new[] @@ -848,10 +848,10 @@ public void BracketNotationWithWildcardAfterArraySlice( string query, Type sourc }; Assert.IsTrue( expected.SequenceEqual( matches ) ); - Assert.IsTrue( JsonValueHelper.GetInt32( matches[0] ) == 1 ); - Assert.IsTrue( JsonValueHelper.GetInt32( matches[1] ) == 2 ); - Assert.IsTrue( JsonValueHelper.GetString( matches[2] ) == "a" ); - Assert.IsTrue( JsonValueHelper.GetString( matches[3] ) == "b" ); + Assert.IsTrue( TestHelper.GetInt32( matches[0] ) == 1 ); + Assert.IsTrue( TestHelper.GetInt32( matches[1] ) == 2 ); + Assert.IsTrue( TestHelper.GetString( matches[2] ) == "a" ); + Assert.IsTrue( TestHelper.GetString( matches[3] ) == "b" ); } [DataTestMethod] @@ -868,7 +868,7 @@ public void BracketNotationWithWildcardAfterDotNotationAfterBracketNotationWithW } ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -896,7 +896,7 @@ public void BracketNotationWithWildcardAfterRecursiveDescent( string query, Type } } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); var expected = new[] @@ -910,12 +910,12 @@ public void BracketNotationWithWildcardAfterRecursiveDescent( string query, Type }; Assert.IsTrue( expected.SequenceEqual( matches ) ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "value" ); - Assert.IsTrue( JsonValueHelper.GetString( matches[1], minify: true ) == JsonValueHelper.MinifyJson( """{"complex": "string", "primitives": [0,1]}""" ) ); - Assert.IsTrue( JsonValueHelper.GetString( matches[2] ) == "string" ); - Assert.IsTrue( JsonValueHelper.GetString( matches[3], minify: true ) == "[0,1]" ); - Assert.IsTrue( JsonValueHelper.GetInt32( matches[4] ) == 0 ); - Assert.IsTrue( JsonValueHelper.GetInt32( matches[5] ) == 1 ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "value" ); + Assert.IsTrue( TestHelper.GetString( matches[1], minify: true ) == TestHelper.MinifyJson( """{"complex": "string", "primitives": [0,1]}""" ) ); + Assert.IsTrue( TestHelper.GetString( matches[2] ) == "string" ); + Assert.IsTrue( TestHelper.GetString( matches[3], minify: true ) == "[0,1]" ); + Assert.IsTrue( TestHelper.GetInt32( matches[4] ) == 0 ); + Assert.IsTrue( TestHelper.GetInt32( matches[5] ) == 1 ); } [DataTestMethod] @@ -935,7 +935,7 @@ public void BracketNotationWithWildcardOnArray( string query, Type sourceType ) [0, 1] ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -957,7 +957,7 @@ public void BracketNotationWithWildcardOnEmptyArray( string query, Type sourceTy // consensus: [] const string json = "[]"; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -973,7 +973,7 @@ public void BracketNotationWithWildcardOnEmptyObject( string query, Type sourceT // consensus: [] const string json = "{}"; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = Enumerable.Empty(); @@ -995,7 +995,7 @@ public void BracketNotationWithWildcardOnNullValueArray( string query, Type sour 42 ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -1026,7 +1026,7 @@ public void BracketNotationWithWildcardOnObject( string query, Type sourceType ) "array": [0, 1] } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -1052,7 +1052,7 @@ public void BracketNotationWithoutQuotes( string query, Type sourceType ) "key": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); Assert.ThrowsException( () => { diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathDescentTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathDescentTests.cs index 7181998f..3e3a354e 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathDescentTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathDescentTests.cs @@ -25,7 +25,7 @@ public void Descent( string query, Type sourceType ) ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); _ = source.Select( query ).ToList(); } @@ -42,7 +42,7 @@ public void DescentOnNestedArrays( string query, Type sourceType ) ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); var expected = new[] @@ -74,7 +74,7 @@ public void DescentAfterDotNotation( string query, Type sourceType ) } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); _ = source.Select( query ).ToList(); } @@ -127,7 +127,7 @@ public void DotNotationAfterBracketNotationAfterDescent( string query, Type sour } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); var expected = new[] diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathDotNotationTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathDotNotationTests.cs index 6793e011..11588287 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathDotNotationTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathDotNotationTests.cs @@ -27,7 +27,7 @@ public void DotBracketNotationWithoutQuotes( string query, Type sourceType ) } } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); Assert.ThrowsException( () => { @@ -47,7 +47,7 @@ public void DotBracketNotationWithEmptyPath( string query, Type sourceType ) "''": "nice" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); Assert.ThrowsException( () => { @@ -67,7 +67,7 @@ public void DotNotationWithNonAsciiKey( string query, Type sourceType ) "\u5c6c\u6027": "value" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); var expected = new[] @@ -76,7 +76,7 @@ public void DotNotationWithNonAsciiKey( string query, Type sourceType ) }; Assert.IsTrue( expected.SequenceEqual( matches ) ); - Assert.IsTrue( JsonValueHelper.GetString( matches[0] ) == "value" ); + Assert.IsTrue( TestHelper.GetString( matches[0] ) == "value" ); } [DataTestMethod] @@ -90,7 +90,7 @@ public void DotNotationWithoutDot( string query, Type sourceType ) "$a": 2 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); Assert.ThrowsException( () => { diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs index 0b01f3e1..e8e75d89 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs @@ -24,7 +24,7 @@ public void FilterExpressionWithArrayTruthyProperty( string query, Type sourceTy {"key": "value"} ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] { source.FromJsonPathPointer( "$[1]" ) }; @@ -46,7 +46,7 @@ public void FilterExpressionWithTruthyProperty( string query, Type sourceType ) } } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] { source.FromJsonPathPointer( "$['another']" ) }; @@ -76,7 +76,7 @@ public void FilterExpressionWithLessThan( string query, Type sourceType ) ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] { source.FromJsonPathPointer( "$[0]" ), source.FromJsonPathPointer( "$[2]" ), source.FromJsonPathPointer( "$[3]" ), source.FromJsonPathPointer( "$[6]" ) }; @@ -127,7 +127,7 @@ public void FilterExpressionAfterDoNotationWithWildcardAfterRecursiveDecent( str ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -177,7 +177,7 @@ public void FilterExpressionWithDifferentGroupedOperators( string query, Type so ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] { @@ -231,7 +231,7 @@ public void FilterExpressionWithDifferentUngroupedOperators( string query, Type """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] { source.FromJsonPathPointer( "$[0]" ), source.FromJsonPathPointer( "$[1]" ), source.FromJsonPathPointer( "$[2]" ), source.FromJsonPathPointer( "$[3]" ), source.FromJsonPathPointer( "$[6]" ) }; @@ -301,7 +301,7 @@ public void FilterExpressionWithEqualsArray( string query, Type sourceType ) ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] { source.FromJsonPathPointer( "$[0]" ) }; @@ -328,7 +328,7 @@ public void FilterExpressionWithEqualsArrayForSliceWithRange1( string query, Typ ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); _ = source.Select( query ).ToArray(); } @@ -354,7 +354,7 @@ public void FilterExpressionWithEqualsArrayForDotNotationWithStart( string query ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); _ = source.Select( query ).ToArray(); } @@ -376,7 +376,7 @@ public void FilterExpressionWithEqualsArrayOrEqualsTrue( string query, Type sour ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] { source.FromJsonPathPointer( "$[0]" ), source.FromJsonPathPointer( "$[2]" ) }; @@ -450,7 +450,7 @@ public void FilterExpressionWithEqualsArrayWithSingleQuotes( string query, Type """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); _ = source.Select( query ).ToArray(); } @@ -472,7 +472,7 @@ public void FilterExpressionWithEqualsBooleanExpressionValue( string query, Type ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] { source.FromJsonPathPointer( "$[2]" ) }; @@ -530,7 +530,7 @@ public void FilterExpressionWithEqualsFalse( string query, Type sourceType ) """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] { source.FromJsonPathPointer( "$[2]" ) }; @@ -587,7 +587,7 @@ public void FilterExpressionWithEqualsNull( string query, Type sourceType ) ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToArray(); var expected = new[] { source.FromJsonPathPointer( "$[3]" ) }; diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathRootOnScalarTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathRootOnScalarTests.cs index f93998a7..6e8b642a 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathRootOnScalarTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathRootOnScalarTests.cs @@ -18,7 +18,7 @@ public void RootOnScalar( string query, Type sourceType ) // consensus: none const string json = "42"; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); var expected = new[] @@ -27,7 +27,7 @@ public void RootOnScalar( string query, Type sourceType ) }; Assert.IsTrue( expected.SequenceEqual( matches ) ); - Assert.IsTrue( JsonValueHelper.GetInt32( matches.First() ) == 42 ); + Assert.IsTrue( TestHelper.GetInt32( matches.First() ) == 42 ); } [DataTestMethod] @@ -36,7 +36,7 @@ public void RootOnScalar( string query, Type sourceType ) public void RootOnScalarFalse( string query, Type sourceType ) { const string json = "false"; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); var expected = new[] @@ -45,7 +45,7 @@ public void RootOnScalarFalse( string query, Type sourceType ) }; Assert.IsTrue( expected.SequenceEqual( matches ) ); - Assert.IsTrue( JsonValueHelper.GetBoolean( matches.First() ) == false ); + Assert.IsTrue( TestHelper.GetBoolean( matches.First() ) == false ); } [DataTestMethod] @@ -54,7 +54,7 @@ public void RootOnScalarFalse( string query, Type sourceType ) public void RootOnScalarTrue( string query, Type sourceType ) { const string json = "true"; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ).ToList(); var expected = new[] @@ -63,6 +63,6 @@ public void RootOnScalarTrue( string query, Type sourceType ) }; Assert.IsTrue( expected.SequenceEqual( matches ) ); - Assert.IsTrue( JsonValueHelper.GetBoolean( matches.First() ) ); + Assert.IsTrue( TestHelper.GetBoolean( matches.First() ) ); } } diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathUnionTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathUnionTests.cs index 4c10de4a..b981ac9b 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathUnionTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathUnionTests.cs @@ -22,7 +22,7 @@ public void UnionWithDuplicationFromArray( string query, Type sourceType ) "a" ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -46,7 +46,7 @@ public void UnionWithDuplicationFromObject( string query, Type sourceType ) "a": 1 } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -79,7 +79,7 @@ public void UnionWithFilter( string query, Type sourceType ) { "key": 4 } ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -108,7 +108,7 @@ public void UnionWithKeys( string query, Type sourceType ) "another": "entry" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -134,7 +134,7 @@ public void UnionWithMultipleKeys( string query, Type sourceType ) "thing1": "thing2" } """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] @@ -182,7 +182,7 @@ public void UnionWithKeysAfterRecursiveDescent( string query, Type sourceType ) ] """; - var source = GetDocumentFromSource( sourceType, json ); + var source = GetDocumentAdapter( sourceType, json ); var matches = source.Select( query ); var expected = new[] diff --git a/test/Hyperbee.Json.Tests/TestSupport/IJsonPathSource.cs b/test/Hyperbee.Json.Tests/TestSupport/IJsonDocument.cs similarity index 85% rename from test/Hyperbee.Json.Tests/TestSupport/IJsonPathSource.cs rename to test/Hyperbee.Json.Tests/TestSupport/IJsonDocument.cs index 5502fd9b..c198f59d 100644 --- a/test/Hyperbee.Json.Tests/TestSupport/IJsonPathSource.cs +++ b/test/Hyperbee.Json.Tests/TestSupport/IJsonDocument.cs @@ -2,7 +2,7 @@ namespace Hyperbee.Json.Tests.TestSupport; -public interface IJsonPathSource +public interface IJsonDocument { IEnumerable Select( string query ); dynamic FromJsonPathPointer( string pathLiteral ); diff --git a/test/Hyperbee.Json.Tests/TestSupport/JsonDocumentSource.cs b/test/Hyperbee.Json.Tests/TestSupport/JsonDocumentSource.cs deleted file mode 100644 index f8150ede..00000000 --- a/test/Hyperbee.Json.Tests/TestSupport/JsonDocumentSource.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; -using Hyperbee.Json.Extensions; - -namespace Hyperbee.Json.Tests.TestSupport; - -public class JsonDocumentSource( string source ) : IJsonPathSource -{ - private JsonDocument Document { get; } = JsonDocument.Parse( source ); - public IEnumerable Select( string query ) => Document.Select( query ).Cast(); - public dynamic FromJsonPathPointer( string pathLiteral ) => Document.RootElement.FromJsonPathPointer( pathLiteral ); -} diff --git a/test/Hyperbee.Json.Tests/TestSupport/JsonElementHelper.cs b/test/Hyperbee.Json.Tests/TestSupport/JsonElementHelper.cs new file mode 100644 index 00000000..3cb9112d --- /dev/null +++ b/test/Hyperbee.Json.Tests/TestSupport/JsonElementHelper.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Tests.TestSupport; + +public class JsonPathDocument( string source ) : IJsonDocument +{ + private JsonDocument Document { get; } = JsonDocument.Parse( source ); + public IEnumerable Select( string query ) => Document.Select( query ).Cast(); + public dynamic FromJsonPathPointer( string pathLiteral ) => Document.RootElement.FromJsonPathPointer( pathLiteral ); +} + +internal static partial class TestHelper +{ + // JsonElement Values + + public static bool GetBoolean( JsonElement value ) => value.GetBoolean(); + + public static int GetInt32( JsonElement value ) => value.GetInt32(); + + public static string GetString( JsonElement value, bool minify = false ) + { + if ( value.ValueKind != JsonValueKind.Object && value.ValueKind != JsonValueKind.Array ) + return value.GetString(); + + var result = value.ToString(); + return minify ? MinifyJson( result ) : result; + } +} diff --git a/test/Hyperbee.Json.Tests/TestSupport/JsonNodeHelper.cs b/test/Hyperbee.Json.Tests/TestSupport/JsonNodeHelper.cs new file mode 100644 index 00000000..fc1e4dfa --- /dev/null +++ b/test/Hyperbee.Json.Tests/TestSupport/JsonNodeHelper.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Tests.TestSupport; + +public class JsonPathNode( string source ) : IJsonDocument +{ + private JsonNode Document { get; } = JsonNode.Parse( source ); + public IEnumerable Select( string query ) => Document.Select( query ); + + public dynamic FromJsonPathPointer( string pathLiteral ) => Document.FromJsonPathPointer( pathLiteral ); +} + +internal static partial class TestHelper +{ + public static bool GetBoolean( JsonNode value ) => value.AsValue().GetValue(); + + public static int GetInt32( JsonNode value ) => value.AsValue().GetValue(); + + public static string GetString( JsonNode value, bool minify = false ) + { + if ( value is not JsonObject && value is not JsonArray ) + return value.AsValue().GetValue(); + + var options = new JsonSerializerOptions { WriteIndented = false }; + + var result = value.ToJsonString( options ); + return minify ? MinifyJson( result ) : result; + } +} diff --git a/test/Hyperbee.Json.Tests/TestSupport/JsonNodeSource.cs b/test/Hyperbee.Json.Tests/TestSupport/JsonNodeSource.cs deleted file mode 100644 index 72e35878..00000000 --- a/test/Hyperbee.Json.Tests/TestSupport/JsonNodeSource.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; - -namespace Hyperbee.Json.Tests.TestSupport; - -public class JsonNodeSource( string source ) : IJsonPathSource -{ - private JsonNode Document { get; } = JsonNode.Parse( source ); - public IEnumerable Select( string query ) => Document.Select( query ); - - public dynamic FromJsonPathPointer( string pathLiteral ) => Document.FromJsonPathPointer( pathLiteral ); -} diff --git a/test/Hyperbee.Json.Tests/TestSupport/JsonTestBase.cs b/test/Hyperbee.Json.Tests/TestSupport/JsonTestBase.cs index 4c6231ae..3def0587 100644 --- a/test/Hyperbee.Json.Tests/TestSupport/JsonTestBase.cs +++ b/test/Hyperbee.Json.Tests/TestSupport/JsonTestBase.cs @@ -10,27 +10,27 @@ public class JsonTestBase { protected static string DocumentDefault { get; set; } = "BookStore.json"; - protected static string ReadJsonString( string filename = null ) + protected static string ReadJsonString( string resourceName = null ) { - using var stream = GetManifestStream( filename ); + using var stream = GetManifestStream( resourceName ); using var reader = new StreamReader( stream! ); return reader.ReadToEnd(); } - private static Stream GetManifestStream( string filename = null ) + private static Stream GetManifestStream( string resourceName = null ) { - filename ??= DocumentDefault; + resourceName ??= DocumentDefault; return Assembly .GetExecutingAssembly() - .GetManifestResourceStream( $"Hyperbee.Json.Tests.TestDocuments.{filename}" ); + .GetManifestResourceStream( $"Hyperbee.Json.Tests.TestDocuments.{resourceName}" ); } - public static TType GetDocument( string filename = null ) + public static TType GetDocument( string resourceName = null ) { var type = typeof( TType ); - var stream = GetManifestStream( filename ); + var stream = GetManifestStream( resourceName ); if ( type == typeof( JsonDocument ) ) return (TType) (object) JsonDocument.Parse( stream! ); @@ -44,19 +44,19 @@ public static TType GetDocument( string filename = null ) throw new NotSupportedException(); } - public static IJsonPathSource GetDocumentFromResource( Type target, string filename = null ) + public static IJsonDocument GetDocumentAdapter( Type target ) { - var source = ReadJsonString( filename ); - return GetDocumentFromSource( target, source ); + var source = ReadJsonString(); + return GetDocumentAdapter( target, source ); } - public static IJsonPathSource GetDocumentFromSource( Type target, string source ) + public static IJsonDocument GetDocumentAdapter( Type target, string source ) { if ( target == typeof( JsonDocument ) ) - return new JsonDocumentSource( source ); + return new JsonPathDocument( source ); if ( target == typeof( JsonNode ) ) - return new JsonNodeSource( source ); + return new JsonPathNode( source ); throw new NotSupportedException(); } diff --git a/test/Hyperbee.Json.Tests/TestSupport/JsonValueHelper.cs b/test/Hyperbee.Json.Tests/TestSupport/JsonValueHelper.cs deleted file mode 100644 index f1156e69..00000000 --- a/test/Hyperbee.Json.Tests/TestSupport/JsonValueHelper.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Nodes; - -namespace Hyperbee.Json.Tests.TestSupport; - -// Test helper to assist with getting, and normalizing, json values -// -// JsonElement and JsonNode return values differently. -// Provide a common interface for value retrieval to simplify unit tests. - -internal static class JsonValueHelper -{ - // JsonElement Values - - public static bool GetBoolean( JsonElement value ) => value.GetBoolean(); - - public static int GetInt32( JsonElement value ) => value.GetInt32(); - - public static string GetString( JsonElement value, bool minify = false ) - { - if ( value.ValueKind != JsonValueKind.Object && value.ValueKind != JsonValueKind.Array ) - return value.GetString(); - - var result = value.ToString(); - return minify ? MinifyJson( result ) : result; - } - - // JsonNode Values - - public static bool GetBoolean( JsonNode value ) => value.AsValue().GetValue(); - - public static int GetInt32( JsonNode value ) => value.AsValue().GetValue(); - - public static string GetString( JsonNode value, bool minify = false ) - { - if ( value is not JsonObject && value is not JsonArray ) - return value.AsValue().GetValue(); - - var options = new JsonSerializerOptions { WriteIndented = false }; - - var result = value.ToJsonString( options ); - return minify ? MinifyJson( result ) : result; - } - - // Json string helpers - - public static string MinifyJson( ReadOnlySpan input ) - { - Span buffer = new char[input.Length]; - int bufferIndex = 0; - bool insideString = false; - bool escapeNext = false; - - foreach ( char ch in input ) - { - switch ( ch ) - { - case '\\': - if ( insideString ) escapeNext = !escapeNext; - buffer[bufferIndex++] = ch; - break; - - case '\"': - if ( !escapeNext ) - insideString = !insideString; - - escapeNext = false; - buffer[bufferIndex++] = ch; - break; - - case '\r': - case '\n': - case '\t': - case ' ': - if ( insideString ) - buffer[bufferIndex++] = ch; - - break; - - default: - escapeNext = false; - buffer[bufferIndex++] = ch; - break; - } - } - - return new string( buffer[..bufferIndex] ); - } -} diff --git a/test/Hyperbee.Json.Tests/TestSupport/TestHelper.cs b/test/Hyperbee.Json.Tests/TestSupport/TestHelper.cs new file mode 100644 index 00000000..59e70e1d --- /dev/null +++ b/test/Hyperbee.Json.Tests/TestSupport/TestHelper.cs @@ -0,0 +1,49 @@ +using System; + +namespace Hyperbee.Json.Tests.TestSupport; + +internal static partial class TestHelper +{ + public static string MinifyJson( ReadOnlySpan input ) + { + Span buffer = new char[input.Length]; + int bufferIndex = 0; + bool insideString = false; + bool escapeNext = false; + + foreach ( char ch in input ) + { + switch ( ch ) + { + case '\\': + if ( insideString ) escapeNext = !escapeNext; + buffer[bufferIndex++] = ch; + break; + + case '\"': + if ( !escapeNext ) + insideString = !insideString; + + escapeNext = false; + buffer[bufferIndex++] = ch; + break; + + case '\r': + case '\n': + case '\t': + case ' ': + if ( insideString ) + buffer[bufferIndex++] = ch; + + break; + + default: + escapeNext = false; + buffer[bufferIndex++] = ch; + break; + } + } + + return new string( buffer[..bufferIndex] ); + } +} From 0bfb6b99757172d5711198ee39a4391338d38aed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 11:04:33 -0700 Subject: [PATCH 11/18] [FEATURE]: Add support for math operators (#51) * Add + - / * operators * Add % operator * Add `in` operator * Add unit tests --------- Co-authored-by: Brenton Farmer --- docs/JSONPATH-SYNTAX.md | 2 +- .../Element/ElementValueAccessor.cs | 38 +- .../Element/ValueTypeExtensions.cs | 10 + .../Descriptors/Node/NodeValueAccessor.cs | 38 +- .../Extensions/EnumerableExtensions.cs | 3 +- .../Parser/Expressions/CompareExpression.cs | 82 ++- .../Parser/Expressions/MathExpression.cs | 141 ++++ .../Parser/Expressions/TruthyExpression.cs | 14 +- .../Filters/Parser/FilterParser.cs | 139 +++- src/Hyperbee.Json/Filters/Parser/Operator.cs | 67 +- .../Filters/Parser/ValueTypeComparer.cs | 47 +- .../Filters/Values/ScalarValue.cs | 3 +- .../Tests/cts-basic-tests.cs | 120 ++-- .../Tests/cts-filter-tests.cs | 380 +++++------ .../Tests/cts-functions-tests.cs | 228 +++---- .../Tests/cts-index-selector-tests.cs | 28 +- .../Tests/cts-name-selector-tests.cs | 136 ++-- .../Tests/cts-slice-selector-tests.cs | 124 ++-- .../Tests/cts-whitespace-tests.cs | 624 +++++++++--------- test/Hyperbee.Json.Cts/generate_tests.ps1 | 46 +- .../Query/JsonPathFilterExpressionTests.cs | 71 -- .../Query/JsonPathInOperationsTests.cs | 130 ++++ .../Query/JsonPathMathOperationsTests.cs | 105 +++ 23 files changed, 1546 insertions(+), 1030 deletions(-) create mode 100644 src/Hyperbee.Json/Filters/Parser/Expressions/MathExpression.cs create mode 100644 test/Hyperbee.Json.Tests/Query/JsonPathInOperationsTests.cs create mode 100644 test/Hyperbee.Json.Tests/Query/JsonPathMathOperationsTests.cs diff --git a/docs/JSONPATH-SYNTAX.md b/docs/JSONPATH-SYNTAX.md index 8d73add8..3d6ff697 100644 --- a/docs/JSONPATH-SYNTAX.md +++ b/docs/JSONPATH-SYNTAX.md @@ -372,7 +372,7 @@ Filters can be combined using logical operators `&&` (and) and `||` (or). // Output: { "price": 15 } ``` -## More Code Examples +## More Examples ### JSON Sample Document 1 ```json diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs index 2c2487ac..aae8a8ca 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs @@ -136,6 +136,13 @@ static bool IsPathOperator( ReadOnlySpan x ) } } + public bool TryGetFromPointer( in JsonElement element, JsonPathSegment segment, out JsonElement childValue ) + { + return element.TryGetFromJsonPathPointer( segment, out childValue ); + } + + // Filter Methods + public bool DeepEquals( JsonElement left, JsonElement right ) { return left.DeepEquals( right ); @@ -143,8 +150,14 @@ public bool DeepEquals( JsonElement left, JsonElement right ) public bool TryParseNode( ReadOnlySpan item, out JsonElement element ) { - var bytes = Encoding.UTF8.GetBytes( item.ToArray() ); - var reader = new Utf8JsonReader( bytes ); + var maxLength = Encoding.UTF8.GetMaxByteCount( item.Length ); + Span utf8Bytes = maxLength <= 256 ? stackalloc byte[maxLength] : new byte[maxLength]; + + var length = Encoding.UTF8.GetBytes( item, utf8Bytes ); + + ReplaceSingleQuotes( ref utf8Bytes, length ); + + var reader = new Utf8JsonReader( utf8Bytes[..length] ); try { @@ -161,6 +174,22 @@ public bool TryParseNode( ReadOnlySpan item, out JsonElement element ) element = default; return false; + + static void ReplaceSingleQuotes( ref Span utf8Bytes, int length ) + { + var insideString = false; + for ( var i = 0; i < length; i++ ) + { + if ( utf8Bytes[i] == (byte) '\"' ) + { + insideString = !insideString; + } + else if ( !insideString && utf8Bytes[i] == (byte) '\'' && (i == 0 || utf8Bytes[i - 1] != '\\') ) + { + utf8Bytes[i] = (byte) '\"'; + } + } + } } public bool TryGetValueFromNode( JsonElement element, out IConvertible value ) @@ -202,9 +231,4 @@ public bool TryGetValueFromNode( JsonElement element, out IConvertible value ) return true; } - - public bool TryGetFromPointer( in JsonElement element, JsonPathSegment segment, out JsonElement childValue ) - { - return element.TryGetFromJsonPathPointer( segment, out childValue ); - } } diff --git a/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs b/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs index 718a93b1..41ca1520 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs @@ -37,6 +37,16 @@ public static bool TryGetNode( this IValueType input, out T value ) return false; } + public static bool TryGetNumber( this IValueType input, out IConvertible value ) + { + if ( TryGetValue( input, out value ) && value is int || value is float ) + return true; + + value = default; + return false; + } + + private static bool TryConvertTo( this JsonElement element, out T value ) where T : IConvertible { value = default; diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs index 010a4209..f2d6b156 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs @@ -1,5 +1,6 @@ using System.Globalization; using System.Runtime.CompilerServices; +using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using Hyperbee.Json.Extensions; @@ -140,17 +141,29 @@ static bool IsPathOperator( ReadOnlySpan x ) } } + public bool TryGetFromPointer( in JsonNode node, JsonPathSegment segment, out JsonNode childValue ) + { + return node.TryGetFromJsonPathPointer( segment, out childValue ); + } + + // Filter methods + public bool DeepEquals( JsonNode left, JsonNode right ) { return JsonNode.DeepEquals( left, right ); } - public bool TryParseNode( ReadOnlySpan item, out JsonNode node ) { + var maxLength = Encoding.UTF8.GetMaxByteCount( item.Length ); + Span utf8Bytes = maxLength <= 256 ? stackalloc byte[maxLength] : new byte[maxLength]; + var length = Encoding.UTF8.GetBytes( item, utf8Bytes ); + + ReplaceSingleQuotes( ref utf8Bytes, length ); + try { - node = JsonNode.Parse( item.ToString() ); + node = JsonNode.Parse( utf8Bytes[..length] ); return true; } catch @@ -158,6 +171,22 @@ public bool TryParseNode( ReadOnlySpan item, out JsonNode node ) node = null; return false; } + + static void ReplaceSingleQuotes( ref Span utf8Bytes, int length ) + { + var insideString = false; + for ( var i = 0; i < length; i++ ) + { + if ( utf8Bytes[i] == (byte) '\"' ) + { + insideString = !insideString; + } + else if ( !insideString && utf8Bytes[i] == (byte) '\'' && (i == 0 || utf8Bytes[i - 1] != '\\') ) + { + utf8Bytes[i] = (byte) '\"'; + } + } + } } public bool TryGetValueFromNode( JsonNode node, out IConvertible value ) @@ -204,9 +233,4 @@ public bool TryGetValueFromNode( JsonNode node, out IConvertible value ) return true; } - - public bool TryGetFromPointer( in JsonNode node, JsonPathSegment segment, out JsonNode childValue ) - { - return node.TryGetFromJsonPathPointer( segment, out childValue ); - } } diff --git a/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs b/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs index 550560f2..9e5079af 100644 --- a/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs +++ b/src/Hyperbee.Json/Extensions/EnumerableExtensions.cs @@ -1,4 +1,5 @@ -namespace Hyperbee.Json.Extensions; + +namespace Hyperbee.Json.Extensions; public static class EnumerableExtensions { diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs index acf9c336..16b676d9 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/CompareExpression.cs @@ -4,36 +4,22 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; -public static class CompareExpression +internal static class CompareExpression { - // Expressions - - public static Expression Equal( Expression left, Expression right, Expression comparer ) - => Expression.Call( AreEqualMethod, left, right, comparer ); - - public static Expression NotEqual( Expression left, Expression right, Expression comparer ) - => Expression.Call( AreNotEqualMethod, left, right, comparer ); - - public static Expression LessThan( Expression left, Expression right, Expression comparer ) - => Expression.Call( IsLessThanMethod, left, right, comparer ); - - public static Expression LessThanOrEqual( Expression left, Expression right, Expression comparer ) - => Expression.Call( IsLessThanOrEqualMethod, left, right, comparer ); - - public static Expression GreaterThan( Expression left, Expression right, Expression comparer ) - => Expression.Call( IsGreaterThanMethod, left, right, comparer ); + private static readonly IValueTypeComparer Comparer = JsonTypeDescriptorRegistry.GetDescriptor().Comparer; - public static Expression GreaterThanOrEqual( Expression left, Expression right, Expression comparer ) - => Expression.Call( IsGreaterThanOrEqualMethod, left, right, comparer ); - - public static Expression And( Expression left, Expression right, Expression comparer ) - => Expression.Call( AndAlsoMethod, left, right, comparer ); - - public static Expression Or( Expression left, Expression right, Expression comparer ) - => Expression.Call( OrElseMethod, left, right, comparer ); + // Expressions - public static Expression Not( Expression expression, Expression comparer ) - => Expression.Call( NotMethod, expression, comparer ); + public static Expression Equal( Expression left, Expression right ) => Expression.Call( AreEqualMethod, left, right ); + public static Expression NotEqual( Expression left, Expression right ) => Expression.Call( AreNotEqualMethod, left, right ); + public static Expression LessThan( Expression left, Expression right ) => Expression.Call( IsLessThanMethod, left, right ); + public static Expression LessThanOrEqual( Expression left, Expression right ) => Expression.Call( IsLessThanOrEqualMethod, left, right ); + public static Expression GreaterThan( Expression left, Expression right ) => Expression.Call( IsGreaterThanMethod, left, right ); + public static Expression GreaterThanOrEqual( Expression left, Expression right ) => Expression.Call( IsGreaterThanOrEqualMethod, left, right ); + public static Expression In( Expression left, Expression right ) => Expression.Call( InMethod, left, right ); + public static Expression And( Expression left, Expression right ) => Expression.Call( AndAlsoMethod, left, right ); + public static Expression Or( Expression left, Expression right ) => Expression.Call( OrElseMethod, left, right ); + public static Expression Not( Expression expression ) => Expression.Call( NotMethod, expression ); // MethodInfo @@ -45,63 +31,69 @@ public static Expression Not( Expression expression, Expression comparer ) private static readonly MethodInfo IsLessThanOrEqualMethod = typeof( CompareExpression ).GetMethod( nameof( IsLessThanOrEqual ), BindingAttr ); private static readonly MethodInfo IsGreaterThanMethod = typeof( CompareExpression ).GetMethod( nameof( IsGreaterThan ), BindingAttr ); private static readonly MethodInfo IsGreaterThanOrEqualMethod = typeof( CompareExpression ).GetMethod( nameof( IsGreaterThanOrEqual ), BindingAttr ); + private static readonly MethodInfo InMethod = typeof( CompareExpression ).GetMethod( nameof( In ), BindingAttr ); private static readonly MethodInfo AndAlsoMethod = typeof( CompareExpression ).GetMethod( nameof( AndAlso ), BindingAttr ); private static readonly MethodInfo OrElseMethod = typeof( CompareExpression ).GetMethod( nameof( OrElse ), BindingAttr ); private static readonly MethodInfo NotMethod = typeof( CompareExpression ).GetMethod( nameof( NotBoolean ), BindingAttr ); // Methods - private static bool AreEqual( IValueType left, IValueType right, IValueTypeComparer comparer ) + private static ScalarValue AreEqual( IValueType left, IValueType right ) { - return comparer.Compare( left, right, Operator.Equals ) == 0; + return Comparer.Compare( left, right, Operator.Equals ) == 0; } - private static bool AreNotEqual( IValueType left, IValueType right, IValueTypeComparer comparer ) + private static ScalarValue AreNotEqual( IValueType left, IValueType right ) { - return comparer.Compare( left, right, Operator.NotEquals ) != 0; + return Comparer.Compare( left, right, Operator.NotEquals ) != 0; } - private static bool IsLessThan( IValueType left, IValueType right, IValueTypeComparer comparer ) + private static ScalarValue IsLessThan( IValueType left, IValueType right ) { - return comparer.Compare( left, right, Operator.LessThan ) < 0; + return Comparer.Compare( left, right, Operator.LessThan ) < 0; } - private static bool IsLessThanOrEqual( IValueType left, IValueType right, IValueTypeComparer comparer ) + private static ScalarValue IsLessThanOrEqual( IValueType left, IValueType right ) { - return comparer.Compare( left, right, Operator.LessThanOrEqual ) <= 0; + return Comparer.Compare( left, right, Operator.LessThanOrEqual ) <= 0; } - private static bool IsGreaterThan( IValueType left, IValueType right, IValueTypeComparer comparer ) + private static ScalarValue IsGreaterThan( IValueType left, IValueType right ) { - return comparer.Compare( left, right, Operator.GreaterThan ) > 0; + return Comparer.Compare( left, right, Operator.GreaterThan ) > 0; } - private static bool IsGreaterThanOrEqual( IValueType left, IValueType right, IValueTypeComparer comparer ) + private static ScalarValue IsGreaterThanOrEqual( IValueType left, IValueType right ) { - return comparer.Compare( left, right, Operator.GreaterThanOrEqual ) >= 0; + return Comparer.Compare( left, right, Operator.GreaterThanOrEqual ) >= 0; } - private static bool AndAlso( IValueType left, IValueType right, IValueTypeComparer comparer ) + private static ScalarValue AndAlso( IValueType left, IValueType right ) { if ( left is ScalarValue leftBoolValue && right is ScalarValue rightBoolValue ) return leftBoolValue.Value && rightBoolValue.Value; - return comparer.Exists( left ) && comparer.Exists( right ); + return Comparer.Exists( left ) && Comparer.Exists( right ); } - private static bool OrElse( IValueType left, IValueType right, IValueTypeComparer comparer ) + private static ScalarValue OrElse( IValueType left, IValueType right ) { if ( left is ScalarValue leftBoolValue && right is ScalarValue rightBoolValue ) return leftBoolValue.Value || rightBoolValue.Value; - return comparer.Exists( left ) || comparer.Exists( right ); + return Comparer.Exists( left ) || Comparer.Exists( right ); + } + + private static ScalarValue In( IValueType left, IValueType right ) + { + return Comparer.In( left, right ); } - private static bool NotBoolean( IValueType value, IValueTypeComparer comparer ) + private static ScalarValue NotBoolean( IValueType value ) { if ( value is ScalarValue { Value: false } ) return true; - return !comparer.Exists( value ); + return !Comparer.Exists( value ); } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/MathExpression.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/MathExpression.cs new file mode 100644 index 00000000..e4f3fabb --- /dev/null +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/MathExpression.cs @@ -0,0 +1,141 @@ +using System.Linq.Expressions; +using System.Reflection; +using Hyperbee.Json.Descriptors; +using Hyperbee.Json.Extensions; +using Hyperbee.Json.Filters.Values; + +namespace Hyperbee.Json.Filters.Parser.Expressions; + +internal static class MathExpression +{ + private static readonly ITypeDescriptor Descriptor = JsonTypeDescriptorRegistry.GetDescriptor(); + + // Expressions + + public static Expression Add( Expression left, Expression right ) => Expression.Call( AddMethod, left, right ); + public static Expression Subtract( Expression left, Expression right ) => Expression.Call( SubtractMethod, left, right ); + public static Expression Modulus( Expression left, Expression right ) => Expression.Call( ModulusMethod, left, right ); + public static Expression Multiply( Expression left, Expression right ) => Expression.Call( MultiplyMethod, left, right ); + public static Expression Divide( Expression left, Expression right ) => Expression.Call( DivideMethod, left, right ); + + // MethodInfo + + private const BindingFlags BindingAttr = BindingFlags.Static | BindingFlags.NonPublic; + + private static readonly MethodInfo AddMethod = typeof( MathExpression ).GetMethod( nameof( Add ), BindingAttr ); + private static readonly MethodInfo SubtractMethod = typeof( MathExpression ).GetMethod( nameof( Subtract ), BindingAttr ); + private static readonly MethodInfo ModulusMethod = typeof( MathExpression ).GetMethod( nameof( Modulus ), BindingAttr ); + private static readonly MethodInfo MultiplyMethod = typeof( MathExpression ).GetMethod( nameof( Multiply ), BindingAttr ); + private static readonly MethodInfo DivideMethod = typeof( MathExpression ).GetMethod( nameof( Divide ), BindingAttr ); + + // Methods + + private static IValueType Add( IValueType left, IValueType right ) + { + if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) + return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + + return leftValue is int leftInt && rightValue is int rightInt + ? Scalar.Value( leftInt + rightInt ) + : Scalar.Value( (float) leftValue + (float) rightValue ); + } + + private static IValueType Subtract( IValueType left, IValueType right ) + { + if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) + return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + + return leftValue is int leftInt && rightValue is int rightInt + ? Scalar.Value( leftInt - rightInt ) + : Scalar.Value( (float) leftValue - (float) rightValue ); + } + + private static IValueType Modulus( IValueType left, IValueType right ) + { + if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) + return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + + return leftValue is int leftInt && rightValue is int rightInt + ? Scalar.Value( leftInt % rightInt ) + : Scalar.Value( (float) leftValue % (float) rightValue ); + } + + private static IValueType Multiply( IValueType left, IValueType right ) + { + if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) + return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + + return leftValue is int leftInt && rightValue is int rightInt + ? Scalar.Value( leftInt * rightInt ) + : Scalar.Value( (float) leftValue * (float) rightValue ); + } + + private static IValueType Divide( IValueType left, IValueType right ) + { + if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) + return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + + // dividing two int values may produce a fractional result + var floatValue = Convert.ToSingle( leftValue ) / Convert.ToSingle( rightValue ); + + // back to int if possible + if ( leftValue is int && rightValue is int && TryConvertToInt( floatValue, out var intValue ) ) + return Scalar.Value( intValue ); + + return Scalar.Value( floatValue ); + + // Helper to convert float to int if the fractional part is within a tolerance + + static bool TryConvertToInt( float value, out int result, float tolerance = 1e-6f ) + { + // Calculate the difference between the float and the nearest integer + float fractionalPart = Math.Abs( value - (float) Math.Round( value ) ); + + // Check if the fractional part is within the tolerance + if ( fractionalPart < tolerance ) + { + // If within the tolerance, assign the rounded value to result and return true + result = (int) Math.Round( value ); + return true; + } + + // If not within the tolerance, assign default value to result and return false + result = default; + return false; + } + } + + // Helpers + + private static bool TryGetNumber( IValueType valueType, out IConvertible value ) + { + if ( valueType is ScalarValue intValue ) + { + value = intValue.Value; + return true; + } + + if ( valueType is ScalarValue floatValue ) + { + value = floatValue.Value; + return true; + } + + if ( valueType is NodeList nodes ) + { + var node = nodes.OneOrDefault(); + + if ( node != null && Descriptor.Accessor.TryGetValueFromNode( node, out var nodeValue ) ) + { + if ( nodeValue is float || nodeValue is int ) + { + value = nodeValue; + return true; + } + } + } + + value = default; + return false; + } +} diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs index 16937c8b..9afe6299 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/TruthyExpression.cs @@ -11,9 +11,13 @@ public static class TruthyExpression public static Expression IsTruthyExpression( Expression expression ) { - return expression.Type == typeof( bool ) - ? expression - : Expression.Call( IsTruthyMethod, expression ); + if ( expression.Type == typeof( bool ) ) + return expression; + + if ( expression.Type == typeof( ScalarValue ) ) + expression = Expression.Convert( expression, typeof( IValueType ) ); + + return Expression.Call( IsTruthyMethod, expression ); } private static bool IsTruthy( IValueType value ) @@ -32,7 +36,9 @@ private static bool IsTruthy( IValueType value ) return truthy; - static bool Any( IEnumerable enumerable ) // Avoid cast to object: enumerable.Cast().Any() + // Helper method to avoid casting IEnumerable to IEnumerable + + static bool Any( IEnumerable enumerable ) { var enumerator = enumerable.GetEnumerator(); using var disposable = enumerator as IDisposable; diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index b0ba49fb..721ba5a7 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -60,7 +60,7 @@ internal static Expression Parse( ref ParserState state ) // recursion entrypoin do { MoveNext( ref state ); - items.Enqueue( GetExprItem( ref state ) ); // will recurse for nested expressions + items.Enqueue( GetExprItem( ref state ) ); // may cause recursion } while ( state.IsParsing ); @@ -82,13 +82,13 @@ private static ExprItem GetExprItem( ref ParserState state ) if ( NotExpressionFactory.TryGetExpression( ref state, out var expression, ref expressionInfo ) ) return ExprItem( ref state, expression, expressionInfo ); - if ( ParenExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) // will recurse. + if ( ParenExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) // will recurse return ExprItem( ref state, expression, expressionInfo ); if ( SelectExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) return ExprItem( ref state, expression, expressionInfo ); - if ( FunctionExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, Descriptor ) ) // may recurse for each function argument. + if ( FunctionExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, Descriptor ) ) // may recurse for each function argument return ExprItem( ref state, expression, expressionInfo ); if ( LiteralExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) @@ -129,9 +129,9 @@ private static void MoveNext( ref ParserState state ) // move to the next item while ( true ) { - itemEnd = state.Pos; // store before calling NextCharacter + itemEnd = state.Pos; // save Pos before calling NextCharacter - NextCharacter( ref state, out var nextChar, ref quote ); // will advance state.Pos + NextCharacter( ref state, itemStart, out var nextChar, ref quote ); // will advance state.Pos if ( IsFinished( in state, nextChar ) ) { @@ -149,7 +149,7 @@ private static void MoveNext( ref ParserState state ) // move to the next item return; - // Helper method to determine if item parsing is finished + // Helper method to determine if item collection is finished static bool IsFinished( in ParserState state, char ch ) { // order of operations matters @@ -166,7 +166,7 @@ static bool IsFinished( in ParserState state, char ch ) private static void MoveNextOperator( ref ParserState state ) // move to the next operator { - if ( state.Operator.IsLogical() || state.Operator.IsComparison() ) + if ( state.Operator.IsLogical() || state.Operator.IsComparison() || state.Operator.IsMath() ) { return; } @@ -178,14 +178,15 @@ private static void MoveNextOperator( ref ParserState state ) // move to the nex } char? quoteChar = null; + var start = state.Pos; - while ( !(state.Operator.IsLogical() || state.Operator.IsComparison()) && !state.EndOfBuffer ) + while ( !(state.Operator.IsLogical() || state.Operator.IsComparison() || state.Operator.IsMath()) && !state.EndOfBuffer ) { - NextCharacter( ref state, out _, ref quoteChar ); + NextCharacter( ref state, start, out _, ref quoteChar ); } } - private static void NextCharacter( ref ParserState state, out char nextChar, ref char? quoteChar ) + private static void NextCharacter( ref ParserState state, int start, out char nextChar, ref char? quoteChar ) { nextChar = state.Buffer[state.Pos++]; @@ -230,6 +231,11 @@ private static void NextCharacter( ref ParserState state, out char nextChar, ref case '<': state.Operator = Operator.LessThan; break; + case 'i' when Next( ref state, 'n' ): + state.Operator = IsInOperator( state ) + ? Operator.In + : Operator.Token; // `in` must be surrounded by whitespace + break; case '!': state.Operator = Operator.Not; break; @@ -241,6 +247,27 @@ private static void NextCharacter( ref ParserState state, out char nextChar, ref state.ParenDepth--; state.Operator = Operator.ClosedParen; break; + case '+': + state.Operator = IsAddSubtractOperator( state, start ) + ? Operator.Add + : Operator.Token; // ignore +1 -1 1e+2 1e-2 + break; + case '-': + state.Operator = IsAddSubtractOperator( state, start ) + ? Operator.Subtract + : Operator.Token; // ignore +1 -1 1e+2 1e-2 + break; + case '*': + state.Operator = IsMultiplyOperator( state, start ) + ? Operator.Multiply + : Operator.Token; // ignore .* [* ,* + break; + case '%': + state.Operator = Operator.Modulus; + break; + case '/': + state.Operator = Operator.Divide; + break; case ' ' or '\t' or '\r' or '\n': state.Operator = Operator.Whitespace; break; @@ -272,6 +299,47 @@ static bool Next( ref ParserState state, char expected ) state.Pos++; return true; } + + // Helper method to check if `in` is a valid operator + static bool IsInOperator( in ParserState state ) + { + // ` in ` must be surrounded by whitespace + + var span = state.Buffer[(state.Pos - 3)..(state.Pos + 1)]; + return span.Length == 4 && char.IsWhiteSpace( span[0] ) && char.IsWhiteSpace( span[^1] ); + } + + // Helper method to check if the operator is a valid add or subtract operator + static bool IsAddSubtractOperator( in ParserState state, int start ) + { + // exclude +1 -1 1e+2 1e-2 .1 + + var span = state.Buffer[start..state.Pos]; + + return !span.IsEmpty && span[0] != '+' && span[0] != '-' && span[0] != '.' && span.Length >= 2 && span[^2] != 'e' && span[^2] != 'E'; + } + + // Helper method to check if the operator is a valid multiply operator + static bool IsMultiplyOperator( in ParserState state, int start ) + { + // exclude `.*` and `,*` and `[*` + + var span = state.Buffer[start..(state.Pos - 1)]; + + for ( var i = span.Length - 1; i >= 0; i-- ) + { + var c = span[i]; + if ( char.IsWhiteSpace( c ) ) + continue; + + if ( c == '[' || c == '.' || c == ',' ) + return false; + + break; + } + + return true; + } } private static Expression Merge( in ParserState state, ExprItem left, Queue items, bool mergeOneOnly = false ) @@ -318,11 +386,17 @@ static int GetPrecedence( Operator type ) Operator.Or => 2, Operator.And => 3, Operator.Equals or + Operator.In or Operator.NotEquals or Operator.GreaterThan or Operator.GreaterThanOrEqual or Operator.LessThan or Operator.LessThanOrEqual => 4, + Operator.Add or + Operator.Subtract => 5, + Operator.Multiply or + Operator.Divide or + Operator.Modulus => 6, _ => 0, }; } @@ -333,38 +407,40 @@ private static void MergeItems( ExprItem left, ExprItem right ) left.Expression = ConvertExpression( left.Expression ); right.Expression = ConvertExpression( right.Expression ); - var comparer = Expression.Constant( Descriptor.Comparer, typeof( IValueTypeComparer ) ); - left.Expression = left.Operator switch { - Operator.Equals => CompareExpression.Equal( left.Expression, right.Expression, comparer ), - Operator.NotEquals => CompareExpression.NotEqual( left.Expression, right.Expression, comparer ), - Operator.GreaterThan => CompareExpression.GreaterThan( left.Expression, right.Expression, comparer ), - Operator.GreaterThanOrEqual => CompareExpression.GreaterThanOrEqual( left.Expression, right.Expression, comparer ), - Operator.LessThan => CompareExpression.LessThan( left.Expression, right.Expression, comparer ), - Operator.LessThanOrEqual => CompareExpression.LessThanOrEqual( left.Expression, right.Expression, comparer ), - Operator.And => CompareExpression.And( left.Expression, right.Expression, comparer ), - Operator.Or => CompareExpression.Or( left.Expression, right.Expression, comparer ), - Operator.Not => CompareExpression.Not( right.Expression, comparer ), + Operator.Equals => CompareExpression.Equal( left.Expression, right.Expression ), + Operator.NotEquals => CompareExpression.NotEqual( left.Expression, right.Expression ), + Operator.GreaterThan => CompareExpression.GreaterThan( left.Expression, right.Expression ), + Operator.GreaterThanOrEqual => CompareExpression.GreaterThanOrEqual( left.Expression, right.Expression ), + Operator.LessThan => CompareExpression.LessThan( left.Expression, right.Expression ), + Operator.LessThanOrEqual => CompareExpression.LessThanOrEqual( left.Expression, right.Expression ), + + Operator.And => CompareExpression.And( left.Expression, right.Expression ), + Operator.Or => CompareExpression.Or( left.Expression, right.Expression ), + Operator.Not => CompareExpression.Not( right.Expression ), + + Operator.In => CompareExpression.Or( left.Expression, right.Expression ), + + Operator.Add => MathExpression.Add( left.Expression, right.Expression ), + Operator.Subtract => MathExpression.Subtract( left.Expression, right.Expression ), + Operator.Multiply => MathExpression.Multiply( left.Expression, right.Expression ), + Operator.Divide => MathExpression.Divide( left.Expression, right.Expression ), + Operator.Modulus => MathExpression.Modulus( left.Expression, right.Expression ), + _ => throw new InvalidOperationException( $"Invalid operator {left.Operator}" ) }; - left.Expression = ConvertBoolToValueTypeExpression( left.Expression ); - left.Operator = right.Operator; left.ExpressionInfo.Kind = ExpressionKind.Merged; return; - static Expression ConvertBoolToValueTypeExpression( Expression leftExpression ) - { - // Convert bool to ScalarValue implicit operator and then return as an IValueType - return ConvertExpression( ConvertExpression>( leftExpression ) ); - } - static Expression ConvertExpression( Expression expression ) { - return expression == null ? null : Expression.Convert( expression, typeof( TType ) ); + return expression != null && expression.Type != typeof( TType ) + ? Expression.Convert( expression, typeof( TType ) ) + : expression; } } @@ -398,6 +474,9 @@ private static void ThrowIfConstantIsNotCompared( in ParserState state, ExprItem if ( state.IsArgument ) return; + if ( left.Operator.IsMath() ) + return; + if ( left.ExpressionInfo.Kind == ExpressionKind.Literal && !left.Operator.IsComparison() ) throw new NotSupportedException( $"Unsupported literal without comparison: {state.Buffer.ToString()}." ); diff --git a/src/Hyperbee.Json/Filters/Parser/Operator.cs b/src/Hyperbee.Json/Filters/Parser/Operator.cs index ff5bd4c0..3676e403 100644 --- a/src/Hyperbee.Json/Filters/Parser/Operator.cs +++ b/src/Hyperbee.Json/Filters/Parser/Operator.cs @@ -6,49 +6,48 @@ public enum Operator None = 0x0, // Flags - NonOperator = 0x1, // 0001 - Comparison = 0x2, // 0010 - Logical = 0x4, // 0100 - Parenthesis = 0x8, // 1000 + NonOperator = 0x0001, + Comparison = 0x0002, + Logical = 0x0004, + Math = 0x0008, + Grouping = 0x0010, - // Parenthesis Operators - OpenParen = 0x10 | Parenthesis, - ClosedParen = 0x20 | Parenthesis, + // Grouping Operators + OpenParen = 0x0100 | Grouping, + ClosedParen = 0x0200 | Grouping, // Logical Operators - Not = 0x30 | Logical, - Or = 0x40 | Logical, - And = 0x50 | Logical, + Not = 0x0300 | Logical, + Or = 0x0400 | Logical, + And = 0x0500 | Logical, + In = 0x0600 | Logical, // Comparison Operators - Equals = 0x60 | Comparison, - NotEquals = 0x70 | Comparison, - LessThan = 0x80 | Comparison, - LessThanOrEqual = 0x90 | Comparison, - GreaterThan = 0xA0 | Comparison, - GreaterThanOrEqual = 0xB0 | Comparison, + Equals = 0x0700 | Comparison, + NotEquals = 0x0800 | Comparison, + LessThan = 0x0900 | Comparison, + LessThanOrEqual = 0x0A00 | Comparison, + GreaterThan = 0x0B00 | Comparison, + GreaterThanOrEqual = 0x0C00 | Comparison, + + // Math Operators + Add = 0x0D00 | Math, + Subtract = 0x0E00 | Math, + Multiply = 0x0F00 | Math, + Divide = 0x1000 | Math, + Modulus = 0x1100 | Math, // Specific non-operators - Whitespace = 0xC0 | NonOperator, - Quotes = 0xD0 | NonOperator, - Token = 0xE0 | NonOperator, - Bracket = 0xF0 | NonOperator, + Whitespace = 0x2000 | NonOperator, + Quotes = 0x2100 | NonOperator, + Token = 0x2200 | NonOperator, + Bracket = 0x2300 | NonOperator, } internal static class OperatorExtensions { - public static bool IsNonOperator( this Operator op ) - { - return (op & Operator.NonOperator) == Operator.NonOperator; - } - - public static bool IsComparison( this Operator op ) - { - return (op & Operator.Comparison) == Operator.Comparison; - } - - public static bool IsLogical( this Operator op ) - { - return (op & Operator.Logical) == Operator.Logical; - } + public static bool IsNonOperator( this Operator op ) => op.HasFlag( Operator.NonOperator ); + public static bool IsComparison( this Operator op ) => op.HasFlag( Operator.Comparison ); + public static bool IsLogical( this Operator op ) => op.HasFlag( Operator.Logical ); + public static bool IsMath( this Operator op ) => op.HasFlag( Operator.Math ); } diff --git a/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs b/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs index c6301fd6..b89295dc 100644 --- a/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs +++ b/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs @@ -8,6 +8,8 @@ public interface IValueTypeComparer public int Compare( IValueType left, IValueType right, Operator operation ); public bool Exists( IValueType node ); + + public bool In( IValueType left, IValueType right ); } public class ValueTypeComparer( IValueAccessor accessor ) : IValueTypeComparer @@ -117,6 +119,47 @@ static void ThrowIfNotNormalized( IValueType nodeType ) } } + public bool In( IValueType left, IValueType right ) + { + if ( right is not NodeList rightList ) + throw new NotSupportedException( "The right side of an `in` must be a node list." ); + + // Check if the left value is in rightList + if ( left is not NodeList leftList ) + { + // Check if the left value is in rightList + return Find( rightList, left ); + } + + // Check if any element in leftList is in rightList + foreach ( var leftItem in leftList ) + { + if ( !TryGetValueType( accessor, leftItem, out var leftItemValue ) ) + continue; + + if ( Find( rightList, leftItemValue ) ) + return true; + } + + return false; + + // Helper method to find a value in a NodeList + + bool Find( NodeList nodeList, IValueType leftItemValue ) + { + foreach ( var rightItem in nodeList ) + { + if ( TryGetValueType( accessor, rightItem, out var rightItemValue ) && + CompareValues( leftItemValue, rightItemValue, out _ ) == 0 ) + { + return true; + } + } + + return false; + } + } + public bool Exists( IValueType node ) { return node switch @@ -195,7 +238,7 @@ private static int CompareValues( IValueType left, IValueType right, out bool ty return 0; } - if ( IsTypeMismatch( left, right ) && !IsFloatToIntComparison( left, right ) ) + if ( IsTypeMismatch( left, right ) && !IsFloatToIntOperation( left, right ) ) { typeMismatch = true; // Type mismatch: important for non-equality comparisons return -1; @@ -228,7 +271,7 @@ private static int CompareValues( IValueType left, IValueType right, out bool ty static bool IsTypeMismatch( IValueType left, IValueType right ) => left?.GetType() != right?.GetType(); static bool IsNullOrNothing( IValueType value ) => value is Null or Nothing; - static bool IsFloatToIntComparison( IValueType left, IValueType right ) => + static bool IsFloatToIntOperation( IValueType left, IValueType right ) => left is ScalarValue && right is ScalarValue || left is ScalarValue && right is ScalarValue; } diff --git a/src/Hyperbee.Json/Filters/Values/ScalarValue.cs b/src/Hyperbee.Json/Filters/Values/ScalarValue.cs index 3d6f6fc3..ec648365 100644 --- a/src/Hyperbee.Json/Filters/Values/ScalarValue.cs +++ b/src/Hyperbee.Json/Filters/Values/ScalarValue.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; namespace Hyperbee.Json.Filters.Values; diff --git a/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs index d1849866..b6fe9489 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs @@ -21,7 +21,7 @@ public void Test_root_1( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -31,7 +31,7 @@ public void Test_root_1( Type documentType ) "second" ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -71,14 +71,14 @@ public void Test_name_shorthand_4( Type documentType ) "a": "A", "b": "B" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -96,14 +96,14 @@ public void Test_name_shorthand__extended_unicode___5( Type documentType ) "☺": "A", "b": "B" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -121,14 +121,14 @@ public void Test_name_shorthand__underscore_6( Type documentType ) "_": "A", "_foo": "B" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -168,12 +168,12 @@ public void Test_name_shorthand__absent_data_9( Type documentType ) "a": "A", "b": "B" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -191,12 +191,12 @@ public void Test_name_shorthand__array_data_10( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -214,7 +214,7 @@ public void Test_wildcard_shorthand__object_data_11( Type documentType ) "a": "A", "b": "B" } - """ ); + """ ); var results = document.Select( selector ); var expectOneOf = TestHelper.Parse( documentType, """ @@ -228,7 +228,7 @@ public void Test_wildcard_shorthand__object_data_11( Type documentType ) "A" ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchAny( documentType, results, expectOneOf ); Assert.IsTrue( match ); @@ -246,7 +246,7 @@ public void Test_wildcard_shorthand__array_data_12( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -254,7 +254,7 @@ public void Test_wildcard_shorthand__array_data_12( Type documentType ) "first", "second" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -272,7 +272,7 @@ public void Test_wildcard_selector__array_data_13( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -280,7 +280,7 @@ public void Test_wildcard_selector__array_data_13( Type documentType ) "first", "second" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -304,7 +304,7 @@ public void Test_wildcard_shorthand__then_name_shorthand_14( Type documentType ) "b": "By" } } - """ ); + """ ); var results = document.Select( selector ); var expectOneOf = TestHelper.Parse( documentType, """ @@ -318,7 +318,7 @@ public void Test_wildcard_shorthand__then_name_shorthand_14( Type documentType ) "Ax" ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchAny( documentType, results, expectOneOf ); Assert.IsTrue( match ); @@ -344,7 +344,7 @@ public void Test_multiple_selectors_15( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -352,7 +352,7 @@ public void Test_multiple_selectors_15( Type documentType ) 0, 2 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -389,14 +389,14 @@ public void Test_multiple_selectors__name_and_index__array_data_17( Type documen 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -414,14 +414,14 @@ public void Test_multiple_selectors__name_and_index__object_data_18( Type docume "a": 1, "b": 2 } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -447,7 +447,7 @@ public void Test_multiple_selectors__index_and_slice_19( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -456,7 +456,7 @@ public void Test_multiple_selectors__index_and_slice_19( Type documentType ) 5, 6 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -482,7 +482,7 @@ public void Test_multiple_selectors__index_and_slice__overlapping_20( Type docum 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -492,7 +492,7 @@ public void Test_multiple_selectors__index_and_slice__overlapping_20( Type docum 1, 2 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -518,7 +518,7 @@ public void Test_multiple_selectors__duplicate_index_21( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -526,7 +526,7 @@ public void Test_multiple_selectors__duplicate_index_21( Type documentType ) 1, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -552,7 +552,7 @@ public void Test_multiple_selectors__wildcard_and_index_22( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -569,7 +569,7 @@ public void Test_multiple_selectors__wildcard_and_index_22( Type documentType ) 9, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -587,7 +587,7 @@ public void Test_multiple_selectors__wildcard_and_name_23( Type documentType ) "a": "A", "b": "B" } - """ ); + """ ); var results = document.Select( selector ); var expectOneOf = TestHelper.Parse( documentType, """ @@ -603,7 +603,7 @@ public void Test_multiple_selectors__wildcard_and_name_23( Type documentType ) "A" ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchAny( documentType, results, expectOneOf ); Assert.IsTrue( match ); @@ -629,7 +629,7 @@ public void Test_multiple_selectors__wildcard_and_slice_24( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -647,7 +647,7 @@ public void Test_multiple_selectors__wildcard_and_slice_24( Type documentType ) 0, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -666,7 +666,7 @@ public void Test_multiple_selectors__multiple_wildcards_25( Type documentType ) 1, 2 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -678,7 +678,7 @@ public void Test_multiple_selectors__multiple_wildcards_25( Type documentType ) 1, 2 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -713,7 +713,7 @@ public void Test_descendant_segment__index_27( Type documentType ) ] ] } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -721,7 +721,7 @@ public void Test_descendant_segment__index_27( Type documentType ) 1, 3 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -745,7 +745,7 @@ public void Test_descendant_segment__name_shorthand_28( Type documentType ) } ] } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -753,7 +753,7 @@ public void Test_descendant_segment__name_shorthand_28( Type documentType ) "b", "c" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -771,7 +771,7 @@ public void Test_descendant_segment__wildcard_shorthand__array_data_29( Type doc 0, 1 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -779,7 +779,7 @@ public void Test_descendant_segment__wildcard_shorthand__array_data_29( Type doc 0, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -797,7 +797,7 @@ public void Test_descendant_segment__wildcard_selector__array_data_30( Type docu 0, 1 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -805,7 +805,7 @@ public void Test_descendant_segment__wildcard_selector__array_data_30( Type docu 0, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -829,7 +829,7 @@ public void Test_descendant_segment__wildcard_selector__nested_arrays_31( Type d 2 ] ] - """ ); + """ ); var results = document.Select( selector ); var expectOneOf = TestHelper.Parse( documentType, """ @@ -865,7 +865,7 @@ public void Test_descendant_segment__wildcard_selector__nested_arrays_31( Type d 1 ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchAny( documentType, results, expectOneOf ); Assert.IsTrue( match ); @@ -889,7 +889,7 @@ public void Test_descendant_segment__wildcard_selector__nested_objects_32( Type "d": 2 } } - """ ); + """ ); var results = document.Select( selector ); var expectOneOf = TestHelper.Parse( documentType, """ @@ -985,7 +985,7 @@ public void Test_descendant_segment__wildcard_selector__nested_objects_32( Type 1 ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchAny( documentType, results, expectOneOf ); Assert.IsTrue( match ); @@ -1002,14 +1002,14 @@ public void Test_descendant_segment__wildcard_shorthand__object_data_33( Type do { "a": "b" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "b" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1030,7 +1030,7 @@ public void Test_descendant_segment__wildcard_shorthand__nested_data_34( Type do } ] } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1045,7 +1045,7 @@ public void Test_descendant_segment__wildcard_shorthand__nested_data_34( Type do }, "b" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1069,7 +1069,7 @@ public void Test_descendant_segment__multiple_selectors_35( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1079,7 +1079,7 @@ public void Test_descendant_segment__multiple_selectors_35( Type documentType ) "c", "f" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1103,7 +1103,7 @@ public void Test_descendant_segment__object_traversal__multiple_selectors_36( Ty "d": "f" } } - """ ); + """ ); var results = document.Select( selector ); var expectOneOf = TestHelper.Parse( documentType, """ @@ -1121,7 +1121,7 @@ public void Test_descendant_segment__object_traversal__multiple_selectors_36( Ty "e" ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchAny( documentType, results, expectOneOf ); Assert.IsTrue( match ); diff --git a/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs index 7a5d79fa..4bb1d1ca 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs @@ -21,7 +21,7 @@ public void Test_existence__without_segments_1( Type documentType ) "a": 1, "b": null } - """ ); + """ ); var results = document.Select( selector ); var expectOneOf = TestHelper.Parse( documentType, """ @@ -35,7 +35,7 @@ public void Test_existence__without_segments_1( Type documentType ) 1 ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchAny( documentType, results, expectOneOf ); Assert.IsTrue( match ); @@ -59,7 +59,7 @@ public void Test_existence_2( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -69,7 +69,7 @@ public void Test_existence_2( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -93,7 +93,7 @@ public void Test_existence__present_with_null_3( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -103,7 +103,7 @@ public void Test_existence__present_with_null_3( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -127,7 +127,7 @@ public void Test_equals_string__single_quotes_4( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -137,7 +137,7 @@ public void Test_equals_string__single_quotes_4( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -161,7 +161,7 @@ public void Test_equals_numeric_string__single_quotes_5( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -171,7 +171,7 @@ public void Test_equals_numeric_string__single_quotes_5( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -195,7 +195,7 @@ public void Test_equals_string__double_quotes_6( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -205,7 +205,7 @@ public void Test_equals_string__double_quotes_6( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -229,7 +229,7 @@ public void Test_equals_numeric_string__double_quotes_7( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -239,7 +239,7 @@ public void Test_equals_numeric_string__double_quotes_7( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -271,7 +271,7 @@ public void Test_equals_number_8( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -281,7 +281,7 @@ public void Test_equals_number_8( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -305,7 +305,7 @@ public void Test_equals_null_9( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -315,7 +315,7 @@ public void Test_equals_null_9( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -338,12 +338,12 @@ public void Test_equals_null__absent_from_data_10( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -367,7 +367,7 @@ public void Test_equals_true_11( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -377,7 +377,7 @@ public void Test_equals_true_11( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -401,7 +401,7 @@ public void Test_equals_false_12( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -411,7 +411,7 @@ public void Test_equals_false_12( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -436,7 +436,7 @@ public void Test_equals_self_13( Type documentType ) false ] ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -451,7 +451,7 @@ public void Test_equals_self_13( Type documentType ) false ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -526,7 +526,7 @@ public void Test_deep_equality__arrays_14( Type documentType ) ] } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -550,7 +550,7 @@ public void Test_deep_equality__arrays_14( Type documentType ) ] } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -628,7 +628,7 @@ public void Test_deep_equality__objects_15( Type documentType ) } } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -662,7 +662,7 @@ public void Test_deep_equality__objects_15( Type documentType ) } } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -686,7 +686,7 @@ public void Test_not_equals_string__single_quotes_16( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -696,7 +696,7 @@ public void Test_not_equals_string__single_quotes_16( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -720,7 +720,7 @@ public void Test_not_equals_numeric_string__single_quotes_17( Type documentType "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -730,7 +730,7 @@ public void Test_not_equals_numeric_string__single_quotes_17( Type documentType "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -754,7 +754,7 @@ public void Test_not_equals_string__single_quotes__different_type_18( Type docum "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -764,7 +764,7 @@ public void Test_not_equals_string__single_quotes__different_type_18( Type docum "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -788,7 +788,7 @@ public void Test_not_equals_string__double_quotes_19( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -798,7 +798,7 @@ public void Test_not_equals_string__double_quotes_19( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -822,7 +822,7 @@ public void Test_not_equals_numeric_string__double_quotes_20( Type documentType "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -832,7 +832,7 @@ public void Test_not_equals_numeric_string__double_quotes_20( Type documentType "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -856,7 +856,7 @@ public void Test_not_equals_string__double_quotes__different_types_21( Type docu "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -866,7 +866,7 @@ public void Test_not_equals_string__double_quotes__different_types_21( Type docu "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -894,7 +894,7 @@ public void Test_not_equals_number_22( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -908,7 +908,7 @@ public void Test_not_equals_number_22( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -932,7 +932,7 @@ public void Test_not_equals_number__different_types_23( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -942,7 +942,7 @@ public void Test_not_equals_number__different_types_23( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -966,7 +966,7 @@ public void Test_not_equals_null_24( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -976,7 +976,7 @@ public void Test_not_equals_null_24( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -999,7 +999,7 @@ public void Test_not_equals_null__absent_from_data_25( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1012,7 +1012,7 @@ public void Test_not_equals_null__absent_from_data_25( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1036,7 +1036,7 @@ public void Test_not_equals_true_26( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1046,7 +1046,7 @@ public void Test_not_equals_true_26( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1070,7 +1070,7 @@ public void Test_not_equals_false_27( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1080,7 +1080,7 @@ public void Test_not_equals_false_27( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1104,7 +1104,7 @@ public void Test_less_than_string__single_quotes_28( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1114,7 +1114,7 @@ public void Test_less_than_string__single_quotes_28( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1138,7 +1138,7 @@ public void Test_less_than_string__double_quotes_29( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1148,7 +1148,7 @@ public void Test_less_than_string__double_quotes_29( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1180,7 +1180,7 @@ public void Test_less_than_number_30( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1190,7 +1190,7 @@ public void Test_less_than_number_30( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1214,12 +1214,12 @@ public void Test_less_than_null_31( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1243,12 +1243,12 @@ public void Test_less_than_true_32( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1272,12 +1272,12 @@ public void Test_less_than_false_33( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1301,7 +1301,7 @@ public void Test_less_than_or_equal_to_string__single_quotes_34( Type documentTy "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1315,7 +1315,7 @@ public void Test_less_than_or_equal_to_string__single_quotes_34( Type documentTy "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1339,7 +1339,7 @@ public void Test_less_than_or_equal_to_string__double_quotes_35( Type documentTy "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1353,7 +1353,7 @@ public void Test_less_than_or_equal_to_string__double_quotes_35( Type documentTy "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1385,7 +1385,7 @@ public void Test_less_than_or_equal_to_number_36( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1399,7 +1399,7 @@ public void Test_less_than_or_equal_to_number_36( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1423,7 +1423,7 @@ public void Test_less_than_or_equal_to_null_37( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1433,7 +1433,7 @@ public void Test_less_than_or_equal_to_null_37( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1457,7 +1457,7 @@ public void Test_less_than_or_equal_to_true_38( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1467,7 +1467,7 @@ public void Test_less_than_or_equal_to_true_38( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1491,7 +1491,7 @@ public void Test_less_than_or_equal_to_false_39( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1501,7 +1501,7 @@ public void Test_less_than_or_equal_to_false_39( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1529,7 +1529,7 @@ public void Test_greater_than_string__single_quotes_40( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1539,7 +1539,7 @@ public void Test_greater_than_string__single_quotes_40( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1567,7 +1567,7 @@ public void Test_greater_than_string__double_quotes_41( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1577,7 +1577,7 @@ public void Test_greater_than_string__double_quotes_41( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1609,7 +1609,7 @@ public void Test_greater_than_number_42( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1619,7 +1619,7 @@ public void Test_greater_than_number_42( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1643,12 +1643,12 @@ public void Test_greater_than_null_43( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1672,12 +1672,12 @@ public void Test_greater_than_true_44( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1701,12 +1701,12 @@ public void Test_greater_than_false_45( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1734,7 +1734,7 @@ public void Test_greater_than_or_equal_to_string__single_quotes_46( Type documen "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1748,7 +1748,7 @@ public void Test_greater_than_or_equal_to_string__single_quotes_46( Type documen "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1776,7 +1776,7 @@ public void Test_greater_than_or_equal_to_string__double_quotes_47( Type documen "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1790,7 +1790,7 @@ public void Test_greater_than_or_equal_to_string__double_quotes_47( Type documen "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1822,7 +1822,7 @@ public void Test_greater_than_or_equal_to_number_48( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1836,7 +1836,7 @@ public void Test_greater_than_or_equal_to_number_48( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1860,7 +1860,7 @@ public void Test_greater_than_or_equal_to_null_49( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1870,7 +1870,7 @@ public void Test_greater_than_or_equal_to_null_49( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1894,7 +1894,7 @@ public void Test_greater_than_or_equal_to_true_50( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1904,7 +1904,7 @@ public void Test_greater_than_or_equal_to_true_50( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1928,7 +1928,7 @@ public void Test_greater_than_or_equal_to_false_51( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1938,7 +1938,7 @@ public void Test_greater_than_or_equal_to_false_51( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1961,7 +1961,7 @@ public void Test_exists_and_not_equals_null__absent_from_data_52( Type documentT "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1971,7 +1971,7 @@ public void Test_exists_and_not_equals_null__absent_from_data_52( Type documentT "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1997,7 +1997,7 @@ public void Test_exists_and_exists__data_false_53( Type documentType ) "c": false } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2007,7 +2007,7 @@ public void Test_exists_and_exists__data_false_53( Type documentType ) "b": false } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2033,7 +2033,7 @@ public void Test_exists_or_exists__data_false_54( Type documentType ) "c": false } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2046,7 +2046,7 @@ public void Test_exists_or_exists__data_false_54( Type documentType ) "b": false } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2074,7 +2074,7 @@ public void Test_and_55( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2084,7 +2084,7 @@ public void Test_and_55( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2116,7 +2116,7 @@ public void Test_or_56( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2130,7 +2130,7 @@ public void Test_or_56( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2158,7 +2158,7 @@ public void Test_not_expression_57( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2172,7 +2172,7 @@ public void Test_not_expression_57( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2199,7 +2199,7 @@ public void Test_not_exists_58( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2208,7 +2208,7 @@ public void Test_not_exists_58( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2235,7 +2235,7 @@ public void Test_not_exists__data_null_59( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2244,7 +2244,7 @@ public void Test_not_exists__data_null_59( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2269,7 +2269,7 @@ public void Test_non_singular_existence__wildcard_60( Type documentType ) "a": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2281,7 +2281,7 @@ public void Test_non_singular_existence__wildcard_60( Type documentType ) "a": 3 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2316,7 +2316,7 @@ public void Test_non_singular_existence__multiple_61( Type documentType ) "b": 4 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2336,7 +2336,7 @@ public void Test_non_singular_existence__multiple_61( Type documentType ) "b": 4 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2366,7 +2366,7 @@ public void Test_non_singular_existence__slice_62( Type documentType ) "a": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2380,7 +2380,7 @@ public void Test_non_singular_existence__slice_62( Type documentType ) 4 ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2405,7 +2405,7 @@ public void Test_non_singular_existence__negated_63( Type documentType ) "a": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2414,7 +2414,7 @@ public void Test_non_singular_existence__negated_63( Type documentType ) [], {} ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2489,7 +2489,7 @@ public void Test_nested_68( Type documentType ) 42 ] ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2503,7 +2503,7 @@ public void Test_nested_68( Type documentType ) 42 ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2520,12 +2520,12 @@ public void Test_name_segment_on_primitive__selects_nothing_69( Type documentTyp { "a": 1 } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2545,12 +2545,12 @@ public void Test_name_segment_on_array__selects_nothing_70( Type documentType ) 6 ] ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2569,12 +2569,12 @@ public void Test_index_segment_on_object__selects_nothing_71( Type documentType "0": 5 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2928,7 +2928,7 @@ public void Test_multiple_selectors_102( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2942,7 +2942,7 @@ public void Test_multiple_selectors_102( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2966,7 +2966,7 @@ public void Test_multiple_selectors__comparison_103( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2976,7 +2976,7 @@ public void Test_multiple_selectors__comparison_103( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3000,7 +3000,7 @@ public void Test_multiple_selectors__overlapping_104( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3018,7 +3018,7 @@ public void Test_multiple_selectors__overlapping_104( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3042,7 +3042,7 @@ public void Test_multiple_selectors__filter_and_index_105( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3056,7 +3056,7 @@ public void Test_multiple_selectors__filter_and_index_105( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3080,7 +3080,7 @@ public void Test_multiple_selectors__filter_and_wildcard_106( Type documentType "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3098,7 +3098,7 @@ public void Test_multiple_selectors__filter_and_wildcard_106( Type documentType "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3125,7 +3125,7 @@ public void Test_multiple_selectors__filter_and_slice_107( Type documentType ) "g": "h" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3142,7 +3142,7 @@ public void Test_multiple_selectors__filter_and_slice_107( Type documentType ) "g": "h" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3166,7 +3166,7 @@ public void Test_multiple_selectors__comparison_filter__index_and_slice_108( Typ "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3184,7 +3184,7 @@ public void Test_multiple_selectors__comparison_filter__index_and_slice_108( Typ "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3212,7 +3212,7 @@ public void Test_equals_number__zero_and_negative_zero_109( Type documentType ) "d": "g" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3222,7 +3222,7 @@ public void Test_equals_number__zero_and_negative_zero_109( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3250,7 +3250,7 @@ public void Test_equals_number__with_and_without_decimal_fraction_110( Type docu "d": "g" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3260,7 +3260,7 @@ public void Test_equals_number__with_and_without_decimal_fraction_110( Type docu "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3288,7 +3288,7 @@ public void Test_equals_number__exponent_111( Type documentType ) "d": "g" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3298,7 +3298,7 @@ public void Test_equals_number__exponent_111( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3326,7 +3326,7 @@ public void Test_equals_number__positive_exponent_112( Type documentType ) "d": "g" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3336,7 +3336,7 @@ public void Test_equals_number__positive_exponent_112( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3364,7 +3364,7 @@ public void Test_equals_number__negative_exponent_113( Type documentType ) "d": "g" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3374,7 +3374,7 @@ public void Test_equals_number__negative_exponent_113( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3402,7 +3402,7 @@ public void Test_equals_number__decimal_fraction_114( Type documentType ) "d": "g" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3412,7 +3412,7 @@ public void Test_equals_number__decimal_fraction_114( Type documentType ) "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3451,7 +3451,7 @@ public void Test_equals_number__decimal_fraction__exponent_116( Type documentTyp "d": "g" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3461,7 +3461,7 @@ public void Test_equals_number__decimal_fraction__exponent_116( Type documentTyp "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3489,7 +3489,7 @@ public void Test_equals_number__decimal_fraction__positive_exponent_117( Type do "d": "g" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3499,7 +3499,7 @@ public void Test_equals_number__decimal_fraction__positive_exponent_117( Type do "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3527,7 +3527,7 @@ public void Test_equals_number__decimal_fraction__negative_exponent_118( Type do "d": "g" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3537,7 +3537,7 @@ public void Test_equals_number__decimal_fraction__negative_exponent_118( Type do "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3565,7 +3565,7 @@ public void Test_equals__special_nothing_119( Type documentType ) } ] } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3577,7 +3577,7 @@ public void Test_equals__special_nothing_119( Type documentType ) "a": null } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3602,7 +3602,7 @@ public void Test_equals__empty_node_list_and_empty_node_list_120( Type documentT "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3611,7 +3611,7 @@ public void Test_equals__empty_node_list_and_empty_node_list_120( Type documentT "c": 3 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3636,7 +3636,7 @@ public void Test_equals__empty_node_list_and_special_nothing_121( Type documentT "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3648,7 +3648,7 @@ public void Test_equals__empty_node_list_and_special_nothing_121( Type documentT "c": 3 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3667,7 +3667,7 @@ public void Test_object_data_122( Type documentType ) "b": 2, "c": 3 } - """ ); + """ ); var results = document.Select( selector ); var expectOneOf = TestHelper.Parse( documentType, """ @@ -3681,7 +3681,7 @@ public void Test_object_data_122( Type documentType ) 1 ] ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchAny( documentType, results, expectOneOf ); Assert.IsTrue( match ); @@ -3715,7 +3715,7 @@ public void Test_and_binds_more_tightly_than_or_123( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3733,7 +3733,7 @@ public void Test_and_binds_more_tightly_than_or_123( Type documentType ) "c": 3 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3775,7 +3775,7 @@ public void Test_left_to_right_evaluation_124( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3801,7 +3801,7 @@ public void Test_left_to_right_evaluation_124( Type documentType ) "c": 3 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3843,7 +3843,7 @@ public void Test_group_terms__left_125( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3862,7 +3862,7 @@ public void Test_group_terms__left_125( Type documentType ) "c": 3 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3900,7 +3900,7 @@ public void Test_group_terms__right_126( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3919,7 +3919,7 @@ public void Test_group_terms__right_126( Type documentType ) "c": 3 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3938,14 +3938,14 @@ public void Test_string_literal__single_quote_in_double_quotes_127( Type documen "a", "quoted\\' literal" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "quoted' literal" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3965,14 +3965,14 @@ public void Test_string_literal__double_quote_in_single_quotes_128( Type documen "quoted\\\" literal", "'quoted\" literal'" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "quoted\" literal" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3992,14 +3992,14 @@ public void Test_string_literal__escaped_single_quote_in_single_quotes_129( Type "quoted\\' literal", "'quoted\" literal'" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "quoted' literal" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4019,14 +4019,14 @@ public void Test_string_literal__escaped_double_quote_in_double_quotes_130( Type "quoted\\\" literal", "'quoted\" literal'" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "quoted\" literal" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); diff --git a/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs index 117fcc60..46f5eb27 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs @@ -36,7 +36,7 @@ public void Test_count__count_function_1( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -55,7 +55,7 @@ public void Test_count__count_function_1( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -88,12 +88,12 @@ public void Test_count__single_node_arg_2( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -126,7 +126,7 @@ public void Test_count__multiple_selector_arg_3( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -142,7 +142,7 @@ public void Test_count__multiple_selector_arg_3( Type documentType ) "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -252,7 +252,7 @@ public void Test_length__string_data_12( Type documentType ) "a": "d" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -261,7 +261,7 @@ public void Test_length__string_data_12( Type documentType ) "a": "ab" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -286,7 +286,7 @@ public void Test_length__string_data__unicode_13( Type documentType ) "阿美", "形声字" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -295,7 +295,7 @@ public void Test_length__string_data__unicode_13( Type documentType ) "жж", "阿美" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -323,7 +323,7 @@ public void Test_length__array_data_14( Type documentType ) ] } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -336,7 +336,7 @@ public void Test_length__array_data_14( Type documentType ) ] } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -355,12 +355,12 @@ public void Test_length__missing_data_15( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -379,12 +379,12 @@ public void Test_length__number_arg_16( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -403,12 +403,12 @@ public void Test_length__true_arg_17( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -427,12 +427,12 @@ public void Test_length__false_arg_18( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -451,12 +451,12 @@ public void Test_length__null_arg_19( Type documentType ) "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -525,7 +525,7 @@ public void Test_length__arg_is_a_function_expression_24( Type documentType ) } ] } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -534,7 +534,7 @@ public void Test_length__arg_is_a_function_expression_24( Type documentType ) "a": "ab" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -559,7 +559,7 @@ public void Test_length__arg_is_special_nothing_25( Type documentType ) "a": null } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -568,7 +568,7 @@ public void Test_length__arg_is_special_nothing_25( Type documentType ) "a": "ab" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -587,7 +587,7 @@ public void Test_match__found_match_26( Type documentType ) "a": "ab" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -596,7 +596,7 @@ public void Test_match__found_match_26( Type documentType ) "a": "ab" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -615,7 +615,7 @@ public void Test_match__double_quotes_27( Type documentType ) "a": "ab" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -624,7 +624,7 @@ public void Test_match__double_quotes_27( Type documentType ) "a": "ab" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -652,14 +652,14 @@ public void Test_match__regex_from_the_document_28( Type documentType ) {} ] } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "bab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -678,12 +678,12 @@ public void Test_match__don_t_select_match_29( Type documentType ) "a": "ab" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -702,12 +702,12 @@ public void Test_match__not_a_match_30( Type documentType ) "a": "bc" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -726,7 +726,7 @@ public void Test_match__select_non_match_31( Type documentType ) "a": "bc" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -735,7 +735,7 @@ public void Test_match__select_non_match_31( Type documentType ) "a": "bc" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -754,12 +754,12 @@ public void Test_match__non_string_first_arg_32( Type documentType ) "a": "bc" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -778,12 +778,12 @@ public void Test_match__non_string_second_arg_33( Type documentType ) "a": "bc" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -806,14 +806,14 @@ public void Test_match__filter__match_function__unicode_char_class__uppercase_34 [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "Ж" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -835,7 +835,7 @@ public void Test_match__filter__match_function__unicode_char_class_negated__uppe [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -843,7 +843,7 @@ public void Test_match__filter__match_function__unicode_char_class_negated__uppe "ж", "1" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -865,14 +865,14 @@ public void Test_match__filter__match_function__unicode__surrogate_pair_36( Type [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "a𐄁b" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -894,14 +894,14 @@ public void Test_match__dot_matcher_on__u2028_37( Type documentType ) [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "\u2028" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -923,14 +923,14 @@ public void Test_match__dot_matcher_on__u2029_38( Type documentType ) [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "\u2029" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -988,7 +988,7 @@ public void Test_match__arg_is_a_function_expression_42( Type documentType ) } ] } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -997,7 +997,7 @@ public void Test_match__arg_is_a_function_expression_42( Type documentType ) "a": "ab" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1016,7 +1016,7 @@ public void Test_match__dot_in_character_class_43( Type documentType ) "a.c", "axc" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1024,7 +1024,7 @@ public void Test_match__dot_in_character_class_43( Type documentType ) "abc", "a.c" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1043,14 +1043,14 @@ public void Test_match__escaped_dot_44( Type documentType ) "a.c", "axc" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "a.c" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1070,14 +1070,14 @@ public void Test_match__escaped_backslash_before_dot_45( Type documentType ) "axc", "a\\\u2028c" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "a\\\u2028c" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1096,14 +1096,14 @@ public void Test_match__escaped_left_square_bracket_46( Type documentType ) "a.c", "a[\u2028c" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "a[\u2028c" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1123,7 +1123,7 @@ public void Test_match__escaped_right_square_bracket_47( Type documentType ) "a\u2028c", "a]c" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1131,7 +1131,7 @@ public void Test_match__escaped_right_square_bracket_47( Type documentType ) "a.c", "a]c" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1151,7 +1151,7 @@ public void Test_match__explicit_caret_48( Type documentType ) "ab", "xab" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1159,7 +1159,7 @@ public void Test_match__explicit_caret_48( Type documentType ) "abc", "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1179,14 +1179,14 @@ public void Test_match__explicit_dollar_49( Type documentType ) "ab", "abcx" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "abc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1205,7 +1205,7 @@ public void Test_search__at_the_end_50( Type documentType ) "a": "the end is ab" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1214,7 +1214,7 @@ public void Test_search__at_the_end_50( Type documentType ) "a": "the end is ab" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1233,7 +1233,7 @@ public void Test_search__double_quotes_51( Type documentType ) "a": "the end is ab" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1242,7 +1242,7 @@ public void Test_search__double_quotes_51( Type documentType ) "a": "the end is ab" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1261,7 +1261,7 @@ public void Test_search__at_the_start_52( Type documentType ) "a": "ab is at the start" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1270,7 +1270,7 @@ public void Test_search__at_the_start_52( Type documentType ) "a": "ab is at the start" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1289,7 +1289,7 @@ public void Test_search__in_the_middle_53( Type documentType ) "a": "contains two matches" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1298,7 +1298,7 @@ public void Test_search__in_the_middle_53( Type documentType ) "a": "contains two matches" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1326,7 +1326,7 @@ public void Test_search__regex_from_the_document_54( Type documentType ) {} ] } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1335,7 +1335,7 @@ public void Test_search__regex_from_the_document_54( Type documentType ) "bba", "bbab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1354,12 +1354,12 @@ public void Test_search__don_t_select_match_55( Type documentType ) "a": "contains two matches" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1378,12 +1378,12 @@ public void Test_search__not_a_match_56( Type documentType ) "a": "bc" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1402,7 +1402,7 @@ public void Test_search__select_non_match_57( Type documentType ) "a": "bc" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1411,7 +1411,7 @@ public void Test_search__select_non_match_57( Type documentType ) "a": "bc" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1430,12 +1430,12 @@ public void Test_search__non_string_first_arg_58( Type documentType ) "a": "bc" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1454,12 +1454,12 @@ public void Test_search__non_string_second_arg_59( Type documentType ) "a": "bc" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1482,7 +1482,7 @@ public void Test_search__filter__search_function__unicode_char_class__uppercase_ [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1490,7 +1490,7 @@ public void Test_search__filter__search_function__unicode_char_class__uppercase_ "Ж", "жЖ" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1512,7 +1512,7 @@ public void Test_search__filter__search_function__unicode_char_class_negated__up [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1520,7 +1520,7 @@ public void Test_search__filter__search_function__unicode_char_class_negated__up "ж", "1" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1542,14 +1542,14 @@ public void Test_search__filter__search_function__unicode__surrogate_pair_62( Ty [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "a𐄁bc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1572,7 +1572,7 @@ public void Test_search__dot_matcher_on__u2028_63( Type documentType ) [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1580,7 +1580,7 @@ public void Test_search__dot_matcher_on__u2028_63( Type documentType ) "\u2028", "\r\u2028\n" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1603,7 +1603,7 @@ public void Test_search__dot_matcher_on__u2029_64( Type documentType ) [], {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1611,7 +1611,7 @@ public void Test_search__dot_matcher_on__u2029_64( Type documentType ) "\u2029", "\r\u2029\n" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1672,7 +1672,7 @@ public void Test_search__arg_is_a_function_expression_68( Type documentType ) {} ] } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1681,7 +1681,7 @@ public void Test_search__arg_is_a_function_expression_68( Type documentType ) "bba", "bbab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1700,7 +1700,7 @@ public void Test_search__dot_in_character_class_69( Type documentType ) "x a.c y", "x axc y" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1708,7 +1708,7 @@ public void Test_search__dot_in_character_class_69( Type documentType ) "x abc y", "x a.c y" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1727,14 +1727,14 @@ public void Test_search__escaped_dot_70( Type documentType ) "x a.c y", "x axc y" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "x a.c y" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1754,14 +1754,14 @@ public void Test_search__escaped_backslash_before_dot_71( Type documentType ) "x axc y", "x a\\\u2028c y" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "x a\\\u2028c y" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1780,14 +1780,14 @@ public void Test_search__escaped_left_square_bracket_72( Type documentType ) "x a.c y", "x a[\u2028c y" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "x a[\u2028c y" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1807,7 +1807,7 @@ public void Test_search__escaped_right_square_bracket_73( Type documentType ) "x a\u2028c y", "x a]c y" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1815,7 +1815,7 @@ public void Test_search__escaped_right_square_bracket_73( Type documentType ) "x a.c y", "x a]c y" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1844,7 +1844,7 @@ public void Test_value__single_value_nodelist_74( Type documentType ) }, 4 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1856,7 +1856,7 @@ public void Test_value__single_value_nodelist_74( Type documentType ) "foo": 4 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1880,12 +1880,12 @@ public void Test_value__multi_value_nodelist_75( Type documentType ) "bar": 4 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); diff --git a/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs index 231296dd..ae7d8bb8 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs @@ -21,14 +21,14 @@ public void Test_first_element_1( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "first" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -46,14 +46,14 @@ public void Test_second_element_2( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "second" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -71,12 +71,12 @@ public void Test_out_of_bound_3( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -116,14 +116,14 @@ public void Test_negative_6( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "second" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -141,14 +141,14 @@ public void Test_more_negative_7( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "first" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -166,12 +166,12 @@ public void Test_negative_out_of_bound_8( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -188,12 +188,12 @@ public void Test_on_object_9( Type documentType ) { "foo": 1 } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); diff --git a/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs index b56cca90..ef0ecc61 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs @@ -21,14 +21,14 @@ public void Test_double_quotes_1( Type documentType ) "a": "A", "b": "B" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -46,12 +46,12 @@ public void Test_double_quotes__absent_data_2( Type documentType ) "a": "A", "b": "B" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -69,12 +69,12 @@ public void Test_double_quotes__array_data_3( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -443,14 +443,14 @@ public void Test_double_quotes__embedded_U_0020_36( Type documentType ) { " ": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -467,14 +467,14 @@ public void Test_double_quotes__escaped_double_quote_37( Type documentType ) { "\"": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -491,14 +491,14 @@ public void Test_double_quotes__escaped_reverse_solidus_38( Type documentType ) { "\\": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -515,14 +515,14 @@ public void Test_double_quotes__escaped_solidus_39( Type documentType ) { "/": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -539,14 +539,14 @@ public void Test_double_quotes__escaped_backspace_40( Type documentType ) { "\b": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -563,14 +563,14 @@ public void Test_double_quotes__escaped_form_feed_41( Type documentType ) { "\f": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -587,14 +587,14 @@ public void Test_double_quotes__escaped_line_feed_42( Type documentType ) { "\n": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -611,14 +611,14 @@ public void Test_double_quotes__escaped_carriage_return_43( Type documentType ) { "\r": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -635,14 +635,14 @@ public void Test_double_quotes__escaped_tab_44( Type documentType ) { "\t": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -659,14 +659,14 @@ public void Test_double_quotes__escaped____upper_case_hex_45( Type documentType { "☺": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -683,14 +683,14 @@ public void Test_double_quotes__escaped____lower_case_hex_46( Type documentType { "☺": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -707,14 +707,14 @@ public void Test_double_quotes__surrogate_pair____47( Type documentType ) { "𝄞": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -731,14 +731,14 @@ public void Test_double_quotes__surrogate_pair____48( Type documentType ) { "😀": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -789,14 +789,14 @@ public void Test_single_quotes_52( Type documentType ) "a": "A", "b": "B" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -814,12 +814,12 @@ public void Test_single_quotes__absent_data_53( Type documentType ) "a": "A", "b": "B" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -837,12 +837,12 @@ public void Test_single_quotes__array_data_54( Type documentType ) "first", "second" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1211,14 +1211,14 @@ public void Test_single_quotes__embedded_U_0020_87( Type documentType ) { " ": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1235,14 +1235,14 @@ public void Test_single_quotes__escaped_single_quote_88( Type documentType ) { "'": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1259,14 +1259,14 @@ public void Test_single_quotes__escaped_reverse_solidus_89( Type documentType ) { "\\": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1283,14 +1283,14 @@ public void Test_single_quotes__escaped_solidus_90( Type documentType ) { "/": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1307,14 +1307,14 @@ public void Test_single_quotes__escaped_backspace_91( Type documentType ) { "\b": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1331,14 +1331,14 @@ public void Test_single_quotes__escaped_form_feed_92( Type documentType ) { "\f": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1355,14 +1355,14 @@ public void Test_single_quotes__escaped_line_feed_93( Type documentType ) { "\n": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1379,14 +1379,14 @@ public void Test_single_quotes__escaped_carriage_return_94( Type documentType ) { "\r": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1403,14 +1403,14 @@ public void Test_single_quotes__escaped_tab_95( Type documentType ) { "\t": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1427,14 +1427,14 @@ public void Test_single_quotes__escaped____upper_case_hex_96( Type documentType { "☺": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1451,14 +1451,14 @@ public void Test_single_quotes__escaped____lower_case_hex_97( Type documentType { "☺": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1475,14 +1475,14 @@ public void Test_single_quotes__surrogate_pair____98( Type documentType ) { "𝄞": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1499,14 +1499,14 @@ public void Test_single_quotes__surrogate_pair____99( Type documentType ) { "😀": "A" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "A" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1558,14 +1558,14 @@ public void Test_double_quotes__empty_103( Type documentType ) "b": "B", "": "C" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "C" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1584,14 +1584,14 @@ public void Test_single_quotes__empty_104( Type documentType ) "b": "B", "": "C" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "C" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); diff --git a/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs index 45ad0f96..6c26723e 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs @@ -29,7 +29,7 @@ public void Test_slice_selector_1( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -37,7 +37,7 @@ public void Test_slice_selector_1( Type documentType ) 1, 2 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -63,7 +63,7 @@ public void Test_slice_selector_with_step_2( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -72,7 +72,7 @@ public void Test_slice_selector_with_step_2( Type documentType ) 3, 5 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -92,7 +92,7 @@ public void Test_slice_selector_with_everything_omitted__short_form_3( Type docu 2, 3 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -102,7 +102,7 @@ public void Test_slice_selector_with_everything_omitted__short_form_3( Type docu 2, 3 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -122,7 +122,7 @@ public void Test_slice_selector_with_everything_omitted__long_form_4( Type docum 2, 3 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -132,7 +132,7 @@ public void Test_slice_selector_with_everything_omitted__long_form_4( Type docum 2, 3 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -158,7 +158,7 @@ public void Test_slice_selector_with_start_omitted_5( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -166,7 +166,7 @@ public void Test_slice_selector_with_start_omitted_5( Type documentType ) 0, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -192,7 +192,7 @@ public void Test_slice_selector_with_start_and_end_omitted_6( Type documentType 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -203,7 +203,7 @@ public void Test_slice_selector_with_start_and_end_omitted_6( Type documentType 6, 8 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -223,7 +223,7 @@ public void Test_negative_step_with_default_start_and_end_7( Type documentType ) 2, 3 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -233,7 +233,7 @@ public void Test_negative_step_with_default_start_and_end_7( Type documentType ) 1, 0 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -253,7 +253,7 @@ public void Test_negative_step_with_default_start_8( Type documentType ) 2, 3 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -262,7 +262,7 @@ public void Test_negative_step_with_default_start_8( Type documentType ) 2, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -282,7 +282,7 @@ public void Test_negative_step_with_default_end_9( Type documentType ) 2, 3 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -291,7 +291,7 @@ public void Test_negative_step_with_default_end_9( Type documentType ) 1, 0 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -311,7 +311,7 @@ public void Test_larger_negative_step_10( Type documentType ) 2, 3 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -319,7 +319,7 @@ public void Test_larger_negative_step_10( Type documentType ) 3, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -345,12 +345,12 @@ public void Test_negative_range_with_default_step_11( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -376,7 +376,7 @@ public void Test_negative_range_with_negative_step_12( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -384,7 +384,7 @@ public void Test_negative_range_with_negative_step_12( Type documentType ) 9, 8 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -410,7 +410,7 @@ public void Test_negative_range_with_larger_negative_step_13( Type documentType 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -419,7 +419,7 @@ public void Test_negative_range_with_larger_negative_step_13( Type documentType 7, 5 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -445,7 +445,7 @@ public void Test_larger_negative_range_with_larger_negative_step_14( Type docume 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -454,7 +454,7 @@ public void Test_larger_negative_range_with_larger_negative_step_14( Type docume 7, 5 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -480,7 +480,7 @@ public void Test_negative_from__positive_to_15( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -488,7 +488,7 @@ public void Test_negative_from__positive_to_15( Type documentType ) 5, 6 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -514,7 +514,7 @@ public void Test_negative_from_16( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -522,7 +522,7 @@ public void Test_negative_from_16( Type documentType ) 8, 9 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -548,7 +548,7 @@ public void Test_positive_from__negative_to_17( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -562,7 +562,7 @@ public void Test_positive_from__negative_to_17( Type documentType ) 7, 8 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -588,7 +588,7 @@ public void Test_negative_from__positive_to__negative_step_18( Type documentType 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -602,7 +602,7 @@ public void Test_negative_from__positive_to__negative_step_18( Type documentType 3, 2 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -628,7 +628,7 @@ public void Test_positive_from__negative_to__negative_step_19( Type documentType 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -636,7 +636,7 @@ public void Test_positive_from__negative_to__negative_step_19( Type documentType 7, 6 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -684,12 +684,12 @@ public void Test_zero_step_22( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -715,12 +715,12 @@ public void Test_empty_range_23( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -735,12 +735,12 @@ public void Test_slice_selector_with_everything_omitted_with_empty_array_24( Typ var document = TestHelper.Parse( documentType, """ [] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -755,12 +755,12 @@ public void Test_negative_step_with_empty_array_25( Type documentType ) var document = TestHelper.Parse( documentType, """ [] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -786,7 +786,7 @@ public void Test_maximal_range_with_positive_step_26( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -802,7 +802,7 @@ public void Test_maximal_range_with_positive_step_26( Type documentType ) 8, 9 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -828,7 +828,7 @@ public void Test_maximal_range_with_negative_step_27( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -843,7 +843,7 @@ public void Test_maximal_range_with_negative_step_27( Type documentType ) 2, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -869,7 +869,7 @@ public void Test_excessively_large_to_value_28( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -883,7 +883,7 @@ public void Test_excessively_large_to_value_28( Type documentType ) 8, 9 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -909,14 +909,14 @@ public void Test_excessively_small_from_value_29( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ 0 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -942,7 +942,7 @@ public void Test_excessively_large_from_value_with_negative_step_30( Type docume 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -957,7 +957,7 @@ public void Test_excessively_large_from_value_with_negative_step_30( Type docume 2, 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -983,7 +983,7 @@ public void Test_excessively_small_to_value_with_negative_step_31( Type document 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -993,7 +993,7 @@ public void Test_excessively_small_to_value_with_negative_step_31( Type document 1, 0 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1019,14 +1019,14 @@ public void Test_excessively_large_step_32( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ 1 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1052,14 +1052,14 @@ public void Test_excessively_small_step_33( Type documentType ) 8, 9 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ 9 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); diff --git a/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs index 98503bad..07f3d761 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs @@ -27,7 +27,7 @@ public void Test_filter__space_between_question_mark_and_expression_1( Type docu "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -37,7 +37,7 @@ public void Test_filter__space_between_question_mark_and_expression_1( Type docu "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -61,7 +61,7 @@ public void Test_filter__newline_between_question_mark_and_expression_2( Type do "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -71,7 +71,7 @@ public void Test_filter__newline_between_question_mark_and_expression_2( Type do "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -95,7 +95,7 @@ public void Test_filter__tab_between_question_mark_and_expression_3( Type docume "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -105,7 +105,7 @@ public void Test_filter__tab_between_question_mark_and_expression_3( Type docume "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -129,7 +129,7 @@ public void Test_filter__return_between_question_mark_and_expression_4( Type doc "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -139,7 +139,7 @@ public void Test_filter__return_between_question_mark_and_expression_4( Type doc "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -163,7 +163,7 @@ public void Test_filter__space_between_question_mark_and_parenthesized_expressio "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -173,7 +173,7 @@ public void Test_filter__space_between_question_mark_and_parenthesized_expressio "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -197,7 +197,7 @@ public void Test_filter__newline_between_question_mark_and_parenthesized_express "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -207,7 +207,7 @@ public void Test_filter__newline_between_question_mark_and_parenthesized_express "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -231,7 +231,7 @@ public void Test_filter__tab_between_question_mark_and_parenthesized_expression_ "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -241,7 +241,7 @@ public void Test_filter__tab_between_question_mark_and_parenthesized_expression_ "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -265,7 +265,7 @@ public void Test_filter__return_between_question_mark_and_parenthesized_expressi "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -275,7 +275,7 @@ public void Test_filter__return_between_question_mark_and_parenthesized_expressi "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -299,7 +299,7 @@ public void Test_filter__space_between_parenthesized_expression_and_bracket_9( T "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -309,7 +309,7 @@ public void Test_filter__space_between_parenthesized_expression_and_bracket_9( T "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -333,7 +333,7 @@ public void Test_filter__newline_between_parenthesized_expression_and_bracket_10 "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -343,7 +343,7 @@ public void Test_filter__newline_between_parenthesized_expression_and_bracket_10 "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -367,7 +367,7 @@ public void Test_filter__tab_between_parenthesized_expression_and_bracket_11( Ty "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -377,7 +377,7 @@ public void Test_filter__tab_between_parenthesized_expression_and_bracket_11( Ty "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -401,7 +401,7 @@ public void Test_filter__return_between_parenthesized_expression_and_bracket_12( "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -411,7 +411,7 @@ public void Test_filter__return_between_parenthesized_expression_and_bracket_12( "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -435,7 +435,7 @@ public void Test_filter__space_between_bracket_and_question_mark_13( Type docume "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -445,7 +445,7 @@ public void Test_filter__space_between_bracket_and_question_mark_13( Type docume "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -469,7 +469,7 @@ public void Test_filter__newline_between_bracket_and_question_mark_14( Type docu "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -479,7 +479,7 @@ public void Test_filter__newline_between_bracket_and_question_mark_14( Type docu "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -503,7 +503,7 @@ public void Test_filter__tab_between_bracket_and_question_mark_15( Type document "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -513,7 +513,7 @@ public void Test_filter__tab_between_bracket_and_question_mark_15( Type document "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -537,7 +537,7 @@ public void Test_filter__return_between_bracket_and_question_mark_16( Type docum "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -547,7 +547,7 @@ public void Test_filter__return_between_bracket_and_question_mark_16( Type docum "d": "e" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -617,7 +617,7 @@ public void Test_functions__space_between_parenthesis_and_arg_21( Type documentT "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -629,7 +629,7 @@ public void Test_functions__space_between_parenthesis_and_arg_21( Type documentT "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -655,7 +655,7 @@ public void Test_functions__newline_between_parenthesis_and_arg_22( Type documen "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -667,7 +667,7 @@ public void Test_functions__newline_between_parenthesis_and_arg_22( Type documen "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -693,7 +693,7 @@ public void Test_functions__tab_between_parenthesis_and_arg_23( Type documentTyp "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -705,7 +705,7 @@ public void Test_functions__tab_between_parenthesis_and_arg_23( Type documentTyp "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -731,7 +731,7 @@ public void Test_functions__return_between_parenthesis_and_arg_24( Type document "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -743,7 +743,7 @@ public void Test_functions__return_between_parenthesis_and_arg_24( Type document "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -761,14 +761,14 @@ public void Test_functions__space_between_arg_and_comma_25( Type documentType ) "foo", "123" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -786,14 +786,14 @@ public void Test_functions__newline_between_arg_and_comma_26( Type documentType "foo", "123" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -811,14 +811,14 @@ public void Test_functions__tab_between_arg_and_comma_27( Type documentType ) "foo", "123" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -836,14 +836,14 @@ public void Test_functions__return_between_arg_and_comma_28( Type documentType ) "foo", "123" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -861,14 +861,14 @@ public void Test_functions__space_between_comma_and_arg_29( Type documentType ) "foo", "123" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -886,14 +886,14 @@ public void Test_functions__newline_between_comma_and_arg_30( Type documentType "foo", "123" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -911,14 +911,14 @@ public void Test_functions__tab_between_comma_and_arg_31( Type documentType ) "foo", "123" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -936,14 +936,14 @@ public void Test_functions__return_between_comma_and_arg_32( Type documentType ) "foo", "123" ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -969,7 +969,7 @@ public void Test_functions__space_between_arg_and_parenthesis_33( Type documentT "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -981,7 +981,7 @@ public void Test_functions__space_between_arg_and_parenthesis_33( Type documentT "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1007,7 +1007,7 @@ public void Test_functions__newline_between_arg_and_parenthesis_34( Type documen "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1019,7 +1019,7 @@ public void Test_functions__newline_between_arg_and_parenthesis_34( Type documen "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1045,7 +1045,7 @@ public void Test_functions__tab_between_arg_and_parenthesis_35( Type documentTyp "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1057,7 +1057,7 @@ public void Test_functions__tab_between_arg_and_parenthesis_35( Type documentTyp "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1083,7 +1083,7 @@ public void Test_functions__return_between_arg_and_parenthesis_36( Type document "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1095,7 +1095,7 @@ public void Test_functions__return_between_arg_and_parenthesis_36( Type document "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1117,7 +1117,7 @@ public void Test_functions__spaces_in_a_relative_singular_selector_37( Type docu }, {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1128,7 +1128,7 @@ public void Test_functions__spaces_in_a_relative_singular_selector_37( Type docu } } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1150,7 +1150,7 @@ public void Test_functions__newlines_in_a_relative_singular_selector_38( Type do }, {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1161,7 +1161,7 @@ public void Test_functions__newlines_in_a_relative_singular_selector_38( Type do } } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1183,7 +1183,7 @@ public void Test_functions__tabs_in_a_relative_singular_selector_39( Type docume }, {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1194,7 +1194,7 @@ public void Test_functions__tabs_in_a_relative_singular_selector_39( Type docume } } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1216,7 +1216,7 @@ public void Test_functions__returns_in_a_relative_singular_selector_40( Type doc }, {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1227,7 +1227,7 @@ public void Test_functions__returns_in_a_relative_singular_selector_40( Type doc } } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1247,14 +1247,14 @@ public void Test_functions__spaces_in_an_absolute_singular_selector_41( Type doc }, {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1274,14 +1274,14 @@ public void Test_functions__newlines_in_an_absolute_singular_selector_42( Type d }, {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1301,14 +1301,14 @@ public void Test_functions__tabs_in_an_absolute_singular_selector_43( Type docum }, {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1328,14 +1328,14 @@ public void Test_functions__returns_in_an_absolute_singular_selector_44( Type do }, {} ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "foo" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1360,7 +1360,7 @@ public void Test_operators__space_before____45( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1372,7 +1372,7 @@ public void Test_operators__space_before____45( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1397,7 +1397,7 @@ public void Test_operators__newline_before____46( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1409,7 +1409,7 @@ public void Test_operators__newline_before____46( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1434,7 +1434,7 @@ public void Test_operators__tab_before____47( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1446,7 +1446,7 @@ public void Test_operators__tab_before____47( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1471,7 +1471,7 @@ public void Test_operators__return_before____48( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1483,7 +1483,7 @@ public void Test_operators__return_before____48( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1508,7 +1508,7 @@ public void Test_operators__space_after____49( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1520,7 +1520,7 @@ public void Test_operators__space_after____49( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1545,7 +1545,7 @@ public void Test_operators__newline_after____50( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1557,7 +1557,7 @@ public void Test_operators__newline_after____50( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1582,7 +1582,7 @@ public void Test_operators__tab_after____51( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1594,7 +1594,7 @@ public void Test_operators__tab_after____51( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1619,7 +1619,7 @@ public void Test_operators__return_after____52( Type documentType ) "c": 3 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1631,7 +1631,7 @@ public void Test_operators__return_after____52( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1657,7 +1657,7 @@ public void Test_operators__space_before____53( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1667,7 +1667,7 @@ public void Test_operators__space_before____53( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1693,7 +1693,7 @@ public void Test_operators__newline_before____54( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1703,7 +1703,7 @@ public void Test_operators__newline_before____54( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1729,7 +1729,7 @@ public void Test_operators__tab_before____55( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1739,7 +1739,7 @@ public void Test_operators__tab_before____55( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1765,7 +1765,7 @@ public void Test_operators__return_before____56( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1775,7 +1775,7 @@ public void Test_operators__return_before____56( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1801,7 +1801,7 @@ public void Test_operators__space_after____57( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1811,7 +1811,7 @@ public void Test_operators__space_after____57( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1837,7 +1837,7 @@ public void Test_operators__newline_after____58( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1847,7 +1847,7 @@ public void Test_operators__newline_after____58( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1873,7 +1873,7 @@ public void Test_operators__tab_after____59( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1883,7 +1883,7 @@ public void Test_operators__tab_after____59( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1909,7 +1909,7 @@ public void Test_operators__return_after____60( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1919,7 +1919,7 @@ public void Test_operators__return_after____60( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1943,7 +1943,7 @@ public void Test_operators__space_before____61( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1953,7 +1953,7 @@ public void Test_operators__space_before____61( Type documentType ) "b": 1 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -1977,7 +1977,7 @@ public void Test_operators__newline_before____62( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -1987,7 +1987,7 @@ public void Test_operators__newline_before____62( Type documentType ) "b": 1 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2011,7 +2011,7 @@ public void Test_operators__tab_before____63( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2021,7 +2021,7 @@ public void Test_operators__tab_before____63( Type documentType ) "b": 1 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2045,7 +2045,7 @@ public void Test_operators__return_before____64( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2055,7 +2055,7 @@ public void Test_operators__return_before____64( Type documentType ) "b": 1 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2079,7 +2079,7 @@ public void Test_operators__space_after____65( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2089,7 +2089,7 @@ public void Test_operators__space_after____65( Type documentType ) "b": 1 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2113,7 +2113,7 @@ public void Test_operators__newline_after____66( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2123,7 +2123,7 @@ public void Test_operators__newline_after____66( Type documentType ) "b": 1 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2147,7 +2147,7 @@ public void Test_operators__tab_after____67( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2157,7 +2157,7 @@ public void Test_operators__tab_after____67( Type documentType ) "b": 1 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2181,7 +2181,7 @@ public void Test_operators__return_after____68( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2191,7 +2191,7 @@ public void Test_operators__return_after____68( Type documentType ) "b": 1 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2215,7 +2215,7 @@ public void Test_operators__space_before____69( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2225,7 +2225,7 @@ public void Test_operators__space_before____69( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2249,7 +2249,7 @@ public void Test_operators__newline_before____70( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2259,7 +2259,7 @@ public void Test_operators__newline_before____70( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2283,7 +2283,7 @@ public void Test_operators__tab_before____71( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2293,7 +2293,7 @@ public void Test_operators__tab_before____71( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2317,7 +2317,7 @@ public void Test_operators__return_before____72( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2327,7 +2327,7 @@ public void Test_operators__return_before____72( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2351,7 +2351,7 @@ public void Test_operators__space_after____73( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2361,7 +2361,7 @@ public void Test_operators__space_after____73( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2385,7 +2385,7 @@ public void Test_operators__newline_after____74( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2395,7 +2395,7 @@ public void Test_operators__newline_after____74( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2419,7 +2419,7 @@ public void Test_operators__tab_after____75( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2429,7 +2429,7 @@ public void Test_operators__tab_after____75( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2453,7 +2453,7 @@ public void Test_operators__return_after____76( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2463,7 +2463,7 @@ public void Test_operators__return_after____76( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2487,7 +2487,7 @@ public void Test_operators__space_before___77( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2497,7 +2497,7 @@ public void Test_operators__space_before___77( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2521,7 +2521,7 @@ public void Test_operators__newline_before___78( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2531,7 +2531,7 @@ public void Test_operators__newline_before___78( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2555,7 +2555,7 @@ public void Test_operators__tab_before___79( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2565,7 +2565,7 @@ public void Test_operators__tab_before___79( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2589,7 +2589,7 @@ public void Test_operators__return_before___80( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2599,7 +2599,7 @@ public void Test_operators__return_before___80( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2623,7 +2623,7 @@ public void Test_operators__space_after___81( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2633,7 +2633,7 @@ public void Test_operators__space_after___81( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2657,7 +2657,7 @@ public void Test_operators__newline_after___82( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2667,7 +2667,7 @@ public void Test_operators__newline_after___82( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2691,7 +2691,7 @@ public void Test_operators__tab_after___83( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2701,7 +2701,7 @@ public void Test_operators__tab_after___83( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2725,7 +2725,7 @@ public void Test_operators__return_after___84( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2735,7 +2735,7 @@ public void Test_operators__return_after___84( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2759,7 +2759,7 @@ public void Test_operators__space_before___85( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2769,7 +2769,7 @@ public void Test_operators__space_before___85( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2793,7 +2793,7 @@ public void Test_operators__newline_before___86( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2803,7 +2803,7 @@ public void Test_operators__newline_before___86( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2827,7 +2827,7 @@ public void Test_operators__tab_before___87( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2837,7 +2837,7 @@ public void Test_operators__tab_before___87( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2861,7 +2861,7 @@ public void Test_operators__return_before___88( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2871,7 +2871,7 @@ public void Test_operators__return_before___88( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2895,7 +2895,7 @@ public void Test_operators__space_after___89( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2905,7 +2905,7 @@ public void Test_operators__space_after___89( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2929,7 +2929,7 @@ public void Test_operators__newline_after___90( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2939,7 +2939,7 @@ public void Test_operators__newline_after___90( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2963,7 +2963,7 @@ public void Test_operators__tab_after___91( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -2973,7 +2973,7 @@ public void Test_operators__tab_after___91( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -2997,7 +2997,7 @@ public void Test_operators__return_after___92( Type documentType ) "b": 2 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3007,7 +3007,7 @@ public void Test_operators__return_after___92( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3035,7 +3035,7 @@ public void Test_operators__space_before____93( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3049,7 +3049,7 @@ public void Test_operators__space_before____93( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3077,7 +3077,7 @@ public void Test_operators__newline_before____94( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3091,7 +3091,7 @@ public void Test_operators__newline_before____94( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3119,7 +3119,7 @@ public void Test_operators__tab_before____95( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3133,7 +3133,7 @@ public void Test_operators__tab_before____95( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3161,7 +3161,7 @@ public void Test_operators__return_before____96( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3175,7 +3175,7 @@ public void Test_operators__return_before____96( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3203,7 +3203,7 @@ public void Test_operators__space_after____97( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3217,7 +3217,7 @@ public void Test_operators__space_after____97( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3245,7 +3245,7 @@ public void Test_operators__newline_after____98( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3259,7 +3259,7 @@ public void Test_operators__newline_after____98( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3287,7 +3287,7 @@ public void Test_operators__tab_after____99( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3301,7 +3301,7 @@ public void Test_operators__tab_after____99( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3329,7 +3329,7 @@ public void Test_operators__return_after____100( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3343,7 +3343,7 @@ public void Test_operators__return_after____100( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3371,7 +3371,7 @@ public void Test_operators__space_before____101( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3385,7 +3385,7 @@ public void Test_operators__space_before____101( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3413,7 +3413,7 @@ public void Test_operators__newline_before____102( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3427,7 +3427,7 @@ public void Test_operators__newline_before____102( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3455,7 +3455,7 @@ public void Test_operators__tab_before____103( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3469,7 +3469,7 @@ public void Test_operators__tab_before____103( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3497,7 +3497,7 @@ public void Test_operators__return_before____104( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3511,7 +3511,7 @@ public void Test_operators__return_before____104( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3539,7 +3539,7 @@ public void Test_operators__space_after____105( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3553,7 +3553,7 @@ public void Test_operators__space_after____105( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3581,7 +3581,7 @@ public void Test_operators__newline_after____106( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3595,7 +3595,7 @@ public void Test_operators__newline_after____106( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3623,7 +3623,7 @@ public void Test_operators__tab_after____107( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3637,7 +3637,7 @@ public void Test_operators__tab_after____107( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3665,7 +3665,7 @@ public void Test_operators__return_after____108( Type documentType ) "b": 1 } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3679,7 +3679,7 @@ public void Test_operators__return_after____108( Type documentType ) "b": 2 } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3706,7 +3706,7 @@ public void Test_operators__space_between_logical_not_and_test_expression_109( T "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3715,7 +3715,7 @@ public void Test_operators__space_between_logical_not_and_test_expression_109( T "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3742,7 +3742,7 @@ public void Test_operators__newline_between_logical_not_and_test_expression_110( "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3751,7 +3751,7 @@ public void Test_operators__newline_between_logical_not_and_test_expression_110( "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3778,7 +3778,7 @@ public void Test_operators__tab_between_logical_not_and_test_expression_111( Typ "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3787,7 +3787,7 @@ public void Test_operators__tab_between_logical_not_and_test_expression_111( Typ "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3814,7 +3814,7 @@ public void Test_operators__return_between_logical_not_and_test_expression_112( "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3823,7 +3823,7 @@ public void Test_operators__return_between_logical_not_and_test_expression_112( "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3851,7 +3851,7 @@ public void Test_operators__space_between_logical_not_and_parenthesized_expressi "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3865,7 +3865,7 @@ public void Test_operators__space_between_logical_not_and_parenthesized_expressi "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3893,7 +3893,7 @@ public void Test_operators__newline_between_logical_not_and_parenthesized_expres "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3907,7 +3907,7 @@ public void Test_operators__newline_between_logical_not_and_parenthesized_expres "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3935,7 +3935,7 @@ public void Test_operators__tab_between_logical_not_and_parenthesized_expression "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3949,7 +3949,7 @@ public void Test_operators__tab_between_logical_not_and_parenthesized_expression "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -3977,7 +3977,7 @@ public void Test_operators__return_between_logical_not_and_parenthesized_express "d": "f" } ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -3991,7 +3991,7 @@ public void Test_operators__return_between_logical_not_and_parenthesized_express "d": "f" } ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4008,14 +4008,14 @@ public void Test_selectors__space_between_root_and_bracket_117( Type documentTyp { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4032,14 +4032,14 @@ public void Test_selectors__newline_between_root_and_bracket_118( Type documentT { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4056,14 +4056,14 @@ public void Test_selectors__tab_between_root_and_bracket_119( Type documentType { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4080,14 +4080,14 @@ public void Test_selectors__return_between_root_and_bracket_120( Type documentTy { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4106,14 +4106,14 @@ public void Test_selectors__space_between_bracket_and_bracket_121( Type document "b": "ab" } } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4132,14 +4132,14 @@ public void Test_selectors__newline_between_root_and_bracket_122( Type documentT "b": "ab" } } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4158,14 +4158,14 @@ public void Test_selectors__tab_between_root_and_bracket_123( Type documentType "b": "ab" } } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4184,14 +4184,14 @@ public void Test_selectors__return_between_root_and_bracket_124( Type documentTy "b": "ab" } } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4208,14 +4208,14 @@ public void Test_selectors__space_between_root_and_dot_125( Type documentType ) { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4232,14 +4232,14 @@ public void Test_selectors__newline_between_root_and_dot_126( Type documentType { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4256,14 +4256,14 @@ public void Test_selectors__tab_between_root_and_dot_127( Type documentType ) { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4280,14 +4280,14 @@ public void Test_selectors__return_between_root_and_dot_128( Type documentType ) { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4392,14 +4392,14 @@ public void Test_selectors__space_between_bracket_and_selector_137( Type documen { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4416,14 +4416,14 @@ public void Test_selectors__newline_between_bracket_and_selector_138( Type docum { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4440,14 +4440,14 @@ public void Test_selectors__tab_between_bracket_and_selector_139( Type documentT { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4464,14 +4464,14 @@ public void Test_selectors__return_between_bracket_and_selector_140( Type docume { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4488,14 +4488,14 @@ public void Test_selectors__space_between_selector_and_bracket_141( Type documen { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4512,14 +4512,14 @@ public void Test_selectors__newline_between_selector_and_bracket_142( Type docum { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4536,14 +4536,14 @@ public void Test_selectors__tab_between_selector_and_bracket_143( Type documentT { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4560,14 +4560,14 @@ public void Test_selectors__return_between_selector_and_bracket_144( Type docume { "a": "ab" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ [ "ab" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4585,7 +4585,7 @@ public void Test_selectors__space_between_selector_and_comma_145( Type documentT "a": "ab", "b": "bc" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4593,7 +4593,7 @@ public void Test_selectors__space_between_selector_and_comma_145( Type documentT "ab", "bc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4611,7 +4611,7 @@ public void Test_selectors__newline_between_selector_and_comma_146( Type documen "a": "ab", "b": "bc" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4619,7 +4619,7 @@ public void Test_selectors__newline_between_selector_and_comma_146( Type documen "ab", "bc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4637,7 +4637,7 @@ public void Test_selectors__tab_between_selector_and_comma_147( Type documentTyp "a": "ab", "b": "bc" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4645,7 +4645,7 @@ public void Test_selectors__tab_between_selector_and_comma_147( Type documentTyp "ab", "bc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4663,7 +4663,7 @@ public void Test_selectors__return_between_selector_and_comma_148( Type document "a": "ab", "b": "bc" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4671,7 +4671,7 @@ public void Test_selectors__return_between_selector_and_comma_148( Type document "ab", "bc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4689,7 +4689,7 @@ public void Test_selectors__space_between_comma_and_selector_149( Type documentT "a": "ab", "b": "bc" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4697,7 +4697,7 @@ public void Test_selectors__space_between_comma_and_selector_149( Type documentT "ab", "bc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4715,7 +4715,7 @@ public void Test_selectors__newline_between_comma_and_selector_150( Type documen "a": "ab", "b": "bc" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4723,7 +4723,7 @@ public void Test_selectors__newline_between_comma_and_selector_150( Type documen "ab", "bc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4741,7 +4741,7 @@ public void Test_selectors__tab_between_comma_and_selector_151( Type documentTyp "a": "ab", "b": "bc" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4749,7 +4749,7 @@ public void Test_selectors__tab_between_comma_and_selector_151( Type documentTyp "ab", "bc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4767,7 +4767,7 @@ public void Test_selectors__return_between_comma_and_selector_152( Type document "a": "ab", "b": "bc" } - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4775,7 +4775,7 @@ public void Test_selectors__return_between_comma_and_selector_152( Type document "ab", "bc" ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4797,7 +4797,7 @@ public void Test_slice__space_between_start_and_colon_153( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4805,7 +4805,7 @@ public void Test_slice__space_between_start_and_colon_153( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4827,7 +4827,7 @@ public void Test_slice__newline_between_start_and_colon_154( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4835,7 +4835,7 @@ public void Test_slice__newline_between_start_and_colon_154( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4857,7 +4857,7 @@ public void Test_slice__tab_between_start_and_colon_155( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4865,7 +4865,7 @@ public void Test_slice__tab_between_start_and_colon_155( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4887,7 +4887,7 @@ public void Test_slice__return_between_start_and_colon_156( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4895,7 +4895,7 @@ public void Test_slice__return_between_start_and_colon_156( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4917,7 +4917,7 @@ public void Test_slice__space_between_colon_and_end_157( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4925,7 +4925,7 @@ public void Test_slice__space_between_colon_and_end_157( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4947,7 +4947,7 @@ public void Test_slice__newline_between_colon_and_end_158( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4955,7 +4955,7 @@ public void Test_slice__newline_between_colon_and_end_158( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -4977,7 +4977,7 @@ public void Test_slice__tab_between_colon_and_end_159( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -4985,7 +4985,7 @@ public void Test_slice__tab_between_colon_and_end_159( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -5007,7 +5007,7 @@ public void Test_slice__return_between_colon_and_end_160( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -5015,7 +5015,7 @@ public void Test_slice__return_between_colon_and_end_160( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -5037,7 +5037,7 @@ public void Test_slice__space_between_end_and_colon_161( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -5045,7 +5045,7 @@ public void Test_slice__space_between_end_and_colon_161( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -5067,7 +5067,7 @@ public void Test_slice__newline_between_end_and_colon_162( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -5075,7 +5075,7 @@ public void Test_slice__newline_between_end_and_colon_162( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -5097,7 +5097,7 @@ public void Test_slice__tab_between_end_and_colon_163( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -5105,7 +5105,7 @@ public void Test_slice__tab_between_end_and_colon_163( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -5127,7 +5127,7 @@ public void Test_slice__return_between_end_and_colon_164( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -5135,7 +5135,7 @@ public void Test_slice__return_between_end_and_colon_164( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -5157,7 +5157,7 @@ public void Test_slice__space_between_colon_and_step_165( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -5165,7 +5165,7 @@ public void Test_slice__space_between_colon_and_step_165( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -5187,7 +5187,7 @@ public void Test_slice__newline_between_colon_and_step_166( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -5195,7 +5195,7 @@ public void Test_slice__newline_between_colon_and_step_166( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -5217,7 +5217,7 @@ public void Test_slice__tab_between_colon_and_step_167( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -5225,7 +5225,7 @@ public void Test_slice__tab_between_colon_and_step_167( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); @@ -5247,7 +5247,7 @@ public void Test_slice__return_between_colon_and_step_168( Type documentType ) 5, 6 ] - """ ); + """ ); var results = document.Select( selector ); var expect = TestHelper.Parse( documentType, """ @@ -5255,7 +5255,7 @@ public void Test_slice__return_between_colon_and_step_168( Type documentType ) 2, 4 ] - """ ).Root; + """ ).Root; var match = TestHelper.MatchOne( documentType, results, expect ); Assert.IsTrue( match ); diff --git a/test/Hyperbee.Json.Cts/generate_tests.ps1 b/test/Hyperbee.Json.Cts/generate_tests.ps1 index edbe38da..4a91ced8 100644 --- a/test/Hyperbee.Json.Cts/generate_tests.ps1 +++ b/test/Hyperbee.Json.Cts/generate_tests.ps1 @@ -106,10 +106,40 @@ function Convert-ToCSharpMethodName { return $name -replace '[^a-zA-Z0-9]', '_' } +# function FormatJson { +# param ( +# [string]$json, +# [int]$indent +# ) + +# # Ignore empty groups +# if ([string]::IsNullOrWhiteSpace($json)) { +# return $null +# } + +# # Detect the line break format +# $lineBreak = if ($json -contains "`r`n") { "`r`n" } else { "`n" } + +# # Split the JSON string into lines +# $lines = $json -split $lineBreak + +# # Create the indentation string +# $indentation = " " * $indent + +# # Add indentation to each line except the first +# $formattedLines = $lines | ForEach-Object { $indentation + $_ } + +# # Join the lines back into a single string with the detected line break format +# $formattedJson = $lineBreak + ($formattedLines -join $lineBreak) + $lineBreak + $indentation + +# return $formattedJson +# } + function FormatJson { param ( [string]$json, - [int]$indent + [int]$indentCount, + [int]$indentSize = 2 ) # Ignore empty groups @@ -123,18 +153,20 @@ function FormatJson { # Split the JSON string into lines $lines = $json -split $lineBreak - # Create the indentation string - $indentation = " " * $indent + # Create the indentation strings + $indentation = " " * ($indentCount * $indentSize) + $lastIndentation = " " * (($indentCount - 2) * $indentSize) # Add indentation to each line except the first $formattedLines = $lines | ForEach-Object { $indentation + $_ } # Join the lines back into a single string with the detected line break format - $formattedJson = $lineBreak + ($formattedLines -join $lineBreak) + $lineBreak + $indentation + $formattedJson = $lineBreak + ($formattedLines -join $lineBreak) + $lineBreak + $lastIndentation return $formattedJson } + function Convert-ToPascalCase { param ( [string]$value @@ -205,9 +237,9 @@ public class $className $invalidSelector = if ($test.invalid_selector) { $true } else { $false } - $document = FormatJson -json $test.document -indent 16 - $result = FormatJson -json $test.result -indent 16 - $results = FormatJson -json $test.results -indent 16 + $document = FormatJson -json $test.document -indentCount 8 + $result = FormatJson -json $test.result -indentCount 8 + $results = FormatJson -json $test.results -indentCount 8 # Replace placeholders in the template with actual test case data $unitTestContent += @" diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs index e8e75d89..6c0d581e 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs @@ -384,77 +384,6 @@ public void FilterExpressionWithEqualsArrayOrEqualsTrue( string query, Type sour Assert.IsTrue( expected.SequenceEqual( matches ) ); } - [DataTestMethod] - [DataRow( "$[?(@.d==['v1','v2'])]", typeof( JsonDocument ) )] - [DataRow( "$[?(@.d==['v1','v2'])]", typeof( JsonNode ) )] - [ExpectedException( typeof( NotSupportedException ) )] - public void FilterExpressionWithEqualsArrayWithSingleQuotes( string query, Type sourceType ) - { - // consensus: NOT_SUPPORTED - - var json = - """ - [ - { - "d": [ - "v1", - "v2" - ] - }, - { - "d": [ - "a", - "b" - ] - }, - { - "d": "v1" - }, - { - "d": "v2" - }, - { - "d": {} - }, - { - "d": [] - }, - { - "d": null - }, - { - "d": -1 - }, - { - "d": 0 - }, - { - "d": 1 - }, - { - "d": "['v1','v2']" - }, - { - "d": "['v1', 'v2']" - }, - { - "d": "v1,v2" - }, - { - "d": "[\"v1\", \"v2\"]" - }, - { - "d": "[\"v1\",\"v2\"]" - } - ] - - """; - - var source = GetDocumentAdapter( sourceType, json ); - - _ = source.Select( query ).ToArray(); - } - [DataTestMethod] [DataRow( "$[?((@.key<44)==false)]", typeof( JsonDocument ) )] [DataRow( "$[?((@.key<44)==false)]", typeof( JsonNode ) )] diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathInOperationsTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathInOperationsTests.cs new file mode 100644 index 00000000..b4644f37 --- /dev/null +++ b/test/Hyperbee.Json.Tests/Query/JsonPathInOperationsTests.cs @@ -0,0 +1,130 @@ +using System; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using Hyperbee.Json.Tests.TestSupport; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Hyperbee.Json.Tests.Query; + +[TestClass] +public class JsonPathInOperationsTests : JsonTestBase +{ + [DataTestMethod] + [DataRow( "$[?(@.value in [1, 42, 100])]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.value in [1, 42, 100])]", typeof( JsonNode ) )] + [DataRow( "$[?(@.value in ['a', 'b', 'c'])]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.value in ['a', 'b', 'c'])]", typeof( JsonNode ) )] + public void InOperation_SingleValue( string query, Type sourceType ) + { + const string json = + """ + [ + { + "value": 42 + }, + { + "value": "b" + } + ] + """; + var source = GetDocumentAdapter( sourceType, json ); + var expected = new[] + { + source.FromJsonPathPointer("$[0]"), + source.FromJsonPathPointer("$[1]") + }; + + var matches = source.Select( query ).ToList(); + Assert.AreEqual( expected.Length, matches.Count ); + Assert.IsTrue( expected.SequenceEqual( matches ) ); + } + + [DataTestMethod] + [DataRow( "$[?(@.values in [1, 2, 3])]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.values in [1, 2, 3])]", typeof( JsonNode ) )] + [DataRow( "$[?(@.values in ['x', 'y', 'z'])]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.values in ['x', 'y', 'z'])]", typeof( JsonNode ) )] + public void InOperation_ArrayValue( string query, Type sourceType ) + { + const string json = + """ + [ + { + "values": [1, 2, 3] + }, + { + "values": ["x", "y", "z"] + } + ] + """; + var source = GetDocumentAdapter( sourceType, json ); + var expected = new[] + { + source.FromJsonPathPointer("$[0]"), + source.FromJsonPathPointer("$[1]") + }; + + var matches = source.Select( query ).ToList(); + Assert.AreEqual( expected.Length, matches.Count ); + Assert.IsTrue( expected.SequenceEqual( matches ) ); + } + + [DataTestMethod] + [DataRow( "$[?(@.a in [1, 2, 3] && @.b in [4, 5, 6])]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.a in [1, 2, 3] && @.b in [4, 5, 6])]", typeof( JsonNode ) )] + public void InOperation_MultipleConditions( string query, Type sourceType ) + { + const string json = + """ + [ + { + "a": 1, + "b": 5 + }, + { + "a": 3, + "b": 6 + } + ] + """; + var source = GetDocumentAdapter( sourceType, json ); + var expected = new[] + { + source.FromJsonPathPointer("$[0]"), + source.FromJsonPathPointer("$[1]") + }; + + var matches = source.Select( query ).ToList(); + Assert.AreEqual( expected.Length, matches.Count ); + Assert.IsTrue( expected.SequenceEqual( matches ) ); + } + + [DataTestMethod] + [DataRow( "$[?(@.array in [1, 2, 3])]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.array in [1, 2, 3])]", typeof( JsonNode ) )] + public void InOperation_ArrayInArray( string query, Type sourceType ) + { + const string json = + """ + [ + { + "array": [1, 2] + }, + { + "array": [3, 4] + } + ] + """; + var source = GetDocumentAdapter( sourceType, json ); + var expected = new[] + { + source.FromJsonPathPointer("$[0]"), + source.FromJsonPathPointer("$[1]") + }; + + var matches = source.Select( query ).ToList(); + Assert.AreEqual( expected.Length, matches.Count ); + Assert.IsTrue( expected.SequenceEqual( matches ) ); + } +} diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathMathOperationsTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathMathOperationsTests.cs new file mode 100644 index 00000000..ced22b72 --- /dev/null +++ b/test/Hyperbee.Json.Tests/Query/JsonPathMathOperationsTests.cs @@ -0,0 +1,105 @@ +using System; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using Hyperbee.Json.Tests.TestSupport; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Hyperbee.Json.Tests.Query; + +[TestClass] +public class JsonPathMathOperationsTests : JsonTestBase +{ + [DataTestMethod] + [DataRow( "$[?(@.value + 1 == 43)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.value + 1 == 43)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.value - 1 == 41)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.value - 1 == 41)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.value * 2 == 84)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.value * 2 == 84)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.value / 2 == 21)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.value / 2 == 21)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.value % 10 == 2)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.value % 10 == 2)]", typeof( JsonNode ) )] + public void MathOperations( string query, Type sourceType ) + { + const string json = + """ + [ + { + "value": 42 + } + ] + """; + var source = GetDocumentAdapter( sourceType, json ); + var expected = new[] + { + source.FromJsonPathPointer( "$[0]" ) + }; + + var matches = source.Select( query ).ToList(); + Assert.AreEqual( 1, matches.Count ); + Assert.IsTrue( expected.SequenceEqual( matches ) ); + } + + [DataTestMethod] + [DataRow( "$[?(@.a + @.b == 3)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.a + @.b == 3)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.a - @.b == -1)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.a - @.b == -1)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.a * @.b == 2)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.a * @.b == 2)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.a / @.b == 0.5)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.a / @.b == 0.5)]", typeof( JsonNode ) )] + public void MathOperationsWithMultipleKeys( string query, Type sourceType ) + { + const string json = + """ + [ + { + "a": 1, + "b": 2 + } + ] + """; + var source = GetDocumentAdapter( sourceType, json ); + var expected = new[] + { + source.FromJsonPathPointer( "$[0]" ) + }; + + var matches = source.Select( query ).ToList(); + Assert.AreEqual( 1, matches.Count ); + Assert.IsTrue( expected.SequenceEqual( matches ) ); + } + + [DataTestMethod] + [DataRow( "$[?(@.array[0] + @.array[1] == 3)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.array[0] + @.array[1] == 3)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.array[2] - @.array[1] == 1)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.array[2] - @.array[1] == 1)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.array[1] * @.array[2] == 6)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.array[1] * @.array[2] == 6)]", typeof( JsonNode ) )] + [DataRow( "$[?(@.array[2] / @.array[0] == 3)]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.array[2] / @.array[0] == 3)]", typeof( JsonNode ) )] + public void MathOperationsInArray( string query, Type sourceType ) + { + const string json = + """ + [ + { + "array": [1, 2, 3] + } + ] + """; + var source = GetDocumentAdapter( sourceType, json ); + var expected = new[] + { + source.FromJsonPathPointer( "$[0]" ) + }; + + var matches = source.Select( query ).ToList(); + Assert.AreEqual( 1, matches.Count ); + Assert.IsTrue( expected.SequenceEqual( matches ) ); + } +} From f23d902f808d10b4533ca90d04550247e32a570f Mon Sep 17 00:00:00 2001 From: Brenton Farmer Date: Sat, 20 Jul 2024 11:47:46 -0700 Subject: [PATCH 12/18] Package tags --- src/Hyperbee.Json/Hyperbee.Json.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hyperbee.Json/Hyperbee.Json.csproj b/src/Hyperbee.Json/Hyperbee.Json.csproj index 8a3c3e16..7c5e90e2 100644 --- a/src/Hyperbee.Json/Hyperbee.Json.csproj +++ b/src/Hyperbee.Json/Hyperbee.Json.csproj @@ -6,7 +6,7 @@ Stillpoint Software, Inc. README.md - JsonPath;NET;Json + json-path;jsonpath;query;path;json;rfc9535 icon.png https://github.com/Stillpoint-Software/Hyperbee.Json/ net8.0 From ea448f51bf5c140556ae9a19db14ac52a73f3683 Mon Sep 17 00:00:00 2001 From: Brenton Farmer Date: Sat, 20 Jul 2024 11:55:46 -0700 Subject: [PATCH 13/18] Update package description --- src/Hyperbee.Json/Hyperbee.Json.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hyperbee.Json/Hyperbee.Json.csproj b/src/Hyperbee.Json/Hyperbee.Json.csproj index 7c5e90e2..c0a3e1c4 100644 --- a/src/Hyperbee.Json/Hyperbee.Json.csproj +++ b/src/Hyperbee.Json/Hyperbee.Json.csproj @@ -13,7 +13,7 @@ LICENSE Stillpoint Software, Inc. Hyperbee Json - A C# implementation of JSONPath for .NET `System.Text.Json` `JsonElement`, and `JsonNode`. + JSON Path (RFC 9535) implementation for System.Text.Json JsonElement, and JsonNode. https://github.com/Stillpoint-Software/Hyperbee.Json git https://github.com/Stillpoint-Software/Hyperbee.Json/releases/latest From 93a943fa5d193012ac90374c64b245476edfaaa7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:30:12 -0700 Subject: [PATCH 14/18] [FEATURE]: Cleanup and unit tests (#53) * Move specific node parse logic from accessor to expression factory * Renames and cleanup * Formatting and comments * Remove hard coded json types from test helpers * Improved path support in dynamic * Add CompareConstraint --------- Co-authored-by: Brenton Farmer --- README.md | 85 +++++++----- .../Element/ElementTypeDescriptor.cs | 6 +- .../Element/ElementValueAccessor.cs | 27 +--- .../Element/Functions/CountElementFunction.cs | 2 +- .../Functions/LengthElementFunction.cs | 2 +- .../Element/Functions/MatchElementFunction.cs | 2 +- .../Functions/SearchElementFunction.cs | 2 +- .../Element/Functions/ValueElementFunction.cs | 2 +- .../Element/ValueTypeExtensions.cs | 48 +++---- .../Descriptors/ITypeDescriptor.cs | 6 +- .../Descriptors/IValueAccessor.cs | 5 +- .../Node/Functions/CountNodeFunction.cs | 2 +- .../Node/Functions/LengthNodeFunction.cs | 2 +- .../Node/Functions/MatchNodeFunction.cs | 2 +- .../Node/Functions/SearchNodeFunction.cs | 2 +- .../Node/Functions/ValueNodeFunction.cs | 2 +- .../Descriptors/Node/NodeTypeDescriptor.cs | 6 +- .../Descriptors/Node/NodeValueAccessor.cs | 26 +--- .../Descriptors/Node/ValueTypeExtensions.cs | 49 +++---- .../Dynamic/DynamicJsonElement.cs | 11 +- src/Hyperbee.Json/Dynamic/DynamicJsonNode.cs | 31 ++++- .../Extensions/JsonDynamicHelper.cs | 6 +- .../Extensions/JsonNodeExtensions.cs | 32 ----- .../{FilterEvaluator.cs => FilterRuntime.cs} | 2 +- ...{IFilterEvaluator.cs => IFilterRuntime.cs} | 2 +- ...{ExtensionInfo.cs => CompareConstraint.cs} | 8 +- .../Filters/Parser/ExpressionInfo.cs | 7 - .../Filters/Parser/ExpressionKind.cs | 13 -- .../Expressions/FunctionExpressionFactory.cs | 10 +- .../Parser/Expressions/IExpressionFactory.cs | 2 +- .../Expressions/JsonExpressionFactory.cs | 44 +++++- .../Expressions/LiteralExpressionFactory.cs | 9 +- .../Parser/Expressions/MathExpression.cs | 15 +- .../Expressions/NotExpressionFactory.cs | 9 +- .../Expressions/ParenExpressionFactory.cs | 5 +- .../Expressions/SelectExpressionFactory.cs | 4 +- .../Filters/Parser/ExtensionFunction.cs | 15 +- .../Filters/Parser/FilterParser.cs | 131 +++++++----------- .../Filters/Parser/ValueTypeComparer.cs | 57 ++++---- .../Internal/JsonElementAccessor.cs | 4 +- src/Hyperbee.Json/JsonPath.cs | 11 +- src/Hyperbee.Json/JsonPathBuilder.cs | 28 +++- .../JsonPathSliceSyntaxHelper.cs | 2 +- .../{ => TestSupport}/AssertExtensions.cs | 2 +- .../TestSupport/JsonElementHelper.cs | 2 +- .../TestSupport/JsonNodeHelper.cs | 2 +- .../TestSupport/TestHelper.cs | 4 +- .../Builder/JsonPathBuilderTests.cs | 8 +- .../Dynamic/JsonDynamicTests.cs | 45 +++++- .../Extensions/JsonExtensionTests.cs | 49 ------- .../Parsers/ExtensionFunctionTests.cs | 4 +- .../Parsers/FilterParserTests.cs | 130 +++++++---------- .../Parsers/JsonPathPointerTests.cs | 55 ++++++++ .../Parsers/JsonPathQueryParserTests.cs | 76 +++++----- .../Parsers/ValueTypeComparerTests.cs | 38 ++--- .../Query/JsonPathInOperationsTests.cs | 75 +++++++--- .../TestSupport/AssertExtensions.cs | 65 +++++++++ .../TestSupport/JsonElementHelper.cs | 4 +- .../TestSupport/JsonNodeHelper.cs | 4 +- .../TestSupport/JsonTestBase.cs | 12 +- 60 files changed, 681 insertions(+), 630 deletions(-) delete mode 100644 src/Hyperbee.Json/Extensions/JsonNodeExtensions.cs rename src/Hyperbee.Json/Filters/{FilterEvaluator.cs => FilterRuntime.cs} (93%) rename src/Hyperbee.Json/Filters/{IFilterEvaluator.cs => IFilterRuntime.cs} (71%) rename src/Hyperbee.Json/Filters/Parser/{ExtensionInfo.cs => CompareConstraint.cs} (56%) delete mode 100644 src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs rename test/Hyperbee.Json.Cts/{ => TestSupport}/AssertExtensions.cs (97%) delete mode 100644 test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs create mode 100644 test/Hyperbee.Json.Tests/Parsers/JsonPathPointerTests.cs create mode 100644 test/Hyperbee.Json.Tests/TestSupport/AssertExtensions.cs diff --git a/README.md b/README.md index b0a7422c..cf79a51c 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ or bracket-notation: - 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 the `?()` syntax for filtering. +- 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: @@ -173,27 +173,36 @@ Filter expressions are supported via the syntax `?()`, as in: ### JSONPath Functions -JsonPath expressions support basic methods calls. +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)]` +| `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. | `$[?@.value in ['a', 'b', 'c'] ]` + ### JSONPath Custom Functions -You can also extend the supported function set by registering your own functions. +You can extend the supported function set by registering your own functions. **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, ExtensionInfo.MustCompare ) +public class PathNodeFunction() : ExtensionFunction( PathMethod, CompareConstraint.MustCompare ) { public const string Name = "path"; private static readonly MethodInfo PathMethod = GetMethod( nameof( Path ) ); @@ -223,7 +232,8 @@ var results = source.Select( "$..[?path(@) == '$.store.book[2].title']" ); - High Performance. - Supports both `JsonElement`, and `JsonNode`. - Deferred execution queries with `IEnumerable`. -- Extendable to support additional JSON document types and functions. +- Enhanced JsonPath syntax. +- Extendable to support additional JSON document types. - RFC conforming JSONPath implementation. ## Comparison with Other Libraries @@ -235,6 +245,7 @@ There are excellent libraries available for RFC-9535 .NET JsonPath. - **Pros:** - Comprehensive feature set. - Deferred execution queries with `IEnumerable`. + - Enhanced JsonPath syntax. - Strong community support. - **Cons:** @@ -307,37 +318,37 @@ Here is a performance comparison of various queries on the standard book store d ``` ``` -| Method | Filter | Mean | Error | StdDev | Allocated -|------------------------ |--------------------------------- |---------- |---------- |---------- |---------- -| Hyperbee_JsonElement | $..* `First()` | 3.210 us | 0.2597 us | 0.0142 us | 3.95 KB -| Hyperbee_JsonNode | $..* `First()` | 3.366 us | 2.7086 us | 0.1485 us | 3.4 KB -| JsonEverything_JsonNode | $..* `First()` | 3.408 us | 3.2650 us | 0.1790 us | 3.53 KB -| JsonCons_JsonElement | $..* `First()` | 6.090 us | 6.1994 us | 0.3398 us | 8.48 KB -| Newtonsoft_JObject | $..* `First()` | 9.366 us | 1.4505 us | 0.0795 us | 14.22 KB -| | | | | | -| JsonCons_JsonElement | $..* | 5.812 us | 1.3734 us | 0.0753 us | 8.45 KB -| Hyperbee_JsonElement | $..* | 7.929 us | 1.5446 us | 0.0847 us | 8.66 KB -| Hyperbee_JsonNode | $..* | 10.061 us | 9.3866 us | 0.5145 us | 10.58 KB -| Newtonsoft_JObject | $..* | 11.061 us | 0.9381 us | 0.0514 us | 14.86 KB -| JsonEverything_JsonNode | $..* | 23.492 us | 1.7850 us | 0.0978 us | 36.81 KB -| | | | | | -| Hyperbee_JsonElement | $..price | 4.813 us | 3.2103 us | 0.1760 us | 4.63 KB -| JsonCons_JsonElement | $..price | 5.244 us | 2.4403 us | 0.1338 us | 5.65 KB -| Hyperbee_JsonNode | $..price | 7.741 us | 6.5786 us | 0.3606 us | 7.8 KB -| Newtonsoft_JObject | $..price | 10.335 us | 7.3216 us | 0.4013 us | 14.4 KB -| JsonEverything_JsonNode | $..price | 16.982 us | 9.8753 us | 0.5413 us | 27.63 KB -| | | | | | -| Hyperbee_JsonElement | $.store.book[?(@.price == 8.99)] | 4.464 us | 2.2718 us | 0.1245 us | 5.68 KB -| JsonCons_JsonElement | $.store.book[?(@.price == 8.99)] | 5.262 us | 4.0734 us | 0.2233 us | 5.05 KB -| Hyperbee_JsonNode | $.store.book[?(@.price == 8.99)] | 7.423 us | 0.7463 us | 0.0409 us | 8.31 KB -| Newtonsoft_JObject | $.store.book[?(@.price == 8.99)] | 10.386 us | 7.7620 us | 0.4255 us | 15.84 KB -| JsonEverything_JsonNode | $.store.book[?(@.price == 8.99)] | 12.621 us | 4.5079 us | 0.2471 us | 15.85 KB -| | | | | | -| Hyperbee_JsonElement | $.store.book[0] | 2.787 us | 1.3694 us | 0.0751 us | 2.27 KB -| JsonCons_JsonElement | $.store.book[0] | 3.297 us | 0.1607 us | 0.0088 us | 3.21 KB -| Hyperbee_JsonNode | $.store.book[0] | 3.345 us | 0.5145 us | 0.0282 us | 2.77 KB -| JsonEverything_JsonNode | $.store.book[0] | 4.875 us | 2.6974 us | 0.1479 us | 5.96 KB -| Newtonsoft_JObject | $.store.book[0] | 9.134 us | 5.0882 us | 0.2789 us | 14.56 KB + Method | Filter | Mean | Error | StdDev | Allocated +--------------------------------- |--------------------------------- |-----------|------------|-----------|---------- + JsonPath_Hyperbee_JsonElement | $..* `First()` | 3.105 us | 1.6501 us | 0.0904 us | 3.52 KB + JsonPath_JsonEverything_JsonNode | $..* `First()` | 3.278 us | 3.3157 us | 0.1817 us | 3.53 KB + JsonPath_Hyperbee_JsonNode | $..* `First()` | 3.302 us | 3.2094 us | 0.1759 us | 3.09 KB + JsonPath_JsonCons_JsonElement | $..* `First()` | 6.170 us | 4.1597 us | 0.2280 us | 8.48 KB + JsonPath_Newtonsoft_JObject | $..* `First()` | 8.708 us | 8.7586 us | 0.4801 us | 14.22 KB + | | | | | + JsonPath_JsonCons_JsonElement | $..* | 5.792 us | 6.6920 us | 0.3668 us | 8.45 KB + JsonPath_Hyperbee_JsonElement | $..* | 7.504 us | 7.6479 us | 0.4192 us | 9.13 KB + JsonPath_Hyperbee_JsonNode | $..* | 10.320 us | 5.6676 us | 0.3107 us | 10.91 KB + JsonPath_Newtonsoft_JObject | $..* | 10.862 us | 0.4374 us | 0.0240 us | 14.86 KB + JsonPath_JsonEverything_JsonNode | $..* | 21.914 us | 19.4680 us | 1.0671 us | 36.81 KB + | | | | | + JsonPath_Hyperbee_JsonElement | $..price | 4.557 us | 3.6801 us | 0.2017 us | 4.2 KB + JsonPath_JsonCons_JsonElement | $..price | 4.989 us | 2.3125 us | 0.1268 us | 5.65 KB + JsonPath_Hyperbee_JsonNode | $..price | 7.929 us | 0.6128 us | 0.0336 us | 7.48 KB + JsonPath_Newtonsoft_JObject | $..price | 10.511 us | 11.4901 us | 0.6298 us | 14.4 KB + JsonPath_JsonEverything_JsonNode | $..price | 15.999 us | 0.5210 us | 0.0286 us | 27.63 KB + | | | | | + JsonPath_Hyperbee_JsonElement | $.store.book[?(@.price == 8.99)] | 4.221 us | 2.4758 us | 0.1357 us | 5.24 KB + JsonPath_JsonCons_JsonElement | $.store.book[?(@.price == 8.99)] | 5.424 us | 0.3551 us | 0.0195 us | 5.05 KB + JsonPath_Hyperbee_JsonNode | $.store.book[?(@.price == 8.99)] | 7.023 us | 7.0447 us | 0.3861 us | 8 KB + JsonPath_Newtonsoft_JObject | $.store.book[?(@.price == 8.99)] | 10.572 us | 2.4203 us | 0.1327 us | 15.84 KB + JsonPath_JsonEverything_JsonNode | $.store.book[?(@.price == 8.99)] | 12.478 us | 0.5762 us | 0.0316 us | 15.85 KB + | | | | | + JsonPath_Hyperbee_JsonElement | $.store.book[0] | 2.720 us | 1.9771 us | 0.1084 us | 2.27 KB + JsonPath_JsonCons_JsonElement | $.store.book[0] | 3.266 us | 0.2087 us | 0.0114 us | 3.21 KB + JsonPath_Hyperbee_JsonNode | $.store.book[0] | 3.396 us | 0.5137 us | 0.0282 us | 2.77 KB + JsonPath_JsonEverything_JsonNode | $.store.book[0] | 5.088 us | 0.1202 us | 0.0066 us | 5.96 KB + JsonPath_Newtonsoft_JObject | $.store.book[0] | 9.178 us | 9.5618 us | 0.5241 us | 14.56 KB ``` ## Additional Documentation diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs index c5d1445f..e51d851b 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs @@ -8,7 +8,7 @@ namespace Hyperbee.Json.Descriptors.Element; public class ElementTypeDescriptor : ITypeDescriptor { private ElementValueAccessor _accessor; - private FilterEvaluator _evaluator; + private FilterRuntime _runtime; private ValueTypeComparer _comparer; public FunctionRegistry Functions { get; } = new(); @@ -16,8 +16,8 @@ public class ElementTypeDescriptor : ITypeDescriptor public IValueAccessor Accessor => _accessor ??= new ElementValueAccessor(); - public IFilterEvaluator FilterEvaluator => - _evaluator ??= new FilterEvaluator(); + public IFilterRuntime FilterRuntime => + _runtime ??= new FilterRuntime(); public IValueTypeComparer Comparer => _comparer ??= new ValueTypeComparer( Accessor ); diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs index aae8a8ca..183202ce 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs @@ -148,17 +148,8 @@ public bool DeepEquals( JsonElement left, JsonElement right ) return left.DeepEquals( right ); } - public bool TryParseNode( ReadOnlySpan item, out JsonElement element ) + public bool TryParseNode( ref Utf8JsonReader reader, out JsonElement element ) { - var maxLength = Encoding.UTF8.GetMaxByteCount( item.Length ); - Span utf8Bytes = maxLength <= 256 ? stackalloc byte[maxLength] : new byte[maxLength]; - - var length = Encoding.UTF8.GetBytes( item, utf8Bytes ); - - ReplaceSingleQuotes( ref utf8Bytes, length ); - - var reader = new Utf8JsonReader( utf8Bytes[..length] ); - try { if ( JsonDocument.TryParseValue( ref reader, out var document ) ) @@ -174,22 +165,6 @@ public bool TryParseNode( ReadOnlySpan item, out JsonElement element ) element = default; return false; - - static void ReplaceSingleQuotes( ref Span utf8Bytes, int length ) - { - var insideString = false; - for ( var i = 0; i < length; i++ ) - { - if ( utf8Bytes[i] == (byte) '\"' ) - { - insideString = !insideString; - } - else if ( !insideString && utf8Bytes[i] == (byte) '\'' && (i == 0 || utf8Bytes[i - 1] != '\\') ) - { - utf8Bytes[i] = (byte) '\"'; - } - } - } } public bool TryGetValueFromNode( JsonElement element, out IConvertible value ) diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs index ccc3d29f..ed6925ab 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class CountElementFunction() : ExtensionFunction( CountMethod, ExtensionInfo.MustCompare ) +public class CountElementFunction() : ExtensionFunction( CountMethod, CompareConstraint.MustCompare ) { public const string Name = "count"; private static readonly MethodInfo CountMethod = GetMethod( nameof( Count ) ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs index 7615f1fb..9ca19ca4 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class LengthElementFunction() : ExtensionFunction( LengthMethod, ExtensionInfo.MustCompare | ExtensionInfo.ExpectNormalized ) +public class LengthElementFunction() : ExtensionFunction( LengthMethod, CompareConstraint.MustCompare | CompareConstraint.ExpectNormalized ) { public const string Name = "length"; private static readonly MethodInfo LengthMethod = GetMethod( nameof( Length ) ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs index 37833a74..e9c2e873 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class MatchElementFunction() : ExtensionFunction( MatchMethod, ExtensionInfo.MustNotCompare ) +public class MatchElementFunction() : ExtensionFunction( MatchMethod, CompareConstraint.MustNotCompare ) { public const string Name = "match"; private static readonly MethodInfo MatchMethod = GetMethod( nameof( Match ) ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs index 8aeb5055..ce69a15f 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class SearchElementFunction() : ExtensionFunction( SearchMethod, ExtensionInfo.MustNotCompare ) +public class SearchElementFunction() : ExtensionFunction( SearchMethod, CompareConstraint.MustNotCompare ) { public const string Name = "search"; private static readonly MethodInfo SearchMethod = GetMethod( nameof( Search ) ); diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs index a31621bd..d04f7931 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Element.Functions; -public class ValueElementFunction() : ExtensionFunction( ValueMethod, ExtensionInfo.MustCompare ) +public class ValueElementFunction() : ExtensionFunction( ValueMethod, CompareConstraint.MustCompare ) { public const string Name = "value"; private static readonly MethodInfo ValueMethod = GetMethod( nameof( Value ) ); diff --git a/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs b/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs index 41ca1520..daa0daf1 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ValueTypeExtensions.cs @@ -37,51 +37,35 @@ public static bool TryGetNode( this IValueType input, out T value ) return false; } - public static bool TryGetNumber( this IValueType input, out IConvertible value ) - { - if ( TryGetValue( input, out value ) && value is int || value is float ) - return true; - - value = default; - return false; - } - - private static bool TryConvertTo( this JsonElement element, out T value ) where T : IConvertible { value = default; + try { - if ( typeof( T ) == typeof( string ) && element.ValueKind == JsonValueKind.String ) - { - value = (T) (IConvertible) element.GetString(); - return true; - } + var type = typeof( T ); - if ( typeof( T ) == typeof( int ) && element.ValueKind == JsonValueKind.Number && element.TryGetInt32( out var intValue ) ) + switch ( element.ValueKind ) { - value = (T) (IConvertible) intValue; - return true; - } + case JsonValueKind.String when type == typeof( string ): + value = (T) (IConvertible) element.GetString(); + return true; - if ( typeof( T ) == typeof( float ) && element.ValueKind == JsonValueKind.Number && element.TryGetSingle( out var floatValue ) ) - { - value = (T) (IConvertible) floatValue; - return true; - } + case JsonValueKind.Number when type == typeof( int ) && element.TryGetInt32( out var intValue ): + value = (T) (IConvertible) intValue; + return true; - if ( typeof( T ) == typeof( bool ) ) - { - if ( element.ValueKind == JsonValueKind.True ) - { + case JsonValueKind.Number when type == typeof( float ) && element.TryGetSingle( out var floatValue ): + value = (T) (IConvertible) floatValue; + return true; + + case JsonValueKind.True when type == typeof( bool ): value = (T) (IConvertible) true; return true; - } - if ( element.ValueKind == JsonValueKind.False ) - { + + case JsonValueKind.False when type == typeof( bool ): value = (T) (IConvertible) false; return true; - } } } catch diff --git a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs index 1fd6fda7..4cd06767 100644 --- a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs @@ -13,14 +13,14 @@ public interface ITypeDescriptor public interface ITypeDescriptor : ITypeDescriptor { public IValueAccessor Accessor { get; } - public IFilterEvaluator FilterEvaluator { get; } + public IFilterRuntime FilterRuntime { get; } public IValueTypeComparer Comparer { get; } bool CanUsePointer { get; } - public void Deconstruct( out IValueAccessor valueAccessor, out IFilterEvaluator filterEvaluator ) + public void Deconstruct( out IValueAccessor valueAccessor, out IFilterRuntime filterRuntime ) { valueAccessor = Accessor; - filterEvaluator = FilterEvaluator; + filterRuntime = FilterRuntime; } } diff --git a/src/Hyperbee.Json/Descriptors/IValueAccessor.cs b/src/Hyperbee.Json/Descriptors/IValueAccessor.cs index 656288bf..9ea58fac 100644 --- a/src/Hyperbee.Json/Descriptors/IValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/IValueAccessor.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Nodes; +using System.Text.Json; +using System.Text.Json.Nodes; namespace Hyperbee.Json.Descriptors; @@ -9,7 +10,7 @@ public interface IValueAccessor NodeKind GetNodeKind( in TNode value ); int GetArrayLength( in TNode value ); bool TryGetChild( in TNode value, string childSelector, SelectorKind selectorKind, out TNode childValue ); - bool TryParseNode( ReadOnlySpan item, out TNode value ); + bool TryParseNode( ref Utf8JsonReader reader, out TNode value ); bool DeepEquals( TNode left, TNode right ); bool TryGetValueFromNode( TNode item, out IConvertible value ); bool TryGetFromPointer( in TNode value, JsonPathSegment segment, out TNode childValue ); diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs index c6cdb547..d747e842 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class CountNodeFunction() : ExtensionFunction( CountMethod, ExtensionInfo.MustCompare ) +public class CountNodeFunction() : ExtensionFunction( CountMethod, CompareConstraint.MustCompare ) { public const string Name = "count"; private static readonly MethodInfo CountMethod = GetMethod( nameof( Count ) ); diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs index f247961c..b1b2deaa 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class LengthNodeFunction() : ExtensionFunction( LengthMethod, ExtensionInfo.MustCompare | ExtensionInfo.ExpectNormalized ) +public class LengthNodeFunction() : ExtensionFunction( LengthMethod, CompareConstraint.MustCompare | CompareConstraint.ExpectNormalized ) { public const string Name = "length"; private static readonly MethodInfo LengthMethod = GetMethod( nameof( Length ) ); diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs index 9bb561f2..28520300 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class MatchNodeFunction() : ExtensionFunction( MatchMethod, ExtensionInfo.MustNotCompare ) +public class MatchNodeFunction() : ExtensionFunction( MatchMethod, CompareConstraint.MustNotCompare ) { public const string Name = "match"; private static readonly MethodInfo MatchMethod = GetMethod( nameof( Match ) ); diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs index 1baa28f1..397f6227 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class SearchNodeFunction() : ExtensionFunction( SearchMethod, ExtensionInfo.MustNotCompare ) +public class SearchNodeFunction() : ExtensionFunction( SearchMethod, CompareConstraint.MustNotCompare ) { public const string Name = "search"; private static readonly MethodInfo SearchMethod = GetMethod( nameof( Search ) ); diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs index 38b9740e..85dd4197 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs @@ -7,7 +7,7 @@ namespace Hyperbee.Json.Descriptors.Node.Functions; -public class ValueNodeFunction() : ExtensionFunction( ValueMethod, ExtensionInfo.MustCompare ) +public class ValueNodeFunction() : ExtensionFunction( ValueMethod, CompareConstraint.MustCompare ) { public const string Name = "value"; private static readonly MethodInfo ValueMethod = GetMethod( nameof( Value ) ); diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs index af756abd..bf2a9c85 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs @@ -8,7 +8,7 @@ namespace Hyperbee.Json.Descriptors.Node; public class NodeTypeDescriptor : ITypeDescriptor { private NodeValueAccessor _accessor; - private FilterEvaluator _evaluator; + private FilterRuntime _runtime; private ValueTypeComparer _comparer; public FunctionRegistry Functions { get; } = new(); @@ -16,8 +16,8 @@ public class NodeTypeDescriptor : ITypeDescriptor public IValueAccessor Accessor => _accessor ??= new NodeValueAccessor(); - public IFilterEvaluator FilterEvaluator => - _evaluator ??= new FilterEvaluator(); + public IFilterRuntime FilterRuntime => + _runtime ??= new FilterRuntime(); public IValueTypeComparer Comparer => _comparer ??= new ValueTypeComparer( Accessor ); diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs index f2d6b156..dc5d05a8 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs @@ -153,17 +153,11 @@ public bool DeepEquals( JsonNode left, JsonNode right ) return JsonNode.DeepEquals( left, right ); } - public bool TryParseNode( ReadOnlySpan item, out JsonNode node ) + public bool TryParseNode( ref Utf8JsonReader reader, out JsonNode node ) { - var maxLength = Encoding.UTF8.GetMaxByteCount( item.Length ); - Span utf8Bytes = maxLength <= 256 ? stackalloc byte[maxLength] : new byte[maxLength]; - var length = Encoding.UTF8.GetBytes( item, utf8Bytes ); - - ReplaceSingleQuotes( ref utf8Bytes, length ); - try { - node = JsonNode.Parse( utf8Bytes[..length] ); + node = JsonNode.Parse( ref reader ); return true; } catch @@ -171,22 +165,6 @@ public bool TryParseNode( ReadOnlySpan item, out JsonNode node ) node = null; return false; } - - static void ReplaceSingleQuotes( ref Span utf8Bytes, int length ) - { - var insideString = false; - for ( var i = 0; i < length; i++ ) - { - if ( utf8Bytes[i] == (byte) '\"' ) - { - insideString = !insideString; - } - else if ( !insideString && utf8Bytes[i] == (byte) '\'' && (i == 0 || utf8Bytes[i - 1] != '\\') ) - { - utf8Bytes[i] = (byte) '\"'; - } - } - } } public bool TryGetValueFromNode( JsonNode node, out IConvertible value ) diff --git a/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs b/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs index cf1fe208..dd773ee3 100644 --- a/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs +++ b/src/Hyperbee.Json/Descriptors/Node/ValueTypeExtensions.cs @@ -42,40 +42,33 @@ private static bool TryConvertTo( this JsonNode node, out T value ) where T : value = default; try { - if ( typeof( T ) == typeof( string ) && node is JsonValue jsonValue && jsonValue.TryGetValue( out string stringValue ) ) - { - value = (T) (IConvertible) stringValue; - return true; - } + var type = typeof( T ); - if ( typeof( T ) == typeof( int ) && node is JsonValue jsonInt && jsonInt.TryGetValue( out int intValue ) ) + switch ( node ) { - value = (T) (IConvertible) intValue; - return true; - } + case JsonValue jsonValue when type == typeof( string ) && jsonValue.TryGetValue( out string stringValue ): + value = (T) (IConvertible) stringValue; + return true; - if ( typeof( T ) == typeof( float ) && node is JsonValue jsonFloat && jsonFloat.TryGetValue( out float floatValue ) ) - { - value = (T) (IConvertible) floatValue; - return true; - } + case JsonValue jsonInt when type == typeof( int ) && jsonInt.TryGetValue( out int intValue ): + value = (T) (IConvertible) intValue; + return true; - if ( typeof( T ) == typeof( float ) && node is JsonArray jsonArray ) - { - value = (T) (IConvertible) jsonArray.Count; - return true; - } + case JsonValue jsonFloat when type == typeof( float ) && jsonFloat.TryGetValue( out float floatValue ): + value = (T) (IConvertible) floatValue; + return true; - if ( typeof( T ) == typeof( float ) && node is JsonObject jsonObject ) - { - value = (T) (IConvertible) jsonObject.Count; - return true; - } + case JsonArray jsonArray when type == typeof( float ): + value = (T) (IConvertible) jsonArray.Count; + return true; - if ( typeof( T ) == typeof( bool ) && node is JsonValue jsonBool && jsonBool.TryGetValue( out bool boolValue ) ) - { - value = (T) (IConvertible) boolValue; - return true; + case JsonObject jsonObject when type == typeof( float ): + value = (T) (IConvertible) jsonObject.Count; + return true; + + case JsonValue jsonBool when type == typeof( bool ) && jsonBool.TryGetValue( out bool boolValue ): + value = (T) (IConvertible) boolValue; + return true; } } catch diff --git a/src/Hyperbee.Json/Dynamic/DynamicJsonElement.cs b/src/Hyperbee.Json/Dynamic/DynamicJsonElement.cs index 7d504c9f..17d30f56 100644 --- a/src/Hyperbee.Json/Dynamic/DynamicJsonElement.cs +++ b/src/Hyperbee.Json/Dynamic/DynamicJsonElement.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Dynamic; public class DynamicJsonElement : DynamicObject { private readonly JsonElement Value; - private readonly string Path; + private readonly JsonPathBuilder PathBuilder = new(); public static implicit operator double( DynamicJsonElement proxy ) => proxy.Value.GetDouble(); public static implicit operator decimal( DynamicJsonElement proxy ) => proxy.Value.GetDecimal(); @@ -22,10 +22,9 @@ public class DynamicJsonElement : DynamicObject public static implicit operator DateTimeOffset( DynamicJsonElement proxy ) => proxy.Value.GetDateTimeOffset(); public static implicit operator string( DynamicJsonElement proxy ) => proxy.Value.GetString(); - public DynamicJsonElement( ref JsonElement value, string path ) + public DynamicJsonElement( ref JsonElement value ) { Value = value; - Path = path ?? string.Empty; } public override bool TryConvert( ConvertBinder binder, out object result ) @@ -39,7 +38,7 @@ public override bool TryGetIndex( GetIndexBinder binder, object[] indexes, out o if ( Value.ValueKind == JsonValueKind.Array ) { var resultValue = Value[(int) indexes[0]]; - result = new DynamicJsonElement( ref resultValue, Path + $"[{indexes[0]}]" ); + result = new DynamicJsonElement( ref resultValue ); return true; } @@ -55,7 +54,7 @@ public override bool TryGetMember( GetMemberBinder binder, out object result ) { if ( Value.TryGetProperty( binder.Name, out var resultValue ) ) { - result = new DynamicJsonElement( ref resultValue, Path + $"['{binder.Name}']" ); + result = new DynamicJsonElement( ref resultValue ); return true; } } @@ -70,7 +69,7 @@ public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, if ( binder.Name.Equals( "path", StringComparison.OrdinalIgnoreCase ) ) { - result = Path; + result = PathBuilder.GetPath( Value ); return true; } diff --git a/src/Hyperbee.Json/Dynamic/DynamicJsonNode.cs b/src/Hyperbee.Json/Dynamic/DynamicJsonNode.cs index 58dcdda1..873945d4 100644 --- a/src/Hyperbee.Json/Dynamic/DynamicJsonNode.cs +++ b/src/Hyperbee.Json/Dynamic/DynamicJsonNode.cs @@ -1,4 +1,5 @@ using System.Dynamic; +using System.Numerics; using System.Text.Json.Nodes; using Hyperbee.Json.Extensions; @@ -8,11 +9,11 @@ public class DynamicJsonNode : DynamicObject { private readonly JsonNode Node; + public static implicit operator short( DynamicJsonNode proxy ) => GetNumber( proxy.Node ); + public static implicit operator int( DynamicJsonNode proxy ) => GetNumber( proxy.Node ); + public static implicit operator long( DynamicJsonNode proxy ) => GetNumber( proxy.Node ); public static implicit operator double( DynamicJsonNode proxy ) => proxy.Node.GetValue(); public static implicit operator decimal( DynamicJsonNode proxy ) => proxy.Node.GetValue(); - public static implicit operator short( DynamicJsonNode proxy ) => proxy.Node.GetNumber(); - public static implicit operator int( DynamicJsonNode proxy ) => proxy.Node.GetNumber(); - public static implicit operator long( DynamicJsonNode proxy ) => proxy.Node.GetNumber(); public static implicit operator bool( DynamicJsonNode proxy ) => proxy.Node.GetValue(); public static implicit operator byte( DynamicJsonNode proxy ) => proxy.Node.GetValue(); public static implicit operator sbyte( DynamicJsonNode proxy ) => proxy.Node.GetValue(); @@ -53,7 +54,6 @@ public override bool TryGetMember( GetMemberBinder binder, out object result ) result = new DynamicJsonNode( ref resultValue ); return true; case JsonArray jArray: - //bf not sure if this gets called var arrayValue = jArray[binder.Name]; result = new DynamicJsonNode( ref arrayValue ); return true; @@ -90,4 +90,27 @@ public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, result = null; return false; } + + private static T GetNumber( JsonNode value ) + where T : struct, IComparable, IFormattable, IConvertible, IComparable, IEquatable, INumber + { + var source = value.AsValue(); + + if ( typeof( T ) == typeof( int ) || typeof( T ) == typeof( long ) || typeof( T ) == typeof( short ) || typeof( T ) == typeof( byte ) ) + { + if ( source.TryGetValue( out var result ) ) + return result; + + // the value may contain a decimal. convert to integer without rounding. + // ChangeType rounds values. Cast to integer first to truncate. + var truncated = (long) source.GetValue(); + var converted = Convert.ChangeType( truncated, typeof( T ) ); + return (T) converted; + } + + if ( typeof( T ) == typeof( float ) ) + return (T) (IConvertible) source.GetValue(); + + throw new NotSupportedException(); + } } diff --git a/src/Hyperbee.Json/Extensions/JsonDynamicHelper.cs b/src/Hyperbee.Json/Extensions/JsonDynamicHelper.cs index 9fa0383e..ec076695 100644 --- a/src/Hyperbee.Json/Extensions/JsonDynamicHelper.cs +++ b/src/Hyperbee.Json/Extensions/JsonDynamicHelper.cs @@ -6,9 +6,7 @@ namespace Hyperbee.Json.Extensions; public static class JsonDynamicHelper { - // conversion - public static dynamic ConvertToDynamic( JsonNode value ) => new DynamicJsonNode( ref value ); - public static dynamic ConvertToDynamic( JsonElement value, string path = null ) => new DynamicJsonElement( ref value, path ); - public static dynamic ConvertToDynamic( JsonDocument value ) => ConvertToDynamic( value.RootElement, "$" ); + public static dynamic ConvertToDynamic( JsonElement value ) => new DynamicJsonElement( ref value ); + public static dynamic ConvertToDynamic( JsonDocument value ) => ConvertToDynamic( value.RootElement ); } diff --git a/src/Hyperbee.Json/Extensions/JsonNodeExtensions.cs b/src/Hyperbee.Json/Extensions/JsonNodeExtensions.cs deleted file mode 100644 index d77bf283..00000000 --- a/src/Hyperbee.Json/Extensions/JsonNodeExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Numerics; -using System.Text.Json.Nodes; - -namespace Hyperbee.Json.Extensions; - -public static class JsonNodeExtensions -{ - // Value extensions - - internal static T GetNumber( this JsonNode value ) - where T : struct, IComparable, IFormattable, IConvertible, IComparable, IEquatable, INumber - { - var source = value.AsValue(); - - if ( typeof( T ) == typeof( int ) || typeof( T ) == typeof( long ) || typeof( T ) == typeof( short ) || typeof( T ) == typeof( byte ) ) - { - if ( source.TryGetValue( out var result ) ) - return result; - - // the value may contain a decimal. convert to integer without rounding. - // ChangeType rounds values. Cast to integer first to truncate. - var truncated = (long) source.GetValue(); - var converted = Convert.ChangeType( truncated, typeof( T ) ); - return (T) converted; - } - - if ( typeof( T ) == typeof( float ) ) - return (T) (IConvertible) source.GetValue(); - - throw new NotSupportedException(); - } -} diff --git a/src/Hyperbee.Json/Filters/FilterEvaluator.cs b/src/Hyperbee.Json/Filters/FilterRuntime.cs similarity index 93% rename from src/Hyperbee.Json/Filters/FilterEvaluator.cs rename to src/Hyperbee.Json/Filters/FilterRuntime.cs index 8ea4a7cc..c1822c08 100644 --- a/src/Hyperbee.Json/Filters/FilterEvaluator.cs +++ b/src/Hyperbee.Json/Filters/FilterRuntime.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Json.Filters; public record FilterRuntimeContext( TNode Current, TNode Root ); -public sealed class FilterEvaluator : IFilterEvaluator +public sealed class FilterRuntime : IFilterRuntime { private static readonly ConcurrentDictionary, bool>> Compiled = new(); diff --git a/src/Hyperbee.Json/Filters/IFilterEvaluator.cs b/src/Hyperbee.Json/Filters/IFilterRuntime.cs similarity index 71% rename from src/Hyperbee.Json/Filters/IFilterEvaluator.cs rename to src/Hyperbee.Json/Filters/IFilterRuntime.cs index a2ca7dec..4c0b9d0a 100644 --- a/src/Hyperbee.Json/Filters/IFilterEvaluator.cs +++ b/src/Hyperbee.Json/Filters/IFilterRuntime.cs @@ -1,7 +1,7 @@  namespace Hyperbee.Json.Filters; -public interface IFilterEvaluator +public interface IFilterRuntime { public bool Evaluate( string filter, TNode current, TNode root ); } diff --git a/src/Hyperbee.Json/Filters/Parser/ExtensionInfo.cs b/src/Hyperbee.Json/Filters/Parser/CompareConstraint.cs similarity index 56% rename from src/Hyperbee.Json/Filters/Parser/ExtensionInfo.cs rename to src/Hyperbee.Json/Filters/Parser/CompareConstraint.cs index 794bfc87..80567445 100644 --- a/src/Hyperbee.Json/Filters/Parser/ExtensionInfo.cs +++ b/src/Hyperbee.Json/Filters/Parser/CompareConstraint.cs @@ -1,9 +1,15 @@ namespace Hyperbee.Json.Filters.Parser; [Flags] -public enum ExtensionInfo +public enum CompareConstraint { + None = 0x00, MustCompare = 0x01, MustNotCompare = 0x02, ExpectNormalized = 0x10, + + // Scope + + Function = 0x100, + Literal = 0x200 } diff --git a/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs b/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs deleted file mode 100644 index c490b80f..00000000 --- a/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Hyperbee.Json.Filters.Parser; - -internal class ExpressionInfo -{ - public ExpressionKind Kind { get; set; } - public ExtensionInfo FunctionInfo { get; set; } -} diff --git a/src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs b/src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs deleted file mode 100644 index 9231d05d..00000000 --- a/src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Hyperbee.Json.Filters.Parser; - -internal enum ExpressionKind -{ - Unspecified, - Function, - Json, - Literal, - Not, - Paren, - Select, - Merged -} diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs index 00058a40..70fcd9dd 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs @@ -5,17 +5,18 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class FunctionExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, out CompareConstraint compareConstraint, ITypeDescriptor descriptor ) { + compareConstraint = CompareConstraint.None; + expression = null; + if ( state.Item.IsEmpty || !char.IsLetter( state.Item[0] ) ) { - expression = null; return false; } if ( !descriptor.Functions.TryGetActivator( state.Item.ToString(), out var functionActivator ) ) { - expression = null; return false; } @@ -25,9 +26,8 @@ public static bool TryGetExpression( ref ParserState state, out Expressio var function = functionActivator(); expression = function.GetExpression( ref state ); // will recurse for each function argument. + compareConstraint = CompareConstraint.Function | function.CompareConstraint; - exprInfo.Kind = ExpressionKind.Function; - exprInfo.FunctionInfo = function.FunctionInfo; return true; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs index 17e66dd4..077f7a59 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs @@ -5,5 +5,5 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal interface IExpressionFactory { - static abstract bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ); + static abstract bool TryGetExpression( ref ParserState state, out Expression expression, out CompareConstraint compareConstraint, ITypeDescriptor descriptor ); } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs index 6d44a689..4308e684 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs @@ -1,4 +1,6 @@ using System.Linq.Expressions; +using System.Text; +using System.Text.Json; using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Values; @@ -6,16 +8,52 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class JsonExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor descriptor ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, out CompareConstraint compareConstraint, ITypeDescriptor descriptor ) { - if ( !descriptor.Accessor.TryParseNode( state.Item.ToString(), out var node ) ) + compareConstraint = CompareConstraint.None; + + if ( !TryParseNode( descriptor.Accessor, state.Item, out var node ) ) { expression = null; return false; } expression = Expression.Constant( new NodeList( [node], isNormalized: true ) ); - exprInfo.Kind = ExpressionKind.Json; return true; } + + private static bool TryParseNode( IValueAccessor accessor, ReadOnlySpan item, out TNode node ) + { + var maxLength = Encoding.UTF8.GetMaxByteCount( item.Length ); + Span bytes = maxLength <= 256 ? stackalloc byte[maxLength] : new byte[maxLength]; + + var length = Encoding.UTF8.GetBytes( item, bytes ); + + // the jsonpath rfc supports single quotes, but the json parser does not + ConvertToDoubleQuotes( ref bytes, length ); + + var reader = new Utf8JsonReader( bytes[..length] ); + + if ( accessor.TryParseNode( ref reader, out node ) ) + return true; + + node = default; + return false; + } + + private static void ConvertToDoubleQuotes( ref Span buffer, int length ) + { + var insideString = false; + for ( var i = 0; i < length; i++ ) + { + if ( buffer[i] == (byte) '\"' ) + { + insideString = !insideString; + } + else if ( !insideString && buffer[i] == (byte) '\'' && (i == 0 || buffer[i - 1] != '\\') ) + { + buffer[i] = (byte) '\"'; + } + } + } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs index ca121b44..5b26a346 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs @@ -6,15 +6,12 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class LiteralExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor _ = null ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, out CompareConstraint compareConstraint, ITypeDescriptor _ = null ) { + compareConstraint = CompareConstraint.Literal | CompareConstraint.MustCompare; expression = GetLiteralExpression( state.Item ); - if ( expression == null ) - return false; - - exprInfo.Kind = ExpressionKind.Literal; - return true; + return expression != null; } private static ConstantExpression GetLiteralExpression( ReadOnlySpan item ) diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/MathExpression.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/MathExpression.cs index e4f3fabb..2a1e8f29 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/MathExpression.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/MathExpression.cs @@ -33,7 +33,7 @@ internal static class MathExpression private static IValueType Add( IValueType left, IValueType right ) { if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) - return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + return Scalar.Nothing; return leftValue is int leftInt && rightValue is int rightInt ? Scalar.Value( leftInt + rightInt ) @@ -43,7 +43,7 @@ private static IValueType Add( IValueType left, IValueType right ) private static IValueType Subtract( IValueType left, IValueType right ) { if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) - return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + return Scalar.Nothing; return leftValue is int leftInt && rightValue is int rightInt ? Scalar.Value( leftInt - rightInt ) @@ -53,7 +53,7 @@ private static IValueType Subtract( IValueType left, IValueType right ) private static IValueType Modulus( IValueType left, IValueType right ) { if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) - return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + return Scalar.Nothing; return leftValue is int leftInt && rightValue is int rightInt ? Scalar.Value( leftInt % rightInt ) @@ -63,7 +63,7 @@ private static IValueType Modulus( IValueType left, IValueType right ) private static IValueType Multiply( IValueType left, IValueType right ) { if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) - return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + return Scalar.Nothing; return leftValue is int leftInt && rightValue is int rightInt ? Scalar.Value( leftInt * rightInt ) @@ -73,7 +73,7 @@ private static IValueType Multiply( IValueType left, IValueType right ) private static IValueType Divide( IValueType left, IValueType right ) { if ( !TryGetNumber( left, out var leftValue ) || !TryGetNumber( right, out var rightValue ) ) - return Scalar.Nothing; //BF: should we be throwing NotSupportedException? + return Scalar.Nothing; // dividing two int values may produce a fractional result var floatValue = Convert.ToSingle( leftValue ) / Convert.ToSingle( rightValue ); @@ -91,15 +91,14 @@ static bool TryConvertToInt( float value, out int result, float tolerance = 1e-6 // Calculate the difference between the float and the nearest integer float fractionalPart = Math.Abs( value - (float) Math.Round( value ) ); - // Check if the fractional part is within the tolerance + // If within the tolerance, return the rounded value if ( fractionalPart < tolerance ) { - // If within the tolerance, assign the rounded value to result and return true result = (int) Math.Round( value ); return true; } - // If not within the tolerance, assign default value to result and return false + // If not within the tolerance, return false result = default; return false; } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs index 67767f3b..545d224b 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs @@ -5,14 +5,11 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class NotExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor _ = null ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, out CompareConstraint compareConstraint, ITypeDescriptor _ = null ) { + compareConstraint = CompareConstraint.None; expression = null; - if ( state.Operator != Operator.Not ) - return false; - - exprInfo.Kind = ExpressionKind.Not; - return true; + return state.Operator == Operator.Not; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs index dc7f05b7..dfae4362 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs @@ -5,8 +5,10 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class ParenExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor _ = null ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, out CompareConstraint compareConstraint, ITypeDescriptor _ = null ) { + compareConstraint = CompareConstraint.None; + if ( state.Operator != Operator.OpenParen || !state.Item.IsEmpty ) { expression = null; @@ -19,7 +21,6 @@ public static bool TryGetExpression( ref ParserState state, out Expressio }; expression = FilterParser.Parse( ref localState ); // will recurse. - exprInfo.Kind = ExpressionKind.Paren; return true; } } diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs index 300aba59..2d88cd85 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs @@ -7,8 +7,9 @@ namespace Hyperbee.Json.Filters.Parser.Expressions; internal class SelectExpressionFactory : IExpressionFactory { - public static bool TryGetExpression( ref ParserState state, out Expression expression, ref ExpressionInfo exprInfo, ITypeDescriptor _ = null ) + public static bool TryGetExpression( ref ParserState state, out Expression expression, out CompareConstraint compareConstraint, ITypeDescriptor _ = null ) { + compareConstraint = CompareConstraint.None; var item = state.Item; if ( item.IsEmpty || item[0] != '$' && item[0] != '@' ) @@ -18,7 +19,6 @@ public static bool TryGetExpression( ref ParserState state, out Expressio } expression = ExpressionHelper.GetExpression( state.Item, state.IsArgument ); - exprInfo.Kind = ExpressionKind.Select; return true; } diff --git a/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs b/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs index 2947b4a2..92d70a2e 100644 --- a/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs +++ b/src/Hyperbee.Json/Filters/Parser/ExtensionFunction.cs @@ -9,20 +9,20 @@ public abstract class ExtensionFunction private readonly int _argumentCount; private readonly MethodInfo _methodInfo; - public ExtensionInfo FunctionInfo { get; } + public CompareConstraint CompareConstraint { get; } - protected ExtensionFunction( MethodInfo methodInfo, ExtensionInfo info ) + protected ExtensionFunction( MethodInfo methodInfo, CompareConstraint compareConstraint ) { _argumentCount = methodInfo.GetParameters().Length; _methodInfo = methodInfo; - FunctionInfo = info; + CompareConstraint = compareConstraint; } internal Expression GetExpression( ref ParserState state ) { var arguments = new Expression[_argumentCount]; - var expectNormalized = FunctionInfo.HasFlag( ExtensionInfo.ExpectNormalized ); + var expectNormalized = CompareConstraint.HasFlag( CompareConstraint.ExpectNormalized ); for ( var i = 0; i < _argumentCount; i++ ) { @@ -43,10 +43,11 @@ internal Expression GetExpression( ref ParserState state ) } // Call the method and cast the result to support covariant returns - var callExpression = Expression.Call( _methodInfo, arguments ); - var castExpression = Expression.Convert( callExpression, typeof( IValueType ) ); - return castExpression; + return Expression.Convert( + Expression.Call( _methodInfo, arguments ), + typeof( IValueType ) + ); } private Expression ArgumentExpression( bool expectNormalized, Expression argument ) diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index 721ba5a7..9b4e7e3b 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -26,13 +26,15 @@ public abstract class FilterParser public class FilterParser : FilterParser { - internal static readonly ParameterExpression RuntimeContextExpression = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); // must use a common instance - internal static readonly ITypeDescriptor Descriptor = JsonTypeDescriptorRegistry.GetDescriptor(); + internal static readonly ITypeDescriptor Descriptor = + JsonTypeDescriptorRegistry.GetDescriptor(); + + internal static readonly ParameterExpression RuntimeContextExpression = + Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); // must use a common instance public static Func, bool> Compile( ReadOnlySpan filter ) { var expression = Parse( filter ); - return Expression.Lambda, bool>>( expression, RuntimeContextExpression ).Compile(); } @@ -74,36 +76,20 @@ internal static Expression Parse( ref ParserState state ) // recursion entrypoin return Merge( in state, baseItem, items ); } - private static ExprItem GetExprItem( ref ParserState state ) { - var expressionInfo = new ExpressionInfo(); - - if ( NotExpressionFactory.TryGetExpression( ref state, out var expression, ref expressionInfo ) ) - return ExprItem( ref state, expression, expressionInfo ); - - if ( ParenExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) // will recurse - return ExprItem( ref state, expression, expressionInfo ); - - if ( SelectExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) - return ExprItem( ref state, expression, expressionInfo ); - - if ( FunctionExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, Descriptor ) ) // may recurse for each function argument - return ExprItem( ref state, expression, expressionInfo ); - - if ( LiteralExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo ) ) - return ExprItem( ref state, expression, expressionInfo ); - - if ( JsonExpressionFactory.TryGetExpression( ref state, out expression, ref expressionInfo, Descriptor ) ) - return ExprItem( ref state, expression, expressionInfo ); - - throw new NotSupportedException( $"Unsupported literal: {state.Buffer.ToString()}" ); - - // Helper method to create an expression item - static ExprItem ExprItem( ref ParserState state, Expression expression, ExpressionInfo expressionInfo ) + switch ( true ) { - MoveNextOperator( ref state ); // will set state.Operator - return new ExprItem( expression, state.Operator, expressionInfo ); + case true when NotExpressionFactory.TryGetExpression( ref state, out var expression, out var compareConstraint ): + case true when ParenExpressionFactory.TryGetExpression( ref state, out expression, out compareConstraint ): + case true when SelectExpressionFactory.TryGetExpression( ref state, out expression, out compareConstraint ): + case true when FunctionExpressionFactory.TryGetExpression( ref state, out expression, out compareConstraint, Descriptor ): + case true when LiteralExpressionFactory.TryGetExpression( ref state, out expression, out compareConstraint ): + case true when JsonExpressionFactory.TryGetExpression( ref state, out expression, out compareConstraint, Descriptor ): + MoveNextOperator( ref state ); + return new ExprItem( expression, state.Operator, compareConstraint ); + default: + throw new NotSupportedException( $"Unsupported operator: {state.Operator}." ); } } @@ -133,14 +119,8 @@ private static void MoveNext( ref ParserState state ) // move to the next item NextCharacter( ref state, itemStart, out var nextChar, ref quote ); // will advance state.Pos - if ( IsFinished( in state, nextChar ) ) - { - break; - } - - if ( state.EndOfBuffer ) + if ( IsFinished( in state, nextChar, ref itemEnd ) ) { - itemEnd = state.Pos; // include the final character break; } } @@ -149,18 +129,23 @@ private static void MoveNext( ref ParserState state ) // move to the next item return; - // Helper method to determine if item collection is finished - static bool IsFinished( in ParserState state, char ch ) + static bool IsFinished( in ParserState state, char ch, ref int itemEnd ) { // order of operations matters + bool result = state switch + { + _ when state.BracketDepth != 0 => false, + _ when !state.Operator.IsNonOperator() => true, + _ when ch == state.TerminalCharacter => true, // [ '\0' or ',' or ')' ] + _ => false + }; - if ( state.BracketDepth != 0 ) - return false; - - if ( state.Operator.IsNonOperator() == false ) - return true; + if ( result || !state.EndOfBuffer ) + return result; - return ch == state.TerminalCharacter; // [ '\0' or ',' or ')' ] + // not finished, but at end-of-buffer + itemEnd = state.Pos; + return true; } } @@ -404,8 +389,8 @@ Operator.Divide or private static void MergeItems( ExprItem left, ExprItem right ) { - left.Expression = ConvertExpression( left.Expression ); - right.Expression = ConvertExpression( right.Expression ); + left.Expression = ConvertOrDefault( left.Expression, typeof( IValueType ) ); + right.Expression = ConvertOrDefault( right.Expression, typeof( IValueType ) ); left.Expression = left.Operator switch { @@ -420,7 +405,7 @@ private static void MergeItems( ExprItem left, ExprItem right ) Operator.Or => CompareExpression.Or( left.Expression, right.Expression ), Operator.Not => CompareExpression.Not( right.Expression ), - Operator.In => CompareExpression.Or( left.Expression, right.Expression ), + Operator.In => CompareExpression.In( left.Expression, right.Expression ), Operator.Add => MathExpression.Add( left.Expression, right.Expression ), Operator.Subtract => MathExpression.Subtract( left.Expression, right.Expression ), @@ -428,68 +413,56 @@ private static void MergeItems( ExprItem left, ExprItem right ) Operator.Divide => MathExpression.Divide( left.Expression, right.Expression ), Operator.Modulus => MathExpression.Modulus( left.Expression, right.Expression ), - _ => throw new InvalidOperationException( $"Invalid operator {left.Operator}" ) + _ => throw new InvalidOperationException( $"Invalid operator {left.Operator}." ) }; left.Operator = right.Operator; - left.ExpressionInfo.Kind = ExpressionKind.Merged; + left.CompareConstraint = CompareConstraint.None; return; - static Expression ConvertExpression( Expression expression ) - { - return expression != null && expression.Type != typeof( TType ) - ? Expression.Convert( expression, typeof( TType ) ) - : expression; - } + static Expression ConvertOrDefault( Expression expression, Type type ) => + expression == null ? null : Expression.Convert( expression, type ); } // Throw helpers private static void ThrowIfInvalidCompare( in ParserState state, ExprItem left, ExprItem right ) { - ThrowIfConstantIsNotCompared( in state, left, right ); + ThrowIfLiteralInvalidCompare( in state, left, right ); ThrowIfFunctionInvalidCompare( in state, left ); } - private static void ThrowIfFunctionInvalidCompare( in ParserState state, ExprItem item ) + private static void ThrowIfLiteralInvalidCompare( in ParserState state, ExprItem left, ExprItem right ) { - if ( state.IsArgument ) + if ( state.IsArgument || left.Operator.IsMath() ) return; - if ( item.ExpressionInfo.Kind != ExpressionKind.Function ) - return; - - var functionInfo = item.ExpressionInfo.FunctionInfo; - - if ( functionInfo.HasFlag( ExtensionInfo.MustCompare ) && !item.Operator.IsComparison() ) - throw new NotSupportedException( $"Function must compare: {state.Buffer.ToString()}." ); + if ( left.CompareConstraint.HasFlag( CompareConstraint.Literal | CompareConstraint.MustCompare ) && !left.Operator.IsComparison() ) + throw new NotSupportedException( $"Unsupported literal without comparison: {state.Buffer.ToString()}." ); - if ( functionInfo.HasFlag( ExtensionInfo.MustNotCompare ) && item.Operator.IsComparison() ) - throw new NotSupportedException( $"Function must not compare: {state.Buffer.ToString()}." ); + if ( right != null && right.CompareConstraint.HasFlag( CompareConstraint.Literal | CompareConstraint.MustCompare ) && !left.Operator.IsComparison() ) + throw new NotSupportedException( $"Unsupported literal without comparison: {state.Buffer.ToString()}." ); } - private static void ThrowIfConstantIsNotCompared( in ParserState state, ExprItem left, ExprItem right ) + private static void ThrowIfFunctionInvalidCompare( in ParserState state, ExprItem item ) { if ( state.IsArgument ) return; - if ( left.Operator.IsMath() ) - return; - - if ( left.ExpressionInfo.Kind == ExpressionKind.Literal && !left.Operator.IsComparison() ) - throw new NotSupportedException( $"Unsupported literal without comparison: {state.Buffer.ToString()}." ); + if ( item.CompareConstraint.HasFlag( CompareConstraint.Function | CompareConstraint.MustCompare ) && !item.Operator.IsComparison() ) + throw new NotSupportedException( $"Function must compare: {state.Buffer.ToString()}." ); - if ( right != null && right.ExpressionInfo.Kind == ExpressionKind.Literal && !left.Operator.IsComparison() ) - throw new NotSupportedException( $"Unsupported literal without comparison: {state.Buffer.ToString()}." ); + if ( item.CompareConstraint.HasFlag( CompareConstraint.Function | CompareConstraint.MustNotCompare ) && item.Operator.IsComparison() ) + throw new NotSupportedException( $"Function must not compare: {state.Buffer.ToString()}." ); } // ExprItem - [DebuggerDisplay( "{ExpressionInfo.Kind}, Operator = {Operator}" )] - private sealed class ExprItem( Expression expression, Operator op, ExpressionInfo expressionInfo ) + [DebuggerDisplay( "{CompareConstraint}, Operator = {Operator}" )] + private sealed class ExprItem( Expression expression, Operator op, CompareConstraint compareConstraint ) { - public ExpressionInfo ExpressionInfo { get; } = expressionInfo; + public CompareConstraint CompareConstraint { get; set; } = compareConstraint; public Expression Expression { get; set; } = expression; public Operator Operator { get; set; } = op; } diff --git a/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs b/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs index b89295dc..dada1a14 100644 --- a/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs +++ b/src/Hyperbee.Json/Filters/Parser/ValueTypeComparer.cs @@ -1,4 +1,5 @@ using Hyperbee.Json.Descriptors; +using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Values; namespace Hyperbee.Json.Filters.Parser; @@ -122,42 +123,39 @@ static void ThrowIfNotNormalized( IValueType nodeType ) public bool In( IValueType left, IValueType right ) { if ( right is not NodeList rightList ) - throw new NotSupportedException( "The right side of an `in` must be a node list." ); + throw new NotSupportedException( "The right side of `in` must be a node list." ); - // Check if the left value is in rightList - if ( left is not NodeList leftList ) - { - // Check if the left value is in rightList - return Find( rightList, left ); - } - - // Check if any element in leftList is in rightList - foreach ( var leftItem in leftList ) - { - if ( !TryGetValueType( accessor, leftItem, out var leftItemValue ) ) - continue; + var rightNode = rightList.OneOrDefault(); - if ( Find( rightList, leftItemValue ) ) - return true; - } - - return false; + if ( rightNode == null || accessor.GetNodeKind( rightNode ) != NodeKind.Array ) + return false; - // Helper method to find a value in a NodeList + return Contains( this, accessor, left, rightNode ); - bool Find( NodeList nodeList, IValueType leftItemValue ) + static bool Contains( IValueTypeComparer comparer, IValueAccessor accessor, IValueType left, TNode rightNode ) { - foreach ( var rightItem in nodeList ) + foreach ( var (rightChild, _, _) in accessor.EnumerateChildren( rightNode ) ) { - if ( TryGetValueType( accessor, rightItem, out var rightItemValue ) && - CompareValues( leftItemValue, rightItemValue, out _ ) == 0 ) - { + var comparand = GetComparand( accessor, rightChild ); + var result = comparer.Compare( left, comparand, Operator.Equals ); + + if ( result == 0 ) return true; - } } return false; } + + static IValueType GetComparand( IValueAccessor accessor, TNode childValue ) + { + return accessor.GetNodeKind( childValue ) switch + { + NodeKind.Value => TryGetValue( accessor, childValue, out var comparand ) ? comparand + : throw new NotSupportedException( "Unsupported value type." ), + + _ => new NodeList( [childValue], true ) + }; + } } public bool Exists( IValueType node ) @@ -184,8 +182,8 @@ private int CompareEnumerables( IEnumerable left, IEnumerable righ return 1; // Left has more elements, so it is greater // if the values can be extracted, compare the values directly - if ( TryGetValueType( accessor, leftEnumerator.Current, out var leftItemValue ) && - TryGetValueType( accessor, rightEnumerator.Current, out var rightItemValue ) ) + if ( TryGetValue( accessor, leftEnumerator.Current, out var leftItemValue ) && + TryGetValue( accessor, rightEnumerator.Current, out var rightItemValue ) ) return CompareValues( leftItemValue, rightItemValue, out _ ); if ( !accessor.DeepEquals( leftEnumerator.Current, rightEnumerator.Current ) ) @@ -208,7 +206,7 @@ private int CompareEnumerableToValue( IEnumerable enumeration, IValueType { nodeCount++; - if ( !TryGetValueType( accessor, item, out var itemValue ) ) + if ( !TryGetValue( accessor, item, out var itemValue ) ) continue; // Skip if value cannot be extracted lastCompare = CompareValues( itemValue, value, out typeMismatch ); @@ -226,7 +224,6 @@ private int CompareEnumerableToValue( IEnumerable enumeration, IValueType } return nodeCount != 1 ? -1 : lastCompare; // Return the last comparison if there is only one node - } private static int CompareValues( IValueType left, IValueType right, out bool typeMismatch ) @@ -275,7 +272,7 @@ static bool IsFloatToIntOperation( IValueType left, IValueType right ) => left is ScalarValue && right is ScalarValue || left is ScalarValue && right is ScalarValue; } - private static bool TryGetValueType( IValueAccessor accessor, TNode node, out IValueType nodeType ) + private static bool TryGetValue( IValueAccessor accessor, TNode node, out IValueType nodeType ) { if ( accessor.TryGetValueFromNode( node, out var itemValue ) ) { diff --git a/src/Hyperbee.Json/Internal/JsonElementAccessor.cs b/src/Hyperbee.Json/Internal/JsonElementAccessor.cs index 99167f89..f72347d9 100644 --- a/src/Hyperbee.Json/Internal/JsonElementAccessor.cs +++ b/src/Hyperbee.Json/Internal/JsonElementAccessor.cs @@ -6,9 +6,9 @@ namespace Hyperbee.Json.Internal; internal static class JsonElementAccessor { - // We need to identify an element's unique metadata location to establish instance identity. + // We need to identify an element's unique metadata location to establish positional identity. // Deeply nested elements can have the same value but different locations in the document. - // Deep compare is not sufficient to establish instance identity in such cases from either a + // Deep compare is not sufficient to establish positional identity in such cases from either a // correctness or performance perspective. We can use the private _idx field of JsonElement // to identify the element's unique location in the document. // diff --git a/src/Hyperbee.Json/JsonPath.cs b/src/Hyperbee.Json/JsonPath.cs index 51bfe4b1..d23d74df 100644 --- a/src/Hyperbee.Json/JsonPath.cs +++ b/src/Hyperbee.Json/JsonPath.cs @@ -96,7 +96,7 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N { var stack = new NodeArgsStack(); - var (accessor, filterEvaluator) = Descriptor; + var (accessor, filterRuntime) = Descriptor; do { @@ -205,7 +205,7 @@ private static IEnumerable EnumerateMatches( TNode root, NodeArgs args, N { foreach ( var (childValue, childKey, childKind) in accessor.EnumerateChildren( value ) ) { - if ( !filterEvaluator.Evaluate( selector[1..], childValue, root ) ) // remove the leading '?' character + if ( !filterRuntime.Evaluate( selector[1..], childValue, root ) ) // remove the leading '?' character continue; // optimization: quicker return for tail values @@ -299,18 +299,16 @@ private static (int Upper, int Lower, int Step) GetSliceRange( TNode value, stri var (lower, upper, step) = JsonPathSliceSyntaxHelper.ParseExpression( sliceExpr, length, reverse: true ); if ( step < 0 ) - { (lower, upper) = (upper, lower); - } return (upper, lower, step); } [DebuggerDisplay( "Parent = {Parent}, Value = {Value}, {Segment}" )] - private record struct NodeArgs( TNode Parent, TNode Value, string Key, JsonPathSegment Segment, NodeFlags Flags ); + private readonly record struct NodeArgs( TNode Parent, TNode Value, string Key, JsonPathSegment Segment, NodeFlags Flags ); [DebuggerDisplay( "{_stack}" )] - private sealed class NodeArgsStack( int capacity = 16 ) + private sealed class NodeArgsStack( int capacity = 8 ) { [DebuggerBrowsable( DebuggerBrowsableState.RootHidden )] private readonly Stack _stack = new( capacity ); @@ -327,4 +325,5 @@ public bool TryPop( out NodeArgs args ) return _stack.TryPop( out args ); } } + } diff --git a/src/Hyperbee.Json/JsonPathBuilder.cs b/src/Hyperbee.Json/JsonPathBuilder.cs index 0f76e09b..a059ab50 100644 --- a/src/Hyperbee.Json/JsonPathBuilder.cs +++ b/src/Hyperbee.Json/JsonPathBuilder.cs @@ -7,27 +7,37 @@ namespace Hyperbee.Json; public class JsonPathBuilder { - private readonly JsonElement _rootElement; private readonly JsonElementPositionComparer _comparer = new(); private readonly Dictionary _parentMap = []; + private JsonElement _rootElement; + + public JsonPathBuilder() + { + } public JsonPathBuilder( JsonDocument rootDocument ) - : this( rootDocument.RootElement ) { + SetRootElement( rootDocument.RootElement ); } public JsonPathBuilder( JsonElement rootElement ) { - _rootElement = rootElement; - - // avoid allocating full paths for every node by building - // a dictionary of (parentId, segment) pairs. + SetRootElement( rootElement ); + } + private void SetRootElement( in JsonElement rootElement ) + { + _rootElement = rootElement; _parentMap[GetUniqueId( _rootElement )] = (-1, "$"); // seed parent map with root } public string GetPath( in JsonElement targetElement ) { + // make sure the root element is set + + if ( _rootElement.ValueKind == JsonValueKind.Undefined ) + SetRootElement( GetDocument( targetElement ).RootElement ); + // quick out var targetId = GetUniqueId( targetElement ); @@ -108,6 +118,12 @@ private static int GetUniqueId( in JsonElement element ) return JsonElementAccessor.GetIdx( element ); } + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static JsonDocument GetDocument( in JsonElement element ) + { + return JsonElementAccessor.GetParent( element ); + } + private static string BuildPath( in int currentId, Dictionary parentMap ) { // use recursion to reduce allocations by avoiding a stack diff --git a/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs b/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs index 0faaa739..86884ef7 100644 --- a/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs +++ b/src/Hyperbee.Json/JsonPathSliceSyntaxHelper.cs @@ -75,7 +75,7 @@ static int ParsePart( ReadOnlySpan part, int defaultValue ) } // helper to get bounded values - // per https://datatracker.ietf.org/doc/rfc9535/ 2.3.4.2.2. + // https://www.rfc-editor.org/rfc/rfc9535.html#section-2.3.4.2.2 private static (int Lower, int Upper, int Step) GetBoundedValues( int start, int end, int step, int length, bool reverse ) { diff --git a/test/Hyperbee.Json.Cts/AssertExtensions.cs b/test/Hyperbee.Json.Cts/TestSupport/AssertExtensions.cs similarity index 97% rename from test/Hyperbee.Json.Cts/AssertExtensions.cs rename to test/Hyperbee.Json.Cts/TestSupport/AssertExtensions.cs index 4e0f379b..c29cb95b 100644 --- a/test/Hyperbee.Json.Cts/AssertExtensions.cs +++ b/test/Hyperbee.Json.Cts/TestSupport/AssertExtensions.cs @@ -1,4 +1,4 @@ -namespace Hyperbee.Json.Cts; +namespace Hyperbee.Json.Cts.TestSupport; public static class AssertExtensions { diff --git a/test/Hyperbee.Json.Cts/TestSupport/JsonElementHelper.cs b/test/Hyperbee.Json.Cts/TestSupport/JsonElementHelper.cs index 7aa2e237..cd844d3b 100644 --- a/test/Hyperbee.Json.Cts/TestSupport/JsonElementHelper.cs +++ b/test/Hyperbee.Json.Cts/TestSupport/JsonElementHelper.cs @@ -3,7 +3,7 @@ namespace Hyperbee.Json.Cts.TestSupport; -public class JsonPathDocument( string source ) : IJsonDocument +public class JsonElementDocument( string source ) : IJsonDocument { private JsonDocument Document { get; } = JsonDocument.Parse( source ); diff --git a/test/Hyperbee.Json.Cts/TestSupport/JsonNodeHelper.cs b/test/Hyperbee.Json.Cts/TestSupport/JsonNodeHelper.cs index 065f77cb..90edca29 100644 --- a/test/Hyperbee.Json.Cts/TestSupport/JsonNodeHelper.cs +++ b/test/Hyperbee.Json.Cts/TestSupport/JsonNodeHelper.cs @@ -3,7 +3,7 @@ namespace Hyperbee.Json.Cts.TestSupport; -public class JsonPathNode( string source ) : IJsonDocument +public class JsonNodeDocument( string source ) : IJsonDocument { private JsonNode? Document { get; } = JsonNode.Parse( source ); diff --git a/test/Hyperbee.Json.Cts/TestSupport/TestHelper.cs b/test/Hyperbee.Json.Cts/TestSupport/TestHelper.cs index 440f4087..c060ed80 100644 --- a/test/Hyperbee.Json.Cts/TestSupport/TestHelper.cs +++ b/test/Hyperbee.Json.Cts/TestSupport/TestHelper.cs @@ -8,10 +8,10 @@ internal static class TestHelper public static IJsonDocument Parse( Type target, string source ) { if ( target == typeof( JsonElement ) ) - return new JsonPathDocument( source ); + return new JsonElementDocument( source ); if ( target == typeof( JsonNode ) ) - return new JsonPathNode( source ); + return new JsonNodeDocument( source ); throw new NotSupportedException(); } diff --git a/test/Hyperbee.Json.Tests/Builder/JsonPathBuilderTests.cs b/test/Hyperbee.Json.Tests/Builder/JsonPathBuilderTests.cs index 39dd921a..14bd9608 100644 --- a/test/Hyperbee.Json.Tests/Builder/JsonPathBuilderTests.cs +++ b/test/Hyperbee.Json.Tests/Builder/JsonPathBuilderTests.cs @@ -13,7 +13,13 @@ public class JsonPathBuilderTests : JsonTestBase [DataRow( "$['store']['book'][1]['author']", "$.store.book[1].author" )] [DataRow( "$['store']['book'][2]['author']", "$.store.book[2].author" )] [DataRow( "$['store']['book'][3]['author']", "$.store.book[3].author" )] - public void Should_GetPath( string pointer, string expected ) + [DataRow( "$['store']['book'][0]['category']", "$.store.book[0].category" )] + [DataRow( "$['store']['book'][1]['title']", "$.store.book[1].title" )] + [DataRow( "$['store']['book'][2]['isbn']", "$.store.book[2].isbn" )] + [DataRow( "$['store']['book'][3]['price']", "$.store.book[3].price" )] + [DataRow( "$['store']['bicycle']['color']", "$.store.bicycle.color" )] + [DataRow( "$['store']['bicycle']['price']", "$.store.bicycle.price" )] + public void GetPath( string pointer, string expected ) { var source = GetDocument(); var target = source.RootElement.FromJsonPathPointer( pointer ); diff --git a/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs b/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs index 11129dc7..cede4e0a 100644 --- a/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs +++ b/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System; +using System.Text.Json; using Hyperbee.Json.Dynamic; using Hyperbee.Json.Extensions; using Hyperbee.Json.Tests.TestSupport; @@ -18,7 +19,7 @@ private enum Thing } [TestMethod] - public void DynamicJsonElement_ShouldReturnCorrectResults() + public void DynamicHelperConvert() { var source = GetDocument(); var element = JsonDynamicHelper.ConvertToDynamic( source ); @@ -27,12 +28,15 @@ public void DynamicJsonElement_ShouldReturnCorrectResults() var author = book.author; var price = book.price; + var path = book.Path(); + Assert.IsTrue( price == 8.95 ); Assert.IsTrue( author == "Nigel Rees" ); + Assert.AreEqual( "$.store.book[0]", path ); } [TestMethod] - public void DynamicJsonConverter_ShouldReturnCorrectResults() + public void DynamicSerializerConverter() { var jobject = JsonSerializer.Deserialize( ReadJsonString(), SerializerOptions ); @@ -45,5 +49,40 @@ public void DynamicJsonConverter_ShouldReturnCorrectResults() Assert.IsNotNull( output ); Assert.IsTrue( output.Contains( "ThatThing" ) ); } + + [TestMethod] + public void DynamicModifyExistingProperty() + { + var jobject = JsonSerializer.Deserialize( ReadJsonString(), SerializerOptions ); + + jobject.store.book[0].price = 9.99; + + var modifiedPrice = jobject.store.book[0].price; + + Assert.AreEqual( 9.99, modifiedPrice ); + } + + [TestMethod] + public void DynamicAddNewProperty() + { + var jobject = JsonSerializer.Deserialize( ReadJsonString(), SerializerOptions ); + + jobject.store.newProperty = "NewValue"; + + var newValue = jobject.store.newProperty; + + Assert.AreEqual( "NewValue", newValue ); + } + + [TestMethod] + public void DynamicArrayAccessOutOfBounds() + { + var jobject = JsonSerializer.Deserialize( ReadJsonString(), SerializerOptions ); + + Assert.ThrowsException( () => + { + _ = jobject.store.book[10]; + } ); + } } diff --git a/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs b/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs deleted file mode 100644 index 7f6da371..00000000 --- a/test/Hyperbee.Json.Tests/Extensions/JsonExtensionTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Text.Json; -using Hyperbee.Json.Extensions; -using Hyperbee.Json.Tests.TestSupport; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Hyperbee.Json.Tests.Extensions; - -[TestClass] -public class JsonExtensionTests : JsonTestBase -{ - public struct TestItem - { - public string A { get; set; } - public string B { get; set; } - } - - [TestMethod] - public void Should_ReturnPropertyValue_ForJsonPathPointer() - { - // arrange - const string json = """ - { - "message": "The operation was successful", - "status": 200, - "timestamp": { - "$date": "2021-07-24T20:14:06.613Z" - }, - "assets": [ - { - "hash": "22e1ea7a1c694262159271851eb6cff001fb39bf8e5edc795a345a771b2c3ffc", - "owners": [], - "asset": { - "code": "#load" - }, - "votes": [] - } - ] - } - """; - - var document = JsonDocument.Parse( json ); - - // act - var result = document.RootElement.FromJsonPathPointer( "$.assets[0].asset.['code']" ).GetString(); - - // asset - Assert.AreEqual( "#load", result ); - } -} diff --git a/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs b/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs index 905d8f67..9a197b05 100644 --- a/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs @@ -14,7 +14,7 @@ namespace Hyperbee.Json.Tests.Parsers; public class ExtensionFunctionTests : JsonTestBase { [TestMethod] - public void Should_CallCustomFunction() + public void CallCustomFunction() { // arrange var source = GetDocument(); @@ -32,7 +32,7 @@ public void Should_CallCustomFunction() Assert.AreEqual( "$.store.book[2].title", results[0].GetPath() ); } - private class PathNodeFunction() : ExtensionFunction( PathMethod, ExtensionInfo.MustCompare ) + private class PathNodeFunction() : ExtensionFunction( PathMethod, CompareConstraint.MustCompare ) { public const string Name = "path"; private static readonly MethodInfo PathMethod = GetMethod( nameof( Path ) ); diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs index 3a17c5d1..22e1331f 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs @@ -1,9 +1,9 @@ using System; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; -using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Tests.TestSupport; @@ -27,15 +27,15 @@ public class FilterParserTests : JsonTestBase [DataRow( "(1 == 1)", true, typeof( JsonNode ) )] [DataRow( "(1 != 2)", true, typeof( JsonNode ) )] [DataRow( "!(1 == 2)", true, typeof( JsonNode ) )] - [DataRow( "(\"world\" == 'world') || 1 == 1", true, typeof( JsonNode ) )] + [DataRow( "(\"world\" == 'world') || 1 == 1", true, typeof( JsonNode ) )] [DataRow( "!('World' != 'World') && !(1 == 2 || 1 == 3)", true, typeof( JsonNode ) )] - public void Should_MatchExpectedResult_WhenUsingConstants( string filter, bool expected, Type sourceType ) + public void MatchExpectedResult_WhenUsingConstants( string filter, bool expected, Type sourceType ) { // arrange var (expression, param) = GetExpression( filter, sourceType ); // act - var result = Execute( expression, param, sourceType ); + var result = ExecuteExpression( expression, param, sourceType ); // assert Assert.AreEqual( expected, result ); @@ -46,7 +46,7 @@ public void Should_MatchExpectedResult_WhenUsingConstants( string filter, bool e [DataRow( "false", typeof( JsonElement ) )] [DataRow( "true", typeof( JsonNode ) )] [DataRow( "false", typeof( JsonNode ) )] - public void Should_Fail_WhenNotComparingLiterals( string filter, Type sourceType ) + public void Fail_WhenNotComparingLiterals( string filter, Type sourceType ) { // arrange @@ -54,7 +54,7 @@ public void Should_Fail_WhenNotComparingLiterals( string filter, Type sourceType Assert.ThrowsException( () => { var (expression, param) = GetExpression( filter, sourceType ); - return Execute( expression, param, sourceType ); + return ExecuteExpression( expression, param, sourceType ); } ); } @@ -89,7 +89,7 @@ public void Should_Fail_WhenNotComparingLiterals( string filter, Type sourceType [DataRow( "@.store.nothing", false, typeof( JsonNode ) )] [DataRow( "@.store.bicycle.price", true, typeof( JsonNode ) )] [DataRow( "@.store.book[0].category", true, typeof( JsonNode ) )] - public void Should_MatchExpectedResult_WhenUsingJsonPath( string filter, bool expected, Type sourceType ) + public void MatchExpectedResult_WhenUsingJsonPath( string filter, bool expected, Type sourceType ) { // arrange & act var result = CompileAndExecuteFilter( filter, sourceType ); @@ -109,18 +109,22 @@ public void Should_MatchExpectedResult_WhenUsingJsonPath( string filter, bool ex [DataRow( "$.store.book[?(@.price < 9.00 && @.category == 'reference')].price", 8.95F, typeof( JsonNode ) )] [DataRow( "$.store.book[?(match(@.title, \"Sayings.*\" ))].price", 8.95F, typeof( JsonNode ) )] [DataRow( "$.store.book[?(@.category == $.store.book[0].category)].price", 8.95F, typeof( JsonNode ) )] - public void Should_ReturnExpectedResult_WhenUsingExpressionEvaluator( string filter, float expected, Type sourceType ) + public void ReturnExpectedResult_WhenUsingExpressionEvaluator( string filter, float expected, Type sourceType ) { // arrange & act - var result = Select( filter, sourceType ); + var document = GetDocumentAdapter( sourceType ); + + // act + var matches = document.Select( filter ).ToArray(); + var result = TestHelper.GetSingle( matches[0] ); // assert Assert.AreEqual( expected, result ); } [DataTestMethod] - [DataRow( "count(@.store.book) == 1", true, typeof( JsonElement ) )] // - [DataRow( "count(@.store.book.*) == 4", true, typeof( JsonElement ) )] // + [DataRow( "count(@.store.book) == 1", true, typeof( JsonElement ) )] + [DataRow( "count(@.store.book.*) == 4", true, typeof( JsonElement ) )] [DataRow( "length(@.store.book) == 4", true, typeof( JsonElement ) )] [DataRow( "length(@.store.book[0].category) == 9", true, typeof( JsonElement ) )] [DataRow( "match(@.store.book[0].title, \"Sayings.*\" )", true, typeof( JsonElement ) )] @@ -133,7 +137,7 @@ public void Should_ReturnExpectedResult_WhenUsingExpressionEvaluator( string fil [DataRow( "match(@.store.book[0].title, \"Sayings.*\" )", true, typeof( JsonNode ) )] [DataRow( "search(@.store.book[0].author, \"[Nn]igel Rees\" )", true, typeof( JsonNode ) )] [DataRow( "value(@.store.book[0].author) == \"Nigel Rees\"", true, typeof( JsonNode ) )] - public void Should_MatchExpectedResult_WhenUsingFunctions( string filter, bool expected, Type sourceType ) + public void MatchExpectedResult_WhenUsingFunctions( string filter, bool expected, Type sourceType ) { // arrange & act var result = CompileAndExecuteFilter( filter, sourceType ); @@ -151,7 +155,7 @@ public void Should_MatchExpectedResult_WhenUsingFunctions( string filter, bool e [DataRow( " 4 == length(@.store.book)", true, typeof( JsonElement ) )] [DataRow( " 4 == length(@.store.book) ", true, typeof( JsonElement ) )] [DataRow( " 4 == length( @.store.book ) ", true, typeof( JsonElement ) )] - public void Should_MatchExpectedResult_WhenHasExtraSpaces( string filter, bool expected, Type sourceType ) + public void MatchExpectedResult_WhenHasExtraSpaces( string filter, bool expected, Type sourceType ) { // arrange & act var result = CompileAndExecuteFilter( filter, sourceType ); @@ -163,7 +167,7 @@ public void Should_MatchExpectedResult_WhenHasExtraSpaces( string filter, bool e [DataTestMethod] [DataRow( "4 == length ( @.store.book )", typeof( JsonElement ) )] [DataRow( "length (@.store.book) == 4", typeof( JsonElement ) )] - public void Should_Fail_WhenHasInvalidWhitespace( string filter, Type sourceType ) + public void Fail_WhenHasInvalidWhitespace( string filter, Type sourceType ) { Assert.ThrowsException( () => CompileAndExecuteFilter( filter, sourceType ) ); } @@ -178,89 +182,57 @@ public void Should_Fail_WhenHasInvalidWhitespace( string filter, Type sourceType [DataRow( "(1 == ", typeof( JsonElement ) )] [DataRow( "== 1", typeof( JsonElement ) )] [DataRow( "badMethod(1)", typeof( JsonElement ) )] - public void Should_FailToParse_WhenUsingInvalidFilters( string filter, Type sourceType ) + public void FailToParse_WhenUsingInvalidFilters( string filter, Type sourceType ) { - try - { - GetExpression( filter, sourceType ); - } - catch - { - // Most are FormatExceptions, but some are ArgumentExceptions - return; - } - - Assert.Fail( "Did not throw an exception" ); + AssertExtensions.ThrowsAny( () => GetExpression( filter, sourceType ) ); } - private static (Expression, ParameterExpression) GetExpression( string filter, Type sourceType ) + // Helper methods + + public static T InvokeGenericMethod( string methodName, Type sourceType, params object[] args ) { - if ( sourceType == typeof( JsonElement ) ) + try { - var runtimeContext = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); - return (FilterParser.Parse( filter ), runtimeContext); - } + var method = typeof( FilterParserTests ) + .GetMethods( BindingFlags.NonPublic | BindingFlags.Static ) + .First( x => x.Name == methodName && x.IsGenericMethodDefinition ) + .MakeGenericMethod( sourceType ); - if ( sourceType == typeof( JsonNode ) ) - { - var runtimeContext = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); - return (FilterParser.Parse( filter ), runtimeContext); + return (T) method.Invoke( null, args )!; } - - throw new NotImplementedException(); - } - - private static bool Execute( Expression expression, ParameterExpression param, Type sourceType ) - { - if ( sourceType == typeof( JsonElement ) ) + catch ( Exception ex ) { - var filterFunc = Expression - .Lambda, bool>>( expression, param ) - .Compile(); - - var runtimeContext = new FilterRuntimeContext( new JsonElement(), new JsonElement() ); - - return filterFunc( runtimeContext ); + throw ex.InnerException!; } + } - if ( sourceType == typeof( JsonNode ) ) - { - var filterFunc = Expression - .Lambda, bool>>( expression, param ) - .Compile(); + public static (Expression, ParameterExpression) GetExpression( string filter, Type sourceType ) => + InvokeGenericMethod<(Expression, ParameterExpression)>( nameof( GetExpression ), sourceType, filter ); - var runtimeContext = new FilterRuntimeContext( new JsonObject(), new JsonObject() ); + public static bool CompileAndExecuteFilter( string filter, Type sourceType ) => + InvokeGenericMethod( nameof( CompileAndExecuteFilter ), sourceType, filter ); - return filterFunc( runtimeContext ); - } + public static bool ExecuteExpression( Expression expression, ParameterExpression param, Type sourceType ) => + InvokeGenericMethod( nameof( ExecuteExpression ), sourceType, expression, param ); - throw new NotImplementedException(); + private static (Expression, ParameterExpression) GetExpression( string filter ) + { + var runtimeContext = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); + return (FilterParser.Parse( filter ), runtimeContext); } - private static bool CompileAndExecuteFilter( string filter, Type sourceType ) + private static bool ExecuteExpression( Expression expression, ParameterExpression param ) { - if ( sourceType == typeof( JsonElement ) ) - { - var source = GetDocument(); - var filterFunc = FilterParser.Compile( filter ); - var runtimeContext = new FilterRuntimeContext( source.RootElement, source.RootElement ); - - return filterFunc( runtimeContext ); - } - else - { - var source = GetDocument(); - var filterFunc = FilterParser.Compile( filter ); - var runtimeContext = new FilterRuntimeContext( source, source ); - - return filterFunc( runtimeContext ); - } + var filterFunc = Expression.Lambda, bool>>( expression, param ).Compile(); + var runtimeContext = new FilterRuntimeContext( default, default ); // Initialize with default values + return filterFunc( runtimeContext ); } - private static float Select( string filter, Type sourceType ) + private static bool CompileAndExecuteFilter( string filter ) { - return sourceType == typeof( JsonElement ) - ? GetDocument().Select( filter ).First().GetSingle() - : GetDocument().Select( filter ).First().GetValue(); + var source = GetDocument(); + var filterFunc = FilterParser.Compile( filter ); + var runtimeContext = new FilterRuntimeContext( source, source ); + return filterFunc( runtimeContext ); } } diff --git a/test/Hyperbee.Json.Tests/Parsers/JsonPathPointerTests.cs b/test/Hyperbee.Json.Tests/Parsers/JsonPathPointerTests.cs new file mode 100644 index 00000000..2e7d7dd9 --- /dev/null +++ b/test/Hyperbee.Json.Tests/Parsers/JsonPathPointerTests.cs @@ -0,0 +1,55 @@ +using System.Text.Json; +using Hyperbee.Json.Extensions; +using Hyperbee.Json.Tests.TestSupport; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Hyperbee.Json.Tests.Parsers; + +[TestClass] +public class JsonPathPointerTests : JsonTestBase +{ + [DataTestMethod] + [DataRow( "$.message", "The operation was successful" )] + [DataRow( "$.status", 200 )] + [DataRow( "$.timestamp['$date']", "2021-07-24T20:14:06.613Z" )] + [DataRow( "$.assets[0].hash", "22e1ea7a1c694262159271851eb6cff001fb39bf8e5edc795a345a771b2c3ffc" )] + [DataRow( "$.assets[0].asset['code']", "#load" )] + public void ValueFromJsonPathPointer( string pointer, object expected ) + { + // arrange + const string json = + """ + { + "message": "The operation was successful", + "status": 200, + "timestamp": { + "$date": "2021-07-24T20:14:06.613Z" + }, + "assets": [ + { + "hash": "22e1ea7a1c694262159271851eb6cff001fb39bf8e5edc795a345a771b2c3ffc", + "owners": [], + "asset": { + "code": "#load" + }, + "votes": [] + } + ] + } + """; + + var document = JsonDocument.Parse( json ); + + // act + var target = document.RootElement.FromJsonPathPointer( pointer ); + object result = target.ValueKind switch + { + JsonValueKind.String => target.GetString(), + JsonValueKind.Number => target.GetInt32(), + _ => target.GetRawText() + }; + + // assert + Assert.AreEqual( expected, result ); + } +} diff --git a/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs index d0b905c1..ec224356 100644 --- a/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs @@ -8,67 +8,53 @@ namespace Hyperbee.Json.Tests.Parsers; public class JsonPathQueryParserTests { [DataTestMethod] - [DataRow( "$", "{$|s}" )] - [DataRow( "$.two.some", "{$|s};{two|s};{some|s}" )] - [DataRow( "$.thing[1:2:3]", "{$|s};{thing|s};{1:2:3|g}" )] - [DataRow( "$..thing[?(@.x == 1)]", "{$|s};{..|g};{thing|s};{?(@.x == 1)|g}" )] - [DataRow( "$['two.some']", "{$|s};{two.some|s}" )] - [DataRow( "$.two.some.thing['this.or.that']", "{$|s};{two|s};{some|s};{thing|s};{this.or.that|s}" )] - [DataRow( "$.store.book[*].author", "{$|s};{store|s};{book|s};{*|g};{author|s}" )] - [DataRow( "@..author", "{@|s};{..|g};{author|s}" )] - [DataRow( "$.store.*", "{$|s};{store|s};{*|g}" )] - [DataRow( "$.store..price", "{$|s};{store|s};{..|g};{price|s}" )] - [DataRow( "$..book[2]", "{$|s};{..|g};{book|s};{2|s}" )] - [DataRow( "$..book[-1:]", "{$|s};{..|g};{book|s};{-1:|g}" )] - [DataRow( "$..book[:2]", "{$|s};{..|g};{book|s};{:2|g}" )] - [DataRow( "$..book[0,1]", "{$|s};{..|g};{book|s};{1,0|g}" )] - [DataRow( "$.store.book[0,1]", "{$|s};{store|s};{book|s};{1,0|g}" )] - [DataRow( "$..book['category','author']", "{$|s};{..|g};{book|s};{author,category|g}" )] - [DataRow( "$..book[?(@.isbn)]", "{$|s};{..|g};{book|s};{?(@.isbn)|g}" )] - [DataRow( "$..book[?@.isbn]", "{$|s};{..|g};{book|s};{?@.isbn|g}" )] - [DataRow( "$..book[?(@.price<10)]", "{$|s};{..|g};{book|s};{?(@.price<10)|g}" )] - [DataRow( "$..book[?@.price<10]", "{$|s};{..|g};{book|s};{?@.price<10|g}" )] - [DataRow( "$..*", "{$|s};{..|g};{*|g}" )] - [DataRow( """$.store.book[?(@path !== "$['store']['book'][0]")]""", """{$|s};{store|s};{book|s};{?(@path !== "$['store']['book'][0]")|g}""" )] - [DataRow( """$..book[?(@.price == 8.99 && @.category == "fiction")]""", """{$|s};{..|g};{book|s};{?(@.price == 8.99 && @.category == "fiction")|g}""" )] - public void Should_TokenizeJsonPath( string jsonPath, string expected ) + [DataRow( "$", "[$ => 1]" )] + [DataRow( "$.two.some", "[$ => 1][two => 1][some => 1]" )] + [DataRow( "$.thing[1:2:3]", "[$ => 1][thing => 1][1:2:3 => #]" )] + [DataRow( "$..thing[?(@.x == 1)]", "[$ => 1][.. => #][thing => 1][?(@.x == 1) => #]" )] + [DataRow( "$['two.some']", "[$ => 1][two.some => 1]" )] + [DataRow( "$.two.some.thing['this.or.that']", "[$ => 1][two => 1][some => 1][thing => 1][this.or.that => 1]" )] + [DataRow( "$.store.book[*].author", "[$ => 1][store => 1][book => 1][* => #][author => 1]" )] + [DataRow( "@..author", "[@ => 1][.. => #][author => 1]" )] + [DataRow( "$.store.*", "[$ => 1][store => 1][* => #]" )] + [DataRow( "$.store..price", "[$ => 1][store => 1][.. => #][price => 1]" )] + [DataRow( "$..book[2]", "[$ => 1][.. => #][book => 1][2 => 1]" )] + [DataRow( "$..book[-1:]", "[$ => 1][.. => #][book => 1][-1: => #]" )] + [DataRow( "$..book[:2]", "[$ => 1][.. => #][book => 1][:2 => #]" )] + [DataRow( "$..book[0,1]", "[$ => 1][.. => #][book => 1][0,1 => #]" )] + [DataRow( "$.store.book[0,1]", "[$ => 1][store => 1][book => 1][0,1 => #]" )] + [DataRow( "$..book['category','author']", "[$ => 1][.. => #][book => 1][category,author => #]" )] + [DataRow( "$..book[?(@.isbn)]", "[$ => 1][.. => #][book => 1][?(@.isbn) => #]" )] + [DataRow( "$..book[?@.isbn]", "[$ => 1][.. => #][book => 1][?@.isbn => #]" )] + [DataRow( "$..book[?(@.price<10)]", "[$ => 1][.. => #][book => 1][?(@.price<10) => #]" )] + [DataRow( "$..book[?@.price<10]", "[$ => 1][.. => #][book => 1][?@.price<10 => #]" )] + [DataRow( "$..*", "[$ => 1][.. => #][* => #]" )] + [DataRow( "$..book[?(@.price == 8.99 && @.category == \"fiction\")]", "[$ => 1][.. => #][book => 1][?(@.price == 8.99 && @.category == \"fiction\") => #]" )] + public void TokenizeJsonPath( string jsonPath, string expected ) { // act var compiledQuery = JsonPathQueryParser.Parse( jsonPath ); // arrange - var result = SegmentsToString( compiledQuery.Segments ); + var result = GetResultString( compiledQuery.Segments ); // assert Assert.AreEqual( expected, result ); + return; - static string SegmentsToString( JsonPathSegment segment ) + static string GetResultString( JsonPathSegment segment ) { - return string.Join( ';', segment.AsEnumerable().Select( SegmentToString ) ); + return string.Join( "", segment.AsEnumerable().Select( ConvertToString ) ); - static string SegmentToString( JsonPathSegment segment ) + static string ConvertToString( JsonPathSegment segment ) { var (singular, selectors) = segment; - var selectorType = singular ? "s" : "g"; // s:singular, g:group - var selectorsString = string.Join( ',', selectors.Select( x => x.Value ) ); + var selectorType = singular ? "1" : "#"; // 1:singular, #:group + var selectorsString = string.Join( ',', selectors.Select( x => x.Value ).Reverse() ); - return $"{{{selectorsString}|{selectorType}}}"; + return $"[{selectorsString} => {selectorType}]"; } } } - - [TestMethod] - public void ShouldFilterExpressionWithParentAxisOperator() - { - // NOT-SUPPORTED: parent axis operator is not supported - - // act & assert - const string jsonPath = "$[*].bookmarks[ ? (@.page == 45)]^^^"; - - Assert.ThrowsException( () => - { - _ = JsonPathQueryParser.Parse( jsonPath ); - } ); - } } diff --git a/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs b/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs index e1116256..801fa125 100644 --- a/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/ValueTypeComparerTests.cs @@ -6,7 +6,7 @@ using Hyperbee.Json.Tests.TestSupport; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Hyperbee.Json.Tests.Query; +namespace Hyperbee.Json.Tests.Parsers; [TestClass] public class NodeTypeComparerTests : JsonTestBase @@ -23,12 +23,12 @@ public class NodeTypeComparerTests : JsonTestBase [DataRow( "hello", 11F, false )] [DataRow( false, 11F, false )] [DataRow( true, 11F, false )] - public void NodeTypeComparer_ShouldCompare_WithEqualResults( object left, object right, bool areEqual ) + public void Compare_WithEqualResults( object left, object right, bool areEqual ) { // Arrange var comparer = GetComparer(); - var a = GetNodeType( left ); - var b = GetNodeType( right ); + var a = GetNodeValue( left ); + var b = GetNodeValue( right ); // Act var result = comparer.Compare( a, b, Operator.Equals ) == 0; @@ -46,12 +46,12 @@ public void NodeTypeComparer_ShouldCompare_WithEqualResults( object left, object [DataRow( 10F, 10F, true )] [DataRow( 14F, 10F, true )] [DataRow( 1F, 14F, false )] - public void NodeTypeComparer_ShouldCompare_WithGreaterResults( object left, object right, bool areEqual ) + public void Compare_WithGreaterResults( object left, object right, bool areEqual ) { // Arrange var comparer = GetComparer(); - var a = GetNodeType( left ); - var b = GetNodeType( right ); + var a = GetNodeValue( left ); + var b = GetNodeValue( right ); // Act var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) >= 0; @@ -66,12 +66,12 @@ public void NodeTypeComparer_ShouldCompare_WithGreaterResults( object left, obje [DataRow( """{ "value": "hello" }""", "world", false )] [DataRow( """{ "value": "hello" }""", "hello", true )] [DataRow( """{ "value": { "child": 5 } }""", "hello", false )] - public void NodeTypeComparer_ShouldCompare_WithJsonObjectResults( string left, object right, bool areEqual ) + public void Compare_WithJsonObjectResults( string left, object right, bool areEqual ) { // Arrange var comparer = GetComparer(); - var a = GetNodeType( new List { JsonNode.Parse( left )!["value"] } ); - var b = GetNodeType( right ); + var a = GetNodeValue( new List { JsonNode.Parse( left )!["value"] } ); + var b = GetNodeValue( right ); // Act var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) == 0; @@ -85,12 +85,12 @@ public void NodeTypeComparer_ShouldCompare_WithJsonObjectResults( string left, o [DataRow( """["hello","hi","world" ]""", "hi", true )] [DataRow( """[1,2,3]""", 99F, false )] [DataRow( """["hello","world" ]""", "hi", false )] - public void NodeTypeComparer_ShouldCompare_WithLeftJsonArray( string left, object right, bool areEqual ) + public void Compare_WithLeftJsonArray( string left, object right, bool areEqual ) { // Arrange var comparer = GetComparer(); - var a = GetNodeType( JsonNode.Parse( left )!.AsArray() ); - var b = GetNodeType( right ); + var a = GetNodeValue( JsonNode.Parse( left )!.AsArray() ); + var b = GetNodeValue( right ); // Act var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) == 0; @@ -104,12 +104,12 @@ public void NodeTypeComparer_ShouldCompare_WithLeftJsonArray( string left, objec [DataRow( "hi", """["hello","hi","world" ]""", true )] [DataRow( 99F, """[1,2,3]""", false )] [DataRow( "hi", """["hello","world" ]""", false )] - public void NodeTypeComparer_ShouldCompare_WithRightJsonArray( object left, string right, bool areEqual ) + public void Compare_WithRightJsonArray( object left, string right, bool areEqual ) { // Arrange var comparer = GetComparer(); - var a = GetNodeType( left ); - var b = GetNodeType( JsonNode.Parse( right )!.AsArray() ); + var a = GetNodeValue( left ); + var b = GetNodeValue( JsonNode.Parse( right )!.AsArray() ); // Act var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) == 0; @@ -119,7 +119,7 @@ public void NodeTypeComparer_ShouldCompare_WithRightJsonArray( object left, stri } [TestMethod] - public void NodeTypeComparer_ShouldCompare_WithEmpty() + public void Compare_WithEmpty() { var comparer = GetComparer(); var a = new NodeList( [], true ); @@ -135,9 +135,11 @@ public void NodeTypeComparer_ShouldCompare_WithEmpty() Assert.IsTrue( comparer.Compare( a, b, Operator.NotEquals ) != 0 ); } + // Helper methods + private static ValueTypeComparer GetComparer() => new( new NodeValueAccessor() ); - private static IValueType GetNodeType( object item ) + private static IValueType GetNodeValue( object item ) { return item switch { diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathInOperationsTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathInOperationsTests.cs index b4644f37..a563d285 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathInOperationsTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathInOperationsTests.cs @@ -11,11 +11,11 @@ namespace Hyperbee.Json.Tests.Query; public class JsonPathInOperationsTests : JsonTestBase { [DataTestMethod] - [DataRow( "$[?(@.value in [1, 42, 100])]", typeof( JsonDocument ) )] - [DataRow( "$[?(@.value in [1, 42, 100])]", typeof( JsonNode ) )] - [DataRow( "$[?(@.value in ['a', 'b', 'c'])]", typeof( JsonDocument ) )] - [DataRow( "$[?(@.value in ['a', 'b', 'c'])]", typeof( JsonNode ) )] - public void InOperation_SingleValue( string query, Type sourceType ) + [DataRow( "$[?(@.value in [1, 42, 100])]", "$[0]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.value in [1, 42, 100])]", "$[0]", typeof( JsonNode ) )] + [DataRow( "$[?(@.value in ['a', 'b', 'c'])]", "$[1]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.value in ['a', 'b', 'c'])]", "$[1]", typeof( JsonNode ) )] + public void InOperation_SingleValue( string query, string expect, Type sourceType ) { const string json = """ @@ -31,8 +31,7 @@ public void InOperation_SingleValue( string query, Type sourceType ) var source = GetDocumentAdapter( sourceType, json ); var expected = new[] { - source.FromJsonPathPointer("$[0]"), - source.FromJsonPathPointer("$[1]") + source.FromJsonPathPointer(expect) }; var matches = source.Select( query ).ToList(); @@ -45,7 +44,7 @@ public void InOperation_SingleValue( string query, Type sourceType ) [DataRow( "$[?(@.values in [1, 2, 3])]", typeof( JsonNode ) )] [DataRow( "$[?(@.values in ['x', 'y', 'z'])]", typeof( JsonDocument ) )] [DataRow( "$[?(@.values in ['x', 'y', 'z'])]", typeof( JsonNode ) )] - public void InOperation_ArrayValue( string query, Type sourceType ) + public void InOperation_ArrayValueIsNotAnArrayElement( string query, Type sourceType ) { const string json = """ @@ -59,11 +58,31 @@ public void InOperation_ArrayValue( string query, Type sourceType ) ] """; var source = GetDocumentAdapter( sourceType, json ); - var expected = new[] - { - source.FromJsonPathPointer("$[0]"), - source.FromJsonPathPointer("$[1]") - }; + + var matches = source.Select( query ).ToList(); + Assert.AreEqual( 0, matches.Count ); + } + + [DataTestMethod] + [DataRow( "$[?(@.values in [1, [1,2,3], 3])]", "$[0]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.values in [1, [1,2,3], 3])]", "$[0]", typeof( JsonNode ) )] + [DataRow( "$[?(@.values in ['x', ['x','y','z'], 'z'])]", "$[1]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.values in ['x', ['x','y','z'], 'z'])]", "$[1]", typeof( JsonNode ) )] + public void InOperation_ArrayValueIsAnArrayElement( string query, string expect, Type sourceType ) + { + const string json = + """ + [ + { + "values": [1, 2, 3] + }, + { + "values": ["x", "y", "z"] + } + ] + """; + var source = GetDocumentAdapter( sourceType, json ); + var expected = new[] { source.FromJsonPathPointer( expect ) }; var matches = source.Select( query ).ToList(); Assert.AreEqual( expected.Length, matches.Count ); @@ -103,7 +122,7 @@ public void InOperation_MultipleConditions( string query, Type sourceType ) [DataTestMethod] [DataRow( "$[?(@.array in [1, 2, 3])]", typeof( JsonDocument ) )] [DataRow( "$[?(@.array in [1, 2, 3])]", typeof( JsonNode ) )] - public void InOperation_ArrayInArray( string query, Type sourceType ) + public void InOperation_ArrayNotAnArrayElement( string query, Type sourceType ) { const string json = """ @@ -117,11 +136,29 @@ public void InOperation_ArrayInArray( string query, Type sourceType ) ] """; var source = GetDocumentAdapter( sourceType, json ); - var expected = new[] - { - source.FromJsonPathPointer("$[0]"), - source.FromJsonPathPointer("$[1]") - }; + + var matches = source.Select( query ).ToList(); + Assert.AreEqual( 0, matches.Count ); + } + + [DataTestMethod] + [DataRow( "$[?(@.array in [1, [1,2], 3])]", typeof( JsonDocument ) )] + [DataRow( "$[?(@.array in [1, [1,2], 3])]", typeof( JsonNode ) )] + public void InOperation_ArrayIsArrayElement( string query, Type sourceType ) + { + const string json = + """ + [ + { + "array": [1, 2] + }, + { + "array": [3, 4] + } + ] + """; + var source = GetDocumentAdapter( sourceType, json ); + var expected = new[] { source.FromJsonPathPointer( "$[0]" ) }; var matches = source.Select( query ).ToList(); Assert.AreEqual( expected.Length, matches.Count ); diff --git a/test/Hyperbee.Json.Tests/TestSupport/AssertExtensions.cs b/test/Hyperbee.Json.Tests/TestSupport/AssertExtensions.cs new file mode 100644 index 00000000..35e5d7d0 --- /dev/null +++ b/test/Hyperbee.Json.Tests/TestSupport/AssertExtensions.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Hyperbee.Json.Tests.TestSupport; + +public static class AssertExtensions +{ + public static void ThrowsAny( Action action ) + where T1 : Exception + where T2 : Exception + { + ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ) ); + } + + public static void ThrowsAny( Action action ) + where T1 : Exception + where T2 : Exception + where T3 : Exception + { + ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ), typeof( T3 ) ); + } + + public static void ThrowsAny( Action action ) + where T1 : Exception + where T2 : Exception + where T3 : Exception + where T4 : Exception + { + ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ), typeof( T3 ), typeof( T4 ) ); + } + + public static void ThrowsAny( Action action ) + where T1 : Exception + where T2 : Exception + where T3 : Exception + where T4 : Exception + where T5 : Exception + { + ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ), typeof( T3 ), typeof( T4 ), typeof( T5 ) ); + } + + private static void ThrowsAnyInternal( Action action, params Type[] expectedExceptionTypes ) + { + Exception caughtException = null; + + try + { + action(); + } + catch ( Exception ex ) + { + caughtException = ex; + } + + if ( caughtException == null ) + { + Assert.Fail( $"No exception was thrown. Expected one of: {string.Join( ", ", expectedExceptionTypes.Select( t => t.Name ) )}" ); + } + else if ( !expectedExceptionTypes.Any( e => e.IsInstanceOfType( caughtException ) ) ) + { + Assert.Fail( $"Exception of type {caughtException.GetType().Name} was thrown, but none of the expected types were: {string.Join( ", ", expectedExceptionTypes.Select( t => t.Name ) )}" ); + } + } +} diff --git a/test/Hyperbee.Json.Tests/TestSupport/JsonElementHelper.cs b/test/Hyperbee.Json.Tests/TestSupport/JsonElementHelper.cs index 3cb9112d..8d13c2c6 100644 --- a/test/Hyperbee.Json.Tests/TestSupport/JsonElementHelper.cs +++ b/test/Hyperbee.Json.Tests/TestSupport/JsonElementHelper.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Json.Tests.TestSupport; -public class JsonPathDocument( string source ) : IJsonDocument +public class JsonElementDocument( string source ) : IJsonDocument { private JsonDocument Document { get; } = JsonDocument.Parse( source ); public IEnumerable Select( string query ) => Document.Select( query ).Cast(); @@ -20,6 +20,8 @@ internal static partial class TestHelper public static int GetInt32( JsonElement value ) => value.GetInt32(); + public static float GetSingle( JsonElement value ) => value.GetSingle(); + public static string GetString( JsonElement value, bool minify = false ) { if ( value.ValueKind != JsonValueKind.Object && value.ValueKind != JsonValueKind.Array ) diff --git a/test/Hyperbee.Json.Tests/TestSupport/JsonNodeHelper.cs b/test/Hyperbee.Json.Tests/TestSupport/JsonNodeHelper.cs index fc1e4dfa..f3221a43 100644 --- a/test/Hyperbee.Json.Tests/TestSupport/JsonNodeHelper.cs +++ b/test/Hyperbee.Json.Tests/TestSupport/JsonNodeHelper.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Json.Tests.TestSupport; -public class JsonPathNode( string source ) : IJsonDocument +public class JsonNodeDocument( string source ) : IJsonDocument { private JsonNode Document { get; } = JsonNode.Parse( source ); public IEnumerable Select( string query ) => Document.Select( query ); @@ -19,6 +19,8 @@ internal static partial class TestHelper public static int GetInt32( JsonNode value ) => value.AsValue().GetValue(); + public static float GetSingle( JsonNode value ) => value.AsValue().GetValue(); + public static string GetString( JsonNode value, bool minify = false ) { if ( value is not JsonObject && value is not JsonArray ) diff --git a/test/Hyperbee.Json.Tests/TestSupport/JsonTestBase.cs b/test/Hyperbee.Json.Tests/TestSupport/JsonTestBase.cs index 3def0587..0920cc3a 100644 --- a/test/Hyperbee.Json.Tests/TestSupport/JsonTestBase.cs +++ b/test/Hyperbee.Json.Tests/TestSupport/JsonTestBase.cs @@ -26,7 +26,7 @@ private static Stream GetManifestStream( string resourceName = null ) .GetManifestResourceStream( $"Hyperbee.Json.Tests.TestDocuments.{resourceName}" ); } - public static TType GetDocument( string resourceName = null ) + protected static TType GetDocument( string resourceName = null ) { var type = typeof( TType ); @@ -44,19 +44,19 @@ public static TType GetDocument( string resourceName = null ) throw new NotSupportedException(); } - public static IJsonDocument GetDocumentAdapter( Type target ) + protected static IJsonDocument GetDocumentAdapter( Type target ) { var source = ReadJsonString(); return GetDocumentAdapter( target, source ); } - public static IJsonDocument GetDocumentAdapter( Type target, string source ) + protected static IJsonDocument GetDocumentAdapter( Type target, string source ) { - if ( target == typeof( JsonDocument ) ) - return new JsonPathDocument( source ); + if ( target == typeof( JsonDocument ) || target == typeof( JsonElement ) ) + return new JsonElementDocument( source ); if ( target == typeof( JsonNode ) ) - return new JsonPathNode( source ); + return new JsonNodeDocument( source ); throw new NotSupportedException(); } From 037783f3f1c2c79a5d1ceaea11cfacc005b09459 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 22 Jul 2024 20:30:48 +0000 Subject: [PATCH 15/18] Updated code formatting to match rules in .editorconfig --- test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs index 22e1331f..7a25cf86 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs @@ -27,7 +27,7 @@ public class FilterParserTests : JsonTestBase [DataRow( "(1 == 1)", true, typeof( JsonNode ) )] [DataRow( "(1 != 2)", true, typeof( JsonNode ) )] [DataRow( "!(1 == 2)", true, typeof( JsonNode ) )] - [DataRow( "(\"world\" == 'world') || 1 == 1", true, typeof( JsonNode ) )] + [DataRow( "(\"world\" == 'world') || 1 == 1", true, typeof( JsonNode ) )] [DataRow( "!('World' != 'World') && !(1 == 2 || 1 == 3)", true, typeof( JsonNode ) )] public void MatchExpectedResult_WhenUsingConstants( string filter, bool expected, Type sourceType ) { @@ -123,8 +123,8 @@ public void ReturnExpectedResult_WhenUsingExpressionEvaluator( string filter, fl } [DataTestMethod] - [DataRow( "count(@.store.book) == 1", true, typeof( JsonElement ) )] - [DataRow( "count(@.store.book.*) == 4", true, typeof( JsonElement ) )] + [DataRow( "count(@.store.book) == 1", true, typeof( JsonElement ) )] + [DataRow( "count(@.store.book.*) == 4", true, typeof( JsonElement ) )] [DataRow( "length(@.store.book) == 4", true, typeof( JsonElement ) )] [DataRow( "length(@.store.book[0].category) == 9", true, typeof( JsonElement ) )] [DataRow( "match(@.store.book[0].title, \"Sayings.*\" )", true, typeof( JsonElement ) )] From 871b1187938b842cd0c2de1f0208898dd4800ad2 Mon Sep 17 00:00:00 2001 From: bfarmer67 Date: Mon, 22 Jul 2024 20:33:28 +0000 Subject: [PATCH 16/18] Previous version was 'v1.3.0'. Version now 'v1.4.0'. --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 983787c5..b3136d2e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ 1 - 3 + 4 0 From 068511e0b9a7d65c0d07da33596a205acb84eb34 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 22 Jul 2024 20:57:11 +0000 Subject: [PATCH 17/18] Updated code formatting to match rules in .editorconfig --- src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs | 2 +- src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs | 2 +- .../Descriptors/Element/Functions/CountElementFunction.cs | 2 +- .../Descriptors/Element/Functions/LengthElementFunction.cs | 2 +- .../Descriptors/Element/Functions/MatchElementFunction.cs | 2 +- .../Descriptors/Element/Functions/SearchElementFunction.cs | 2 +- .../Descriptors/Element/Functions/ValueElementFunction.cs | 2 +- src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs | 2 +- src/Hyperbee.Json/Descriptors/IValueAccessor.cs | 2 +- .../Descriptors/Node/Functions/CountNodeFunction.cs | 2 +- .../Descriptors/Node/Functions/LengthNodeFunction.cs | 2 +- .../Descriptors/Node/Functions/MatchNodeFunction.cs | 2 +- .../Descriptors/Node/Functions/SearchNodeFunction.cs | 2 +- .../Descriptors/Node/Functions/ValueNodeFunction.cs | 2 +- src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs | 2 +- src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs | 2 +- src/Hyperbee.Json/Extensions/JsonElementExtensions.cs | 2 +- src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs | 2 +- src/Hyperbee.Json/Filters/IRegexp.cs | 2 +- .../Filters/Parser/Expressions/FunctionExpressionFactory.cs | 2 +- .../Filters/Parser/Expressions/IExpressionFactory.cs | 2 +- .../Filters/Parser/Expressions/JsonExpressionFactory.cs | 2 +- .../Filters/Parser/Expressions/LiteralExpressionFactory.cs | 2 +- .../Filters/Parser/Expressions/NotExpressionFactory.cs | 2 +- .../Filters/Parser/Expressions/ParenExpressionFactory.cs | 2 +- .../Filters/Parser/Expressions/SelectExpressionFactory.cs | 2 +- src/Hyperbee.Json/Filters/Parser/FilterParser.cs | 2 +- src/Hyperbee.Json/Filters/Parser/Operator.cs | 2 +- src/Hyperbee.Json/Filters/Parser/ParserState.cs | 2 +- src/Hyperbee.Json/Internal/SpanHelper.cs | 2 +- src/Hyperbee.Json/JsonPath.cs | 2 +- src/Hyperbee.Json/JsonPathQueryParser.cs | 2 +- src/Hyperbee.Json/JsonPathSegment.cs | 2 +- test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs | 2 +- test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs | 2 +- test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs | 2 +- test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs | 2 +- test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs | 2 +- test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs | 2 +- test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs | 2 +- test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs | 2 +- test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs | 2 +- test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs | 2 +- test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs | 2 +- test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs | 2 +- test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs | 2 +- 46 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs index f5fa9014..50040e5f 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using Hyperbee.Json.Descriptors.Element.Functions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Values; diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs index 8be19f71..5dd9ddfd 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs index c111624b..ed6925ab 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/CountElementFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.Json; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs index 317fb6d3..9ca19ca4 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/LengthElementFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.Json; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs index ab745265..e9c2e873 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/MatchElementFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.RegularExpressions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs index 04d3aa6d..ce69a15f 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/SearchElementFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.RegularExpressions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; diff --git a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs index 6f9624bf..d04f7931 100644 --- a/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Element/Functions/ValueElementFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.Json; using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Parser; diff --git a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs index 03aeac80..5bf8eed4 100644 --- a/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/ITypeDescriptor.cs @@ -1,4 +1,4 @@ -using Hyperbee.Json.Filters; +using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; diff --git a/src/Hyperbee.Json/Descriptors/IValueAccessor.cs b/src/Hyperbee.Json/Descriptors/IValueAccessor.cs index e83c24e8..9ea58fac 100644 --- a/src/Hyperbee.Json/Descriptors/IValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/IValueAccessor.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Nodes; namespace Hyperbee.Json.Descriptors; diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs index b6fe8ac3..d747e842 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/CountNodeFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.Json.Nodes; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs index 002f4397..0eab456e 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/LengthNodeFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; using Hyperbee.Json.Filters.Parser; diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs index 84b1dfe0..28520300 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/MatchNodeFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.RegularExpressions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs index 8eaa60a7..397f6227 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/SearchNodeFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.RegularExpressions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs index 8a4ac8f5..b2a2043d 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; using Hyperbee.Json.Filters.Parser; diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs index fdcb8499..bf2a9c85 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeTypeDescriptor.cs @@ -1,4 +1,4 @@ -using System.Text.Json.Nodes; +using System.Text.Json.Nodes; using Hyperbee.Json.Descriptors.Node.Functions; using Hyperbee.Json.Filters; using Hyperbee.Json.Filters.Parser; diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs index 8917edf5..a723548e 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; diff --git a/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs b/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs index 00bb8d00..8869ab39 100644 --- a/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs +++ b/src/Hyperbee.Json/Extensions/JsonElementExtensions.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; namespace Hyperbee.Json.Extensions; diff --git a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs index 84bf5583..600eb9d4 100644 --- a/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs +++ b/src/Hyperbee.Json/Extensions/JsonPathPointerExtensions.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Nodes; namespace Hyperbee.Json.Extensions; diff --git a/src/Hyperbee.Json/Filters/IRegexp.cs b/src/Hyperbee.Json/Filters/IRegexp.cs index 49954627..b3c7bdbc 100644 --- a/src/Hyperbee.Json/Filters/IRegexp.cs +++ b/src/Hyperbee.Json/Filters/IRegexp.cs @@ -1,4 +1,4 @@ -namespace Hyperbee.Json.Filters; +namespace Hyperbee.Json.Filters; public static class IRegexp { diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs index bd608721..70fcd9dd 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/FunctionExpressionFactory.cs @@ -1,4 +1,4 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; using Hyperbee.Json.Descriptors; namespace Hyperbee.Json.Filters.Parser.Expressions; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs index 1e8a8f3d..077f7a59 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/IExpressionFactory.cs @@ -1,4 +1,4 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; using Hyperbee.Json.Descriptors; namespace Hyperbee.Json.Filters.Parser.Expressions; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs index 663c847f..4308e684 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/JsonExpressionFactory.cs @@ -1,4 +1,4 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; using System.Text; using System.Text.Json; using Hyperbee.Json.Descriptors; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs index 24562462..5b26a346 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/LiteralExpressionFactory.cs @@ -1,4 +1,4 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Values; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs index cff2ba96..545d224b 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/NotExpressionFactory.cs @@ -1,4 +1,4 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; using Hyperbee.Json.Descriptors; namespace Hyperbee.Json.Filters.Parser.Expressions; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs index e3921329..dfae4362 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/ParenExpressionFactory.cs @@ -1,4 +1,4 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; using Hyperbee.Json.Descriptors; namespace Hyperbee.Json.Filters.Parser.Expressions; diff --git a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs index 7f896f9e..2d88cd85 100644 --- a/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs +++ b/src/Hyperbee.Json/Filters/Parser/Expressions/SelectExpressionFactory.cs @@ -1,4 +1,4 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; using System.Reflection; using Hyperbee.Json.Descriptors; using Hyperbee.Json.Filters.Values; diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs index 6a3ad7f3..9b4e7e3b 100644 --- a/src/Hyperbee.Json/Filters/Parser/FilterParser.cs +++ b/src/Hyperbee.Json/Filters/Parser/FilterParser.cs @@ -1,4 +1,4 @@ -#region License +#region License // This code is adapted from an algorithm published in MSDN Magazine, October 2015. // Original article: "A Split-and-Merge Expression Parser in C#" by Vassili Kaplan. diff --git a/src/Hyperbee.Json/Filters/Parser/Operator.cs b/src/Hyperbee.Json/Filters/Parser/Operator.cs index 061ea569..3676e403 100644 --- a/src/Hyperbee.Json/Filters/Parser/Operator.cs +++ b/src/Hyperbee.Json/Filters/Parser/Operator.cs @@ -1,4 +1,4 @@ -namespace Hyperbee.Json.Filters.Parser; +namespace Hyperbee.Json.Filters.Parser; [Flags] public enum Operator diff --git a/src/Hyperbee.Json/Filters/Parser/ParserState.cs b/src/Hyperbee.Json/Filters/Parser/ParserState.cs index 1cf259ac..bf734eee 100644 --- a/src/Hyperbee.Json/Filters/Parser/ParserState.cs +++ b/src/Hyperbee.Json/Filters/Parser/ParserState.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; namespace Hyperbee.Json.Filters.Parser; diff --git a/src/Hyperbee.Json/Internal/SpanHelper.cs b/src/Hyperbee.Json/Internal/SpanHelper.cs index f3ddb1da..98fc9583 100644 --- a/src/Hyperbee.Json/Internal/SpanHelper.cs +++ b/src/Hyperbee.Json/Internal/SpanHelper.cs @@ -1,4 +1,4 @@ -namespace Hyperbee.Json.Internal; +namespace Hyperbee.Json.Internal; internal enum SpanUnescapeOptions { diff --git a/src/Hyperbee.Json/JsonPath.cs b/src/Hyperbee.Json/JsonPath.cs index 8b05e46d..d23d74df 100644 --- a/src/Hyperbee.Json/JsonPath.cs +++ b/src/Hyperbee.Json/JsonPath.cs @@ -1,4 +1,4 @@ -#region License +#region License // C# Implementation of JSONPath[1] // diff --git a/src/Hyperbee.Json/JsonPathQueryParser.cs b/src/Hyperbee.Json/JsonPathQueryParser.cs index 8a008402..750c47f7 100644 --- a/src/Hyperbee.Json/JsonPathQueryParser.cs +++ b/src/Hyperbee.Json/JsonPathQueryParser.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Runtime.CompilerServices; using Hyperbee.Json.Internal; diff --git a/src/Hyperbee.Json/JsonPathSegment.cs b/src/Hyperbee.Json/JsonPathSegment.cs index c9a424df..4d44d524 100644 --- a/src/Hyperbee.Json/JsonPathSegment.cs +++ b/src/Hyperbee.Json/JsonPathSegment.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; namespace Hyperbee.Json; diff --git a/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs b/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs index 20074c10..e9626464 100644 --- a/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs +++ b/test/Hyperbee.Json.Benchmark/FilterExpressionParserEvaluator.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Nodes; using BenchmarkDotNet.Attributes; using Hyperbee.Json.Filters.Parser; diff --git a/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs index b843513e..b6fe9489 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-basic-tests.cs @@ -1,4 +1,4 @@ -// This file was auto generated. +// This file was auto generated. using System.Text.Json; using System.Text.Json.Nodes; diff --git a/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs index fd5806b4..4bb1d1ca 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-filter-tests.cs @@ -1,4 +1,4 @@ -// This file was auto generated. +// This file was auto generated. using System.Text.Json; using System.Text.Json.Nodes; diff --git a/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs index 7ffc147f..46f5eb27 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-functions-tests.cs @@ -1,4 +1,4 @@ -// This file was auto generated. +// This file was auto generated. using System.Text.Json; using System.Text.Json.Nodes; diff --git a/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs index 4ab9fa7d..ae7d8bb8 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-index-selector-tests.cs @@ -1,4 +1,4 @@ -// This file was auto generated. +// This file was auto generated. using System.Text.Json; using System.Text.Json.Nodes; diff --git a/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs index cf3a2a36..ef0ecc61 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-name-selector-tests.cs @@ -1,4 +1,4 @@ -// This file was auto generated. +// This file was auto generated. using System.Text.Json; using System.Text.Json.Nodes; diff --git a/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs index dc818821..6c26723e 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-slice-selector-tests.cs @@ -1,4 +1,4 @@ -// This file was auto generated. +// This file was auto generated. using System.Text.Json; using System.Text.Json.Nodes; diff --git a/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs b/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs index 64dbd87d..07f3d761 100644 --- a/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs +++ b/test/Hyperbee.Json.Cts/Tests/cts-whitespace-tests.cs @@ -1,4 +1,4 @@ -// This file was auto generated. +// This file was auto generated. using System.Text.Json; using System.Text.Json.Nodes; diff --git a/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs b/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs index bbad464d..cede4e0a 100644 --- a/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs +++ b/test/Hyperbee.Json.Tests/Dynamic/JsonDynamicTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.Json; using Hyperbee.Json.Dynamic; using Hyperbee.Json.Extensions; diff --git a/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs b/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs index eefa1c8d..9a197b05 100644 --- a/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/ExtensionFunctionTests.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Reflection; using System.Text.Json.Nodes; using Hyperbee.Json.Descriptors.Element; diff --git a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs index d532a89a..7a25cf86 100644 --- a/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/FilterParserTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Linq.Expressions; using System.Reflection; diff --git a/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs b/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs index 8686f957..f0e2cac3 100644 --- a/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs +++ b/test/Hyperbee.Json.Tests/Parsers/JsonPathQueryParserTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs b/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs index b87c05d2..6c0d581e 100644 --- a/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs +++ b/test/Hyperbee.Json.Tests/Query/JsonPathFilterExpressionTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; From 02d9f890d6c591b583b2abba2d498877edbe6591 Mon Sep 17 00:00:00 2001 From: Brenton Farmer Date: Mon, 22 Jul 2024 14:22:30 -0700 Subject: [PATCH 18/18] Fix merge --- .../Element/ElementTypeDescriptor.cs | 9 +- .../Element/ElementValueAccessor.cs | 5 - .../Node/Functions/ValueNodeFunction.cs | 7 +- .../Descriptors/Node/NodeValueAccessor.cs | 5 - src/Hyperbee.Json/Extensions/JsonHelper.cs | 14 -- src/Hyperbee.Json/Filters/FilterRuntime.cs | 1 - .../Filters/Parser/ExpressionInfo.cs | 7 - .../Filters/Parser/ExpressionKind.cs | 13 - .../Filters/Parser/FilterExtensionInfo.cs | 9 - .../Filters/Parser/FilterParserContext.cs | 12 - .../NodeTypeComparerBinderExpression.cs | 25 -- .../Filters/Parser/NodeTypeExpression.cs | 64 ----- src/Hyperbee.Json/Filters/Values/Constants.cs | 10 - src/Hyperbee.Json/Filters/Values/INodeType.cs | 8 - .../Filters/Values/NodeTypeComparer.cs | 236 ------------------ .../Filters/Values/NodeTypeKind.cs | 10 - src/Hyperbee.Json/Filters/Values/NodesType.cs | 17 -- src/Hyperbee.Json/Filters/Values/Nothing.cs | 7 - src/Hyperbee.Json/Filters/Values/Null.cs | 7 - src/Hyperbee.Json/Filters/Values/ValueType.cs | 10 - test/Hyperbee.Json.Cts/AssertExtensions.cs | 61 ----- test/Hyperbee.Json.Cts/TestHelper.cs | 54 ---- .../Query/NodeTypeComparerTests.cs | 149 ----------- 23 files changed, 7 insertions(+), 733 deletions(-) delete mode 100644 src/Hyperbee.Json/Extensions/JsonHelper.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs delete mode 100644 src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/Constants.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/INodeType.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/NodesType.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/Nothing.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/Null.cs delete mode 100644 src/Hyperbee.Json/Filters/Values/ValueType.cs delete mode 100644 test/Hyperbee.Json.Cts/AssertExtensions.cs delete mode 100644 test/Hyperbee.Json.Cts/TestHelper.cs delete mode 100644 test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs index 50040e5f..126c76d8 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementTypeDescriptor.cs @@ -1,14 +1,15 @@ using System.Text.Json; using Hyperbee.Json.Descriptors.Element.Functions; using Hyperbee.Json.Filters; -using Hyperbee.Json.Filters.Values; +using Hyperbee.Json.Filters.Parser; namespace Hyperbee.Json.Descriptors.Element; public class ElementTypeDescriptor : ITypeDescriptor { private ElementValueAccessor _accessor; - private NodeTypeComparer _comparer; + private ValueTypeComparer _comparer; + private FilterRuntime _runtime; public FunctionRegistry Functions { get; } = new(); @@ -18,8 +19,8 @@ public class ElementTypeDescriptor : ITypeDescriptor public IFilterRuntime FilterRuntime => _runtime ??= new FilterRuntime(); - public INodeTypeComparer Comparer => - _comparer ??= new NodeTypeComparer( Accessor ); + public IValueTypeComparer Comparer => + _comparer ??= new ValueTypeComparer( Accessor ); public bool CanUsePointer => true; diff --git a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs index 5dd9ddfd..183202ce 100644 --- a/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Element/ElementValueAccessor.cs @@ -206,9 +206,4 @@ public bool TryGetValueFromNode( JsonElement element, out IConvertible value ) return true; } - - public bool TryGetFromPointer( in JsonElement element, JsonPathSegment segment, out JsonElement childValue ) - { - return element.TryGetFromJsonPathPointer( segment, out childValue ); - } } diff --git a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs index b2a2043d..8ad200ae 100644 --- a/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs +++ b/src/Hyperbee.Json/Descriptors/Node/Functions/ValueNodeFunction.cs @@ -1,8 +1,10 @@ using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; +using Hyperbee.Json.Extensions; using Hyperbee.Json.Filters.Parser; using Hyperbee.Json.Filters.Values; +using Microsoft.VisualBasic; namespace Hyperbee.Json.Descriptors.Node.Functions; @@ -18,11 +20,6 @@ public static IValueType Value( IValueType argument ) var node = nodes.OneOrDefault(); - if ( nodeArray.Length != 1 ) - return Constants.Nothing; - - var node = nodeArray.FirstOrDefault(); - return node?.GetValueKind() switch { JsonValueKind.Number when node.AsValue().TryGetValue( out var value ) => Scalar.Value( value ), diff --git a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs index a723548e..dc5d05a8 100644 --- a/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs +++ b/src/Hyperbee.Json/Descriptors/Node/NodeValueAccessor.cs @@ -211,9 +211,4 @@ public bool TryGetValueFromNode( JsonNode node, out IConvertible value ) return true; } - - public bool TryGetFromPointer( in JsonNode node, JsonPathSegment segment, out JsonNode childValue ) - { - return node.TryGetFromJsonPathPointer( segment, out childValue ); - } } diff --git a/src/Hyperbee.Json/Extensions/JsonHelper.cs b/src/Hyperbee.Json/Extensions/JsonHelper.cs deleted file mode 100644 index 1de2809e..00000000 --- a/src/Hyperbee.Json/Extensions/JsonHelper.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Nodes; -using Hyperbee.Json.Dynamic; - -namespace Hyperbee.Json.Extensions; - -public static class JsonHelper -{ - // conversion - - public static dynamic ConvertToDynamic( JsonNode value ) => new DynamicJsonNode( ref value ); - public static dynamic ConvertToDynamic( JsonElement value, string path = null ) => new DynamicJsonElement( ref value, path ); - public static dynamic ConvertToDynamic( JsonDocument value ) => ConvertToDynamic( value.RootElement, "$" ); -} diff --git a/src/Hyperbee.Json/Filters/FilterRuntime.cs b/src/Hyperbee.Json/Filters/FilterRuntime.cs index c1822c08..47da7b9d 100644 --- a/src/Hyperbee.Json/Filters/FilterRuntime.cs +++ b/src/Hyperbee.Json/Filters/FilterRuntime.cs @@ -33,4 +33,3 @@ public bool Evaluate( string filter, TNode current, TNode root ) } } } - diff --git a/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs b/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs deleted file mode 100644 index 0d494ad5..00000000 --- a/src/Hyperbee.Json/Filters/Parser/ExpressionInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Hyperbee.Json.Filters.Parser; - -internal class ExpressionInfo -{ - public ExpressionKind Kind { get; set; } - public FilterExtensionInfo FunctionInfo { get; set; } -} diff --git a/src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs b/src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs deleted file mode 100644 index 9231d05d..00000000 --- a/src/Hyperbee.Json/Filters/Parser/ExpressionKind.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Hyperbee.Json.Filters.Parser; - -internal enum ExpressionKind -{ - Unspecified, - Function, - Json, - Literal, - Not, - Paren, - Select, - Merged -} diff --git a/src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs b/src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs deleted file mode 100644 index 394a6f9e..00000000 --- a/src/Hyperbee.Json/Filters/Parser/FilterExtensionInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Hyperbee.Json.Filters.Parser; - -[Flags] -public enum FilterExtensionInfo -{ - MustCompare = 0x01, - MustNotCompare = 0x02, - ExpectNormalized = 0x10, -} diff --git a/src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs b/src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs deleted file mode 100644 index 286cdb58..00000000 --- a/src/Hyperbee.Json/Filters/Parser/FilterParserContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Json.Descriptors; - -namespace Hyperbee.Json.Filters.Parser; - -internal record FilterParserContext( ITypeDescriptor Descriptor ) -{ - public ParameterExpression RuntimeContext { get; init; } = Expression.Parameter( typeof( FilterRuntimeContext ), "runtimeContext" ); -} - - -public record FilterRuntimeContext( TNode Current, TNode Root, ITypeDescriptor Descriptor ); diff --git a/src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs b/src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs deleted file mode 100644 index b6ca6328..00000000 --- a/src/Hyperbee.Json/Filters/Parser/NodeTypeComparerBinderExpression.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Json.Filters.Values; - -namespace Hyperbee.Json.Filters.Parser; - -public static class NodeTypeComparerBinderExpression -{ - private static readonly Expression BindComparerExpressionConst = Expression.Constant( (Func, INodeType, INodeType>) BindComparer ); - internal static Expression BindComparerExpression( FilterParserContext parserContext, Expression expression ) - { - if ( expression == null ) - return null; - - var parserContextExp = Expression.Constant( parserContext ); - - return Expression.Invoke( BindComparerExpressionConst, parserContextExp, - Expression.Convert( expression, typeof( INodeType ) ) ); - } - - internal static INodeType BindComparer( FilterParserContext parserContext, INodeType item ) - { - item.Comparer = parserContext.Descriptor.Comparer; - return item; - } -} diff --git a/src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs b/src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs deleted file mode 100644 index 8b969121..00000000 --- a/src/Hyperbee.Json/Filters/Parser/NodeTypeExpression.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Linq.Expressions; -using System.Reflection; -using Hyperbee.Json.Filters.Values; - -namespace Hyperbee.Json.Filters.Parser; - -public static class NodeTypeExpression -{ - private static readonly MethodInfo AreEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( AreEqual ) ); - private static readonly MethodInfo AreNotEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( AreNotEqual ) ); - private static readonly MethodInfo IsLessThanMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsLessThan ) ); - private static readonly MethodInfo IsLessThanOrEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsLessThanOrEqual ) ); - private static readonly MethodInfo IsGreaterThanMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsGreaterThan ) ); - private static readonly MethodInfo IsGreaterThanOrEqualMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( IsGreaterThanOrEqual ) ); - - private static readonly MethodInfo AndAlsoMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( AndAlso ) ); - private static readonly MethodInfo OrElseMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( OrElse ) ); - private static readonly MethodInfo NotMethodInfo = typeof( NodeTypeExpression ).GetMethod( nameof( NotBoolean ) ); - - public static Expression Equal( Expression left, Expression right ) => Expression.Call( AreEqualMethodInfo, left, right ); - public static Expression NotEqual( Expression left, Expression right ) => Expression.Call( AreNotEqualMethodInfo, left, right ); - public static Expression LessThan( Expression left, Expression right ) => Expression.Call( IsLessThanMethodInfo, left, right ); - public static Expression LessThanOrEqual( Expression left, Expression right ) => Expression.Call( IsLessThanOrEqualMethodInfo, left, right ); - public static Expression GreaterThan( Expression left, Expression right ) => Expression.Call( IsGreaterThanMethodInfo, left, right ); - public static Expression GreaterThanOrEqual( Expression left, Expression right ) => Expression.Call( IsGreaterThanOrEqualMethodInfo, left, right ); - - // Binary operators - public static Expression And( Expression left, Expression right ) => Expression.Call( AndAlsoMethodInfo, left, right ); - public static Expression Or( Expression left, Expression right ) => Expression.Call( OrElseMethodInfo, left, right ); - public static Expression Not( Expression expression ) => Expression.Call( NotMethodInfo, expression ); - - public static bool AreEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.Equals ) == 0; - public static bool AreNotEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.NotEquals ) != 0; - public static bool IsLessThan( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.LessThan ) < 0; - public static bool IsLessThanOrEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.LessThanOrEqual ) <= 0; - public static bool IsGreaterThan( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.GreaterThan ) > 0; - public static bool IsGreaterThanOrEqual( INodeType left, INodeType right ) => left.Comparer.Compare( left, right, Operator.GreaterThanOrEqual ) >= 0; - - public static bool AndAlso( INodeType left, INodeType right ) - { - if ( left is ValueType leftBoolValue && right is ValueType rightBoolValue ) - return leftBoolValue.Value && rightBoolValue.Value; - - return left.Comparer.Exists( left ) && - right.Comparer.Exists( right ); - } - - public static bool OrElse( INodeType left, INodeType right ) - { - if ( left is ValueType leftBoolValue && right is ValueType rightBoolValue ) - return leftBoolValue.Value || rightBoolValue.Value; - - return left.Comparer.Exists( left ) || - right.Comparer.Exists( right ); - } - - public static bool NotBoolean( INodeType value ) - { - if ( value is ValueType { Value: false } ) - return true; - - return !value.Comparer.Exists( value ); - } -} diff --git a/src/Hyperbee.Json/Filters/Values/Constants.cs b/src/Hyperbee.Json/Filters/Values/Constants.cs deleted file mode 100644 index 31f4b581..00000000 --- a/src/Hyperbee.Json/Filters/Values/Constants.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public static class Constants -{ - public static ValueType True { get; } = new( true ); - public static ValueType False { get; } = new( false ); - - public static Null Null { get; } = new(); - public static Nothing Nothing { get; } = new(); -} diff --git a/src/Hyperbee.Json/Filters/Values/INodeType.cs b/src/Hyperbee.Json/Filters/Values/INodeType.cs deleted file mode 100644 index a7fea226..00000000 --- a/src/Hyperbee.Json/Filters/Values/INodeType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public interface INodeType -{ - public NodeTypeKind Kind { get; } - - public INodeTypeComparer Comparer { get; set; } -} diff --git a/src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs b/src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs deleted file mode 100644 index 47103f4b..00000000 --- a/src/Hyperbee.Json/Filters/Values/NodeTypeComparer.cs +++ /dev/null @@ -1,236 +0,0 @@ -using Hyperbee.Json.Descriptors; -using Hyperbee.Json.Filters.Parser; - -namespace Hyperbee.Json.Filters.Values; - -public interface INodeTypeComparer -{ - public int Compare( INodeType left, INodeType right, Operator operation ); - - public bool Exists( INodeType node ); -} - -public class NodeTypeComparer( IValueAccessor accessor ) : INodeTypeComparer -{ - private const float Tolerance = 1e-6F; // Define a tolerance for float comparisons - - /* - * Comparison Rules (according to JSONPath RFC 9535): - * - * 1. Compare Value to Value: - * - Two values are equal if they are of the same type and have the same value. - * - For float comparisons, use a tolerance to handle precision issues. - * - Comparisons between different types yield false. - * - * 2. Compare Node to Node: - * - Since a Node is essentially an enumerable with a single item, compare the single items directly. - * - Apply the same value comparison rules to the single items. - * - * 3. Compare NodeList to NodeList: - * - Two NodeLists are equal if they are sequence equal. - * - Sequence equality should consider deep equality of Node items. - * - Return 0 if sequences are equal. - * - Return -1 if the left sequence is less. - * - Return 1 if the left sequence is greater. - * - * 4. Compare NodeList to Value: - * - A NodeList is equal to a value if any node in the NodeList matches the value. - * - Return 0 if any node matches the value. - * - Return -1 if the value is less than all nodes. - * - Return 1 if the value is greater than all nodes. - * - * 5. Compare Value to NodeList: - * - Similar to the above, true if the value is found in the NodeList. - * - * 6. Compare Node to NodeList and vice versa: - * - Since Node is a single item enumerable, treat it similarly to Value in comparison to NodeList. - * - * 7. Truthiness Rules: - * - Falsy values: null, false, 0, "", NaN. - * - Truthy values: Anything not falsy, including non-empty strings, non-zero numbers, true, arrays, and objects. - * - Truthiness is generally not used for comparison operators (==, <) in filter expressions. - * - Type mismatches (e.g., string vs. number) result in false for equality (==) and true for inequality (!=). - * - * Order of Operations: - * - Check if both are NodeLists. - * - Check if one is a NodeList and the other is a Value. - * - Compare directly if both are Values. - */ - public int Compare( INodeType left, INodeType right, Operator operation ) - { - ThrowIfNotNormalized( left ); - ThrowIfNotNormalized( right ); - - if ( left is NodesType leftEnumerable && right is NodesType rightEnumerable ) - { - return CompareEnumerables( leftEnumerable, rightEnumerable ); - } - - if ( left is NodesType leftEnumerable1 ) - { - var compare = CompareEnumerableToValue( leftEnumerable1, right, out var typeMismatch, out var nodeCount ); - return AdjustResult( compare, nodeCount, operation, typeMismatch ); - } - - if ( right is NodesType rightEnumerable1 ) - { - var compare = CompareEnumerableToValue( rightEnumerable1, left, out var typeMismatch, out var nodeCount ); - return AdjustResult( compare, nodeCount, operation, typeMismatch ); - } - - return CompareValues( left, right, out _ ); - - static int AdjustResult( int compare, int nodeCount, Operator operation, bool typeMismatch ) - { - // When comparing a NodeList to a Value, '<' and '>' type operators only have meaning when the - // NodeList has a single node. - // - // 1. When there is a single node, the comparison is based on the unwrapped node value. - // This results in a meaningful value to value comparison for equality, and greater-than and - // less-than operations (if the values are the same type). - // - // 2. When there is more than one node, or an empty node list, equality is based on finding the - // value in the set of nodes. The result is true if the value is found in the set, and false - // otherwise. - // - // In this case, the result is not meaningful for greater-than and less-than operations, since - // the comparison is based on the set of nodes, and not on two single values. - // - // However, the comparison result will still be used in the context of a greater-than or less-than - // operation, which will yield indeterminate results based on the left or right order of operands. - // To handle this, we need to normalize the result of the comparison. In this case, we want to - // normalize the result so that greater-than and less-than always return false, regardless of the - // left or right order of the comparands. - - return (nodeCount != 1 || typeMismatch) switch // Test for a non-single value set, or a type comparison mismatch - { - true when (operation == Operator.LessThan || operation == Operator.LessThanOrEqual) => compare < 0 ? -compare : compare, - true when (operation == Operator.GreaterThan || operation == Operator.GreaterThanOrEqual) => compare > 0 ? -compare : compare, - _ => compare - }; - } - - static void ThrowIfNotNormalized( INodeType nodeType ) - { - if ( nodeType is NodesType { IsNormalized: false } ) - throw new NotSupportedException( "Unsupported non-single query." ); - } - } - - public bool Exists( INodeType node ) - { - return node switch - { - ValueType boolValue => boolValue.Value, - ValueType floatValue => floatValue.Value != 0, - ValueType stringValue => !string.IsNullOrEmpty( stringValue.Value ), - NodesType nodes => nodes.Any(), - _ => false - }; - } - - private int CompareEnumerables( IEnumerable left, IEnumerable right ) - { - using var leftEnumerator = left.GetEnumerator(); - using var rightEnumerator = right.GetEnumerator(); - - while ( leftEnumerator.MoveNext() ) - { - if ( !rightEnumerator.MoveNext() ) - return 1; // Left has more elements, so it is greater - - // if the values can be extracted, compare the values directly - if ( TryGetValueType( accessor, leftEnumerator.Current, out var leftItemValue ) && - TryGetValueType( accessor, rightEnumerator.Current, out var rightItemValue ) ) - return CompareValues( leftItemValue, rightItemValue, out _ ); - - if ( !accessor.DeepEquals( leftEnumerator.Current, rightEnumerator.Current ) ) - return -1; // Elements are not deeply equal - } - - if ( rightEnumerator.MoveNext() ) - return -1; // Right has more elements, so left is less - - return 0; // Sequences are equal - } - - private int CompareEnumerableToValue( IEnumerable enumeration, INodeType value, out bool typeMismatch, out int nodeCount ) - { - nodeCount = 0; - typeMismatch = false; - var lastCompare = -1; - - foreach ( var item in enumeration ) - { - nodeCount++; - - if ( !TryGetValueType( accessor, item, out var itemValue ) ) - continue; // Skip if value cannot be extracted - - lastCompare = CompareValues( itemValue, value, out typeMismatch ); - - if ( lastCompare == 0 ) - return 0; // Return 0 if any node matches the value - } - - if ( nodeCount == 0 ) - { - if ( value is Nothing ) // Considered equal - return 0; - - return -1; - } - - return nodeCount != 1 ? -1 : lastCompare; // Return the last comparison if there is only one node - - } - - private static int CompareValues( INodeType left, INodeType right, out bool typeMismatch ) - { - typeMismatch = false; - - if ( left is Null or Nothing && right is Null or Nothing ) - { - return 0; - } - - if ( left?.GetType() != right?.GetType() ) - { - typeMismatch = true; // Type mismatch: important for non-equality comparisons - return -1; - } - - return left switch - { - ValueType leftStringValue when right is ValueType rightStringValue => - string.Compare( leftStringValue.Value, rightStringValue.Value, StringComparison.Ordinal ), - - ValueType leftBoolValue when right is ValueType rightBoolValue => - leftBoolValue.Value.CompareTo( rightBoolValue.Value ), - - ValueType leftFloatValue when right is ValueType rightFloatValue => - Math.Abs( leftFloatValue.Value - rightFloatValue.Value ) < Tolerance ? 0 : leftFloatValue.Value.CompareTo( rightFloatValue.Value ), - - _ => Comparer.Default.Compare( left, right ) - }; - } - - private static bool TryGetValueType( IValueAccessor accessor, TNode node, out INodeType nodeType ) - { - if ( accessor.TryGetValueFromNode( node, out var itemValue ) ) - { - nodeType = itemValue switch - { - string itemString => new ValueType( itemString ), - bool itemBool => new ValueType( itemBool ), - float itemFloat => new ValueType( itemFloat ), - null => Constants.Null, - _ => throw new NotSupportedException( "Unsupported value type." ) - }; - return true; - } - - nodeType = null; - return false; - } -} diff --git a/src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs b/src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs deleted file mode 100644 index 5e401893..00000000 --- a/src/Hyperbee.Json/Filters/Values/NodeTypeKind.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public enum NodeTypeKind -{ - Null, - Nothing, - Value, - Node, - NodeList -} diff --git a/src/Hyperbee.Json/Filters/Values/NodesType.cs b/src/Hyperbee.Json/Filters/Values/NodesType.cs deleted file mode 100644 index 76cb6bc0..00000000 --- a/src/Hyperbee.Json/Filters/Values/NodesType.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections; - -namespace Hyperbee.Json.Filters.Values; - -public struct NodesType( IEnumerable value, bool isNormalized ) : INodeType, IEnumerable -{ - public readonly bool IsNormalized => isNormalized; - public readonly NodeTypeKind Kind => NodeTypeKind.NodeList; - - public INodeTypeComparer Comparer { get; set; } - - public IEnumerable Value { get; } = value; - - public readonly IEnumerator GetEnumerator() => Value.GetEnumerator(); - - readonly IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); -} diff --git a/src/Hyperbee.Json/Filters/Values/Nothing.cs b/src/Hyperbee.Json/Filters/Values/Nothing.cs deleted file mode 100644 index 79a22880..00000000 --- a/src/Hyperbee.Json/Filters/Values/Nothing.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public struct Nothing : INodeType -{ - public readonly NodeTypeKind Kind => NodeTypeKind.Nothing; - public INodeTypeComparer Comparer { get; set; } -} diff --git a/src/Hyperbee.Json/Filters/Values/Null.cs b/src/Hyperbee.Json/Filters/Values/Null.cs deleted file mode 100644 index 72a0942f..00000000 --- a/src/Hyperbee.Json/Filters/Values/Null.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public struct Null : INodeType -{ - public readonly NodeTypeKind Kind => NodeTypeKind.Null; - public INodeTypeComparer Comparer { get; set; } -} diff --git a/src/Hyperbee.Json/Filters/Values/ValueType.cs b/src/Hyperbee.Json/Filters/Values/ValueType.cs deleted file mode 100644 index 8cba9a7d..00000000 --- a/src/Hyperbee.Json/Filters/Values/ValueType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Hyperbee.Json.Filters.Values; - -public struct ValueType( T value ) : INodeType -{ - public readonly NodeTypeKind Kind => NodeTypeKind.Value; - - public INodeTypeComparer Comparer { get; set; } - - public T Value { get; } = value; -} diff --git a/test/Hyperbee.Json.Cts/AssertExtensions.cs b/test/Hyperbee.Json.Cts/AssertExtensions.cs deleted file mode 100644 index 4e0f379b..00000000 --- a/test/Hyperbee.Json.Cts/AssertExtensions.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace Hyperbee.Json.Cts; - -public static class AssertExtensions -{ - public static void ThrowsAny( Action action ) - where T1 : Exception - where T2 : Exception - { - ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ) ); - } - - public static void ThrowsAny( Action action ) - where T1 : Exception - where T2 : Exception - where T3 : Exception - { - ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ), typeof( T3 ) ); - } - - public static void ThrowsAny( Action action ) - where T1 : Exception - where T2 : Exception - where T3 : Exception - where T4 : Exception - { - ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ), typeof( T3 ), typeof( T4 ) ); - } - - public static void ThrowsAny( Action action ) - where T1 : Exception - where T2 : Exception - where T3 : Exception - where T4 : Exception - where T5 : Exception - { - ThrowsAnyInternal( action, typeof( T1 ), typeof( T2 ), typeof( T3 ), typeof( T4 ), typeof( T5 ) ); - } - - private static void ThrowsAnyInternal( Action action, params Type[] expectedExceptionTypes ) - { - Exception? caughtException = null; - - try - { - action(); - } - catch ( Exception? ex ) - { - caughtException = ex; - } - - if ( caughtException == null ) - { - Assert.Fail( $"No exception was thrown. Expected one of: {string.Join( ", ", expectedExceptionTypes.Select( t => t.Name ) )}" ); - } - else if ( !expectedExceptionTypes.Any( e => e.IsInstanceOfType( caughtException ) ) ) - { - Assert.Fail( $"Exception of type {caughtException.GetType().Name} was thrown, but none of the expected types were: {string.Join( ", ", expectedExceptionTypes.Select( t => t.Name ) )}" ); - } - } -} diff --git a/test/Hyperbee.Json.Cts/TestHelper.cs b/test/Hyperbee.Json.Cts/TestHelper.cs deleted file mode 100644 index d7bfd2c1..00000000 --- a/test/Hyperbee.Json.Cts/TestHelper.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Text.Json.Nodes; - -namespace Hyperbee.Json.Cts; - -internal static class TestHelper -{ - // Result Helpers - - public static JsonArray ConvertToJsonArraySet( JsonNode jsonNode ) - { - if ( jsonNode is JsonArray jsonArray && jsonArray[0] is JsonArray ) - return jsonArray; - - JsonArray jsonArraySet = new JsonArray( jsonNode ); - - return jsonArraySet; - } - - public static JsonArray ConvertToJsonArray( IEnumerable nodes, bool force = false ) - { - var nodeArray = nodes.ToArray(); - - if ( !force && nodeArray.Length == 1 && nodeArray[0] is JsonArray array ) - return array; - - var jsonArray = new JsonArray(); - - foreach ( var node in nodeArray ) - { - jsonArray.Add( CopyNode( node ) ); - } - - return jsonArray; - - static JsonNode? CopyNode( JsonNode? node ) - { - return node == null ? null : JsonNode.Parse( node.ToJsonString() ); - } - } - - public static bool MatchAny( IEnumerable results, JsonNode expected ) - { - var expectedSet = ConvertToJsonArraySet( expected ); - var compare = ConvertToJsonArray( results ); - return expectedSet.Any( expect => JsonNode.DeepEquals( expect, compare ) ); - } - - public static bool MatchOne( IEnumerable results, JsonNode expected ) - { - var expect = expected as JsonArray; - var compare = ConvertToJsonArray( results, force: true ); - return JsonNode.DeepEquals( expect, compare ); - } -} diff --git a/test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs b/test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs deleted file mode 100644 index 9bfe5c8d..00000000 --- a/test/Hyperbee.Json.Tests/Query/NodeTypeComparerTests.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Nodes; -using Hyperbee.Json.Descriptors.Node; -using Hyperbee.Json.Filters.Parser; -using Hyperbee.Json.Filters.Values; -using Hyperbee.Json.Tests.TestSupport; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Hyperbee.Json.Tests.Query; - -[TestClass] -public class NodeTypeComparerTests : JsonTestBase -{ - [DataTestMethod] - [DataRow( true, true, true )] - [DataRow( false, false, true )] - [DataRow( false, true, false )] - [DataRow( true, false, false )] - [DataRow( "hello", "hello", true )] - [DataRow( 10F, 10F, true )] - [DataRow( "hello", "world", false )] - [DataRow( 99F, 11F, false )] - [DataRow( "hello", 11F, false )] - [DataRow( false, 11F, false )] - [DataRow( true, 11F, false )] - public void NodeTypeComparer_ShouldCompare_WithEqualResults( object left, object right, bool areEqual ) - { - // Arrange - var comparer = GetComparer(); - var a = GetNodeType( left ); - var b = GetNodeType( right ); - - // Act - var result = comparer.Compare( a, b, Operator.Equals ) == 0; - - // Assert - Assert.AreEqual( areEqual, result ); - } - - [DataTestMethod] - [DataRow( true, true, true )] - [DataRow( false, false, true )] - [DataRow( false, true, false )] - [DataRow( true, false, true )] - [DataRow( "hello", "hello", true )] - [DataRow( 10F, 10F, true )] - [DataRow( 14F, 10F, true )] - [DataRow( 1F, 14F, false )] - public void NodeTypeComparer_ShouldCompare_WithGreaterResults( object left, object right, bool areEqual ) - { - // Arrange - var comparer = GetComparer(); - var a = GetNodeType( left ); - var b = GetNodeType( right ); - - // Act - var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) >= 0; - - // Assert - Assert.AreEqual( areEqual, result ); - } - - [DataTestMethod] - [DataRow( """{ "value": 1 }""", 99F, false )] - [DataRow( """{ "value": 99 }""", 99F, true )] - [DataRow( """{ "value": "hello" }""", "world", false )] - [DataRow( """{ "value": "hello" }""", "hello", true )] - [DataRow( """{ "value": { "child": 5 } }""", "hello", false )] - public void NodeTypeComparer_ShouldCompare_WithJsonObjectResults( string left, object right, bool areEqual ) - { - // Arrange - var comparer = GetComparer(); - var a = GetNodeType( new List { JsonNode.Parse( left )!["value"] } ); - var b = GetNodeType( right ); - - // Act - var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) == 0; - - // Assert - Assert.AreEqual( areEqual, result ); - } - - [DataTestMethod] - [DataRow( """[1,2,3]""", 2F, true )] - [DataRow( """["hello","hi","world" ]""", "hi", true )] - [DataRow( """[1,2,3]""", 99F, false )] - [DataRow( """["hello","world" ]""", "hi", false )] - public void NodeTypeComparer_ShouldCompare_WithLeftJsonArray( string left, object right, bool areEqual ) - { - // Arrange - var comparer = GetComparer(); - var a = GetNodeType( JsonNode.Parse( left )!.AsArray() ); - var b = GetNodeType( right ); - - // Act - var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) == 0; - - // Assert - Assert.AreEqual( areEqual, result ); - } - - [DataTestMethod] - [DataRow( 2F, """[1,2,3]""", true )] - [DataRow( "hi", """["hello","hi","world" ]""", true )] - [DataRow( 99F, """[1,2,3]""", false )] - [DataRow( "hi", """["hello","world" ]""", false )] - public void NodeTypeComparer_ShouldCompare_WithRightJsonArray( object left, string right, bool areEqual ) - { - // Arrange - var comparer = GetComparer(); - var a = GetNodeType( left ); - var b = GetNodeType( JsonNode.Parse( right )!.AsArray() ); - - // Act - var result = comparer.Compare( a, b, Operator.GreaterThanOrEqual ) == 0; - - // Assert - Assert.AreEqual( areEqual, result ); - } - - [TestMethod] - public void NodeTypeComparer_ShouldCompare_WithEmpty() - { - var comparer = GetComparer(); - var a = new NodesType( [], true ); - var b = new ValueType( 1F ); - - Assert.IsFalse( comparer.Compare( a, b, Operator.LessThan ) < 0 ); - Assert.IsFalse( comparer.Compare( a, b, Operator.LessThanOrEqual ) <= 0 ); - - Assert.IsFalse( comparer.Compare( a, b, Operator.GreaterThan ) > 0 ); - Assert.IsFalse( comparer.Compare( a, b, Operator.GreaterThanOrEqual ) >= 0 ); - - Assert.IsFalse( comparer.Compare( a, b, Operator.Equals ) == 0 ); - Assert.IsTrue( comparer.Compare( a, b, Operator.NotEquals ) != 0 ); - } - - private static NodeTypeComparer GetComparer() => new( new NodeValueAccessor() ); - - private static INodeType GetNodeType( object item ) => - item switch - { - string itemString => new ValueType( itemString ), - float itemFloat => new ValueType( itemFloat ), - bool itemBool => new ValueType( itemBool ), - IEnumerable nodes => new NodesType( nodes, true ), - _ => Constants.Nothing - }; -}