Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE]: Use Expressions For Filters #7

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
aff230d
Create draft PR for #1
github-actions[bot] May 8, 2024
5e66e76
Created a generic abstract JsonPathVisitorBase that each implementati…
MattEdwardsWaggleBee May 8, 2024
80bd1bf
Create draft PR for #6
github-actions[bot] May 29, 2024
59ddb49
Merge remote-tracking branch 'origin/feature/1-feature-create-common-…
MattEdwardsWaggleBee May 29, 2024
d026455
WIP for Using Expression
MattEdwardsWaggleBee May 29, 2024
69be400
Updated code formatting to match rules in .editorconfig
invalid-email-address May 29, 2024
af2e32e
Add Benchmarks to compare C# version expression for filters evaluation.
MattEdwardsWaggleBee May 30, 2024
49a7243
Updated code formatting to match rules in .editorconfig
invalid-email-address May 30, 2024
0332314
Switch to using BasePath as an argument versus as part of the express…
MattEdwardsWaggleBee May 30, 2024
4c2a92d
Updated code formatting to match rules in .editorconfig
invalid-email-address May 30, 2024
11f7b93
Configure Benchmark and added Newtonsoft, so we can see how much we n…
MattEdwardsWaggleBee May 31, 2024
43faa3e
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
MattEdwardsWaggleBee May 31, 2024
afc2836
Add some unhappy parse tests
MattEdwardsWaggleBee May 31, 2024
489f697
Updated code formatting to match rules in .editorconfig
invalid-email-address May 31, 2024
2697ada
Remove CSharpEvaluator and Reverse, then added enumerate children
MattEdwardsWaggleBee Jun 7, 2024
98eb592
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 7, 2024
8f13276
Add JsonElementPositionComparer
bfarmer67 Jun 7, 2024
3426883
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 7, 2024
e93cb2d
Removed unnecessary JsonPropertyKeyExtension for JsonDocument.
bfarmer67 Jun 7, 2024
ca916e1
Fixed class name.
bfarmer67 Jun 7, 2024
ea8fb35
Removed SelectPath and building of path while querying a document
MattEdwardsWaggleBee Jun 7, 2024
7b28476
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
MattEdwardsWaggleBee Jun 7, 2024
2f2bd22
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 7, 2024
703dd6a
Added a filter to the parse and select benchmark
MattEdwardsWaggleBee Jun 7, 2024
dae15aa
Removed Select overload for JsonFuncEvaluator.
bfarmer67 Jun 7, 2024
e0c2d3e
Changed filter evaluator parameter name to filter.
bfarmer67 Jun 7, 2024
fac0d72
Updated readme.
bfarmer67 Jun 7, 2024
effe778
Initial work on JsonPathBuilder.
bfarmer67 Jun 7, 2024
92cd7dc
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 7, 2024
aded089
JsonPathBuilder initial naive working version.
bfarmer67 Jun 8, 2024
9388617
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 8, 2024
0b8cc00
Removed Func and Null filter evaluators
bfarmer67 Jun 8, 2024
1876752
Adjusted accessibility modifiers
bfarmer67 Jun 8, 2024
af23392
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
bfarmer67 Jun 8, 2024
d384262
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 8, 2024
da615a0
Implemented JsonPathBuilder caching
bfarmer67 Jun 8, 2024
5f714db
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
bfarmer67 Jun 8, 2024
c2f3504
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 8, 2024
0f071ec
Comment cleanup
bfarmer67 Jun 8, 2024
867ae52
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
bfarmer67 Jun 8, 2024
8618020
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 8, 2024
7158145
Fixed merge formatting issue
bfarmer67 Jun 8, 2024
a1f7056
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 8, 2024
59d78cb
Fixed merge formatting conflict
bfarmer67 Jun 8, 2024
86ef63c
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 8, 2024
b80b917
Refactor filter usages
bfarmer67 Jun 10, 2024
621d785
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 10, 2024
fa1f4eb
small cleanups to comments and method signatures
bfarmer67 Jun 10, 2024
28cc93e
Singular property need only access array element once
bfarmer67 Jun 10, 2024
559687c
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 10, 2024
a846f7d
Refactored use of JsonElement and JsonNode into different implementat…
MattEdwardsWaggleBee Jun 11, 2024
cc46d7e
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
MattEdwardsWaggleBee Jun 11, 2024
eeeb9e1
Slight optimization to JsonPathToken Singular
bfarmer67 Jun 12, 2024
21692d6
Updated readme
bfarmer67 Jun 12, 2024
e6f3dda
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 12, 2024
aadfe57
Moved position of IsSingular in class
bfarmer67 Jun 12, 2024
5bd89ca
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
bfarmer67 Jun 12, 2024
7818833
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 12, 2024
147345b
Refactor namespace
MattEdwardsWaggleBee Jun 12, 2024
e37e7b8
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
MattEdwardsWaggleBee Jun 12, 2024
066a5a6
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 12, 2024
786d51f
Push for brent
MattEdwardsWaggleBee Jun 12, 2024
c585988
Missing files
MattEdwardsWaggleBee Jun 12, 2024
40c356d
Improve Segment debug view
bfarmer67 Jun 13, 2024
39be972
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 13, 2024
88798b8
Rename SegmentArgs to NodeArgs
bfarmer67 Jun 13, 2024
c48b1a5
Add Truthy helper method
bfarmer67 Jun 13, 2024
d148ac8
Fix Tokenizer tests and added `AsEnumerable()` to Segment to help ite…
MattEdwardsWaggleBee Jun 13, 2024
0bbf8b6
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 13, 2024
a249034
comments and simple naming changes
bfarmer67 Jun 13, 2024
29f5349
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 13, 2024
bf92643
Update access modifiers
bfarmer67 Jun 13, 2024
590a0f2
small changes to JsonPath implementations, simple renames, and remova…
bfarmer67 Jun 13, 2024
64748c5
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 13, 2024
14ef728
Comment
bfarmer67 Jun 13, 2024
e93e7b7
Fix bad namespace
bfarmer67 Jun 13, 2024
1260394
Readme improvements
bfarmer67 Jun 13, 2024
95fc192
Push arguments rename
bfarmer67 Jun 13, 2024
0ace7ed
Convert JsonPath to static class, reduce IValueAccessor.GetChildValue…
bfarmer67 Jun 13, 2024
a771f36
Add deconstructor implementation to ITypeDescriptor
bfarmer67 Jun 13, 2024
dd6c934
Remove JsonPathSegment MoveNext
bfarmer67 Jun 14, 2024
c565bf3
Add selector deconstructor
bfarmer67 Jun 14, 2024
f631435
Update regex to allow for optional parens for filter expressions
MattEdwardsWaggleBee Jun 14, 2024
3be021f
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
MattEdwardsWaggleBee Jun 14, 2024
e1bda32
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 14, 2024
ae1a13a
switch to flag enums
MattEdwardsWaggleBee Jun 14, 2024
7207ee5
Add test for filter without surrounding parentheses
bfarmer67 Jun 14, 2024
8e6230a
Merge branch 'feature/6-feature-use-expressions-for-filters' of https…
bfarmer67 Jun 14, 2024
344d7d5
Add test for filter without surrounding parenthesis
bfarmer67 Jun 14, 2024
a9331e6
Updated code formatting to match rules in .editorconfig
invalid-email-address Jun 14, 2024
16e57a9
Update generics to be TNode vs TElement
MattEdwardsWaggleBee Jun 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Hyperbee.Json.sln
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbee.Json.Tests", "test\Hyperbee.Json.Tests\Hyperbee.Json.Tests.csproj", "{97886205-1467-4EE6-B3DA-496CA3D086E4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbee.Json.Benchmark", "test\Hyperbee.Json.Benchmark\Hyperbee.Json.Benchmark.csproj", "{45C24D4B-4A0B-4FF1-AC66-38374D2455E9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -48,6 +50,10 @@ Global
{97886205-1467-4EE6-B3DA-496CA3D086E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97886205-1467-4EE6-B3DA-496CA3D086E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97886205-1467-4EE6-B3DA-496CA3D086E4}.Release|Any CPU.Build.0 = Release|Any CPU
{45C24D4B-4A0B-4FF1-AC66-38374D2455E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -56,6 +62,7 @@ Global
{1FA7CE2A-C9DA-4DC3-A242-5A7EAF8EE4FC} = {870D9301-BE3D-44EA-BF9C-FCC2E87FE4CD}
{4DBDB7F5-3F66-4572-80B5-3322449C77A4} = {1FA7CE2A-C9DA-4DC3-A242-5A7EAF8EE4FC}
{97886205-1467-4EE6-B3DA-496CA3D086E4} = {F9B24CD9-E06B-4834-84CB-8C29E5F10BE0}
{45C24D4B-4A0B-4FF1-AC66-38374D2455E9} = {F9B24CD9-E06B-4834-84CB-8C29E5F10BE0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {32874F5B-B467-4F28-A8E2-82C2536FB228}
Expand Down
6 changes: 6 additions & 0 deletions Hyperbee.Json.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="__" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PublicFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="__" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=53eecf85_002Dd821_002D40e8_002Dac97_002Dfdb734542b84/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=70345118_002D4b40_002D4ece_002D937c_002Dbbeb7a0b2e70/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=C7A83AA09B781941AB1F1DC475E56D3B/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=C7A83AA09B781941AB1F1DC475E56D3B/AbsolutePath/@EntryValue">C:\Development\Hyperbee.Core\Hyperbee.Core.sln.DotSettings</s:String>
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=C7A83AA09B781941AB1F1DC475E56D3B/RelativePath/@EntryValue"></s:String>
Expand All @@ -45,6 +50,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/GrammarAndSpelling/GrammarChecking/Exceptions/=the_0020expire/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/GrammarAndSpelling/GrammarChecking/Exceptions/=the_0020if/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Analytics/@EntryIndexedValue">True</s:Boolean>
Expand Down
164 changes: 69 additions & 95 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,42 @@
# JSONPath

A C# implementation of JSONPath for .NET `System.Text.Json` with a plugable expression selector.
JSON Path is a query language for JSON documents inspired by XPath. JSONPath defines
a string syntax for selecting and extracting JSON values from within a given JSON document.

## Why
This library is a C# implementation of JSONPath for .NET `System.Text.Json` and `System.Text.Json.Nodes`.

.NET `System.Text.Json` lacks support for JSONPath. The primary goal of this project is to create a JSONPath library for .NET that will
The implementation

* Directly leverage `System.Text.Json`
* Align with the draft JSONPath Specification
* Works natively with both `JsonDocument` (`JsonElement`) and `JsonNode`
* Can be extended to support other JSON models
* Aligns with the draft JSONPath Specification RFC 9535
* [Working Draft](https://github.com/ietf-wg-jsonpath/draft-ietf-jsonpath-base).
* [Editor Copy](https://ietf-wg-jsonpath.github.io/draft-ietf-jsonpath-base/draft-ietf-jsonpath-base.html)
* Function according to the emerging consensus of use based on the majority of existing implementations; except through concious exception or deference to the RFC.
* [Parser Comparison](https://cburgmer.github.io/json-path-comparison)
* Provide a plugable model for expression script handling.
* Functions according to the emerging consensus of use based on the majority of existing
implementations; except through concious exception or deference to the RFC.
* [Parser Comparison Results](https://cburgmer.github.io/json-path-comparison)
* [Parser Comparison GitHub](https://github.com/cburgmer/json-path-comparison/tree/master)


## JSONPath Expressions
## JSONPath Syntax

JSONPath expressions always refers to a JSON structure in the same way as XPath
expression are used in combination with an XML document. Since a JSON structure is
usually anonymous and doesn't necessarily have a root member object JSONPath
assumes the abstract name `$` assigned to the outer level object.
JSONPath expressions 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 can use the dot-notation:
JSONPath expressions can use dot-notation:

$.store.book[0].title

or the bracket-notation:
or bracket-notation:

$['store']['book'][0]['title']

for input paths. Internal or output paths will always be converted to the more
general bracket-notation.

JSONPath allows the wildcard symbol `*` for member names and array indices. It
borrows the descendant operator `..` from [E4X][e4x] and the array slice
borrows the descendant operator `..` from [E4X][e4x], and the array slice
syntax proposal `[start:end:step]` from ECMASCRIPT 4.

Expressions of the underlying scripting language (`<expr>`) can be used as an
alternative to explicit names or indices, as in:
Expressions can be used as an alternative to explicit names or indices, as in:

$.store.book[(@.length-1)].title

Expand All @@ -54,15 +52,15 @@ syntax elements with its XPath counterparts:
|:----------|:-------------------|:-----------------------------------------------------------
| `/` | `$` | The root object/element
| `.` | `@` | The current object/element
| `/` | `.` or `[]` | Child operator
| `/` | `.` or `[]` | Child operator
| `..` | n/a | Parent operator
| `//` | `..` | Recursive descent. JSONPath borrows this syntax from E4X.
| `*` | `*` | Wildcard. All objects/elements regardless their names.
| `//` | `..` | Recursive descent. JSONPath borrows this syntax from E4X.
| `*` | `*` | Wildcard. All objects/elements regardless their names.
| `@` | n/a | Attribute access. JSON structures don't have attributes.
| `[]` | `[]` | Subscript operator. XPath uses it to iterate over element collections and for [predicates][xpath-predicates]. In Javascript and JSON it is the native array operator.
| `\|` | `[,]` | Union operator in XPath results in a combination of node sets. JSONPath allows alternate names or array indices as a set.
| n/a | `[start:end:step]`| Array slice operator borrowed from ES4.
| `[]` | `?()` | Applies a filter (script) expression.
| `[]` | `[]` | Subscript operator. XPath uses it to iterate over element collections and for [predicates][xpath-predicates]. In Javascript and JSON it is the native array operator.
| `\|` | `[,]` | Union operator in XPath results in a combination of node sets. JSONPath allows alternate names or array indices as a set.
| n/a | `[start:end:step]` | Array slice operator borrowed from ES4.
| `[]` | `?()` | Applies a filter (script) expression.
| n/a | `()` | Script expression, using the underlying script engine.
| `()` | n/a | Grouping in XPath

Expand Down Expand Up @@ -106,67 +104,20 @@ Given a simple JSON structure that represents a bookstore:

| XPath | JSONPath | Result | Notes
|:----------------------|:--------------------------|:---------------------------------------|:------
|`/store/book/author` | `$.store.book[*].author` | The authors of all books in the store
|`//author` | `$..author` | All authors
|`/store/*` | `$.store.*` | All things in store, which are some books and a red bicycle
|`/store//price` | `$.store..price` | The price of everything in the store
|`//book[3]` | `$..book[2]` | The third book
|`//book[last()]` | `$..book[(@.length-1)]<br>$..book[-1:]` | The last book in order
|`//book[position()<3]`| `$..book[0,1]`<br>`$..book[:2]`| The first two books
|`/store/book/author` | `$.store.book[*].author` | The authors of all books in the store
|`//author` | `$..author` | All authors
|`/store/*` | `$.store.*` | All things in store, which are some books and a red bicycle
|`/store//price` | `$.store..price` | The price of everything in the store
|`//book[3]` | `$..book[2]` | The third book
|`//book[last()]` | `$..book[(@.length-1)]<br>$..book[-1:]` | The last book in order
|`//book[position()<3]` | `$..book[0,1]`<br>`$..book[:2]`| The first two books
|`//book/*[self::category|self::author]` or `//book/(category,author)` in XPath 2.0 | `$..book[category,author]` | The categories and authors of all books
|`//book[isbn]` | `$..book[?(@.isbn)]` | Filter all books with `isbn` number
|`//book[price<10]` | `$..book[?(@.price<10)]` | Filter all books cheapier than 10
|`//*[price>19]/..` | `$..[?(@.price>19)]` | Categories with things more expensive than 19 | Parent (caret) not present in original spec
|`//*` | `$..*` | All elements in XML document; all members of JSON structure
|`//book[isbn]` | `$..book[?(@.isbn)]` | Filter all books with `isbn` number
|`//book[price<10]` | `$..book[?(@.price<10)]` | Filter all books cheapier than 10
|`//*[price>19]/..` | `$..[?(@.price>19)]` | Categories with things more expensive than 19 | Parent (caret) not present in original spec
|`//*` | `$..*` | All elements in XML document; all members of JSON structure
|`/store/book/[position()!=1]` | `$.store.book[?(@path !== "$[\'store\'][\'book\'][0]")]` | All books besides that at the path pointing to the first | `@path` not present in original spec

## Script Evaluators

`Hyperbee.Json` provides out-of-the-box expression evaluators for handling JsonPath filter selectors.

| Name | Description |
| ----------------------- | ----------- |
| JsonPathCSharpEvaluator | A Roslyn based expression evaluator that supports `[(@...)]` and `[?(@...)]` expresison syntax|
| JsonPathFuncEvaluator | A simple `Func<>` evaluator suitable for simple, custom expression handling |
| JsonPathNullEvaluator | An evaluator that does nothing |

You can create your own evaluator by deriving from `IJsonPathScriptEvaluator`.

```csharp
public class JsonPathFuncEvaluator : IJsonPathScriptEvaluator
{
private readonly JsonPathEvaluator _evaluator;

public JsonPathFuncEvaluator( JsonPathEvaluator evaluator )
{
_evaluator = evaluator;
}

public object Evaluator( string script, JsonElement current, string context )
{
return _evaluator?.Invoke( script, current, context );
}
}
```

You can set a global default for the evaluator.

```csharp
JsonPath.DefaultEvaluator = new JsonPathCSharpEvaluator();
```

Or you can wire it up through dependency injection.

```csharp
public static IServiceCollection AddJsonPath( this IServiceCollection services, IConfiguration config )
{
services.AddTransient<IJsonPathScriptEvaluator,JsonPathCSharpEvaluator>();
services.AddTransient<JsonPath>();

return services;
}
```

## Code examples
A couple of trivial code examples. Review the tests for detailed examples.

Expand All @@ -180,13 +131,13 @@ const string json = @"
]";

var document = JsonDocument.Parse( json );
var match = document.SelectPath( "$[-1:]" ).Single();
var match = document.Select( "$[-1:]" ).Single();

Assert.IsTrue( match.Value.GetString() == "third" );
```

**Example 2** Select all elemets that have a `key` property with a value less than 42.
This example leverages bracket expressions using the `Roslyn` jsonpath script evaluator.
This example leverages bracket expressions using the default `Expression` jsonpath filter evaluator.

```csharp
const string json = @"
Expand All @@ -203,7 +154,7 @@ const string json = @"
]";

var document = JsonDocument.Parse( json );
var matches = document.SelectPath( "$[?(@.key<42)]", JsonPathCSharpEvaluator.Evaluator );
var matches = document.Select( "$[?(@.key<42)]" );

// outputs 0 -1 41 41.9999

Expand All @@ -213,13 +164,17 @@ foreach( var element in matches )
};

```
## Helper Classes

In addition to JSONPath processing, a few additional helper classes are provided to support dynamic property access,
property diving, and element comparisons.

## Dynamic Object Serialization
### Dynamic Object Serialization

Basic support is provided for serializing to and from dynamic objects through the use of a custom `JsonConverter`.
The `DynamicJsonConverter` converter class is useful for simple scenareos. It is intended as a simple helper for basic use cases only.

### DynamicJsonConverter
#### DynamicJsonConverter

```csharp
var serializerOptions = new JsonSerializerOptions
Expand All @@ -237,16 +192,35 @@ var jsonOutput = JsonSerializer.Serialize<dynamic>( jobject, serializerOptions )
Assert.IsTrue( jsonInput == jsonOutput );
```

#### Enum handling
##### Enum handling

When deserializing, the converter will treat enumerations as strings. You can override this behavior by setting
the `TryReadValueHandler` on the converter. This handler will allow you to intercept and convert string and
numeric values during the deserialization process.

### Equality Helpers

| Method | Description
|:-----------------------------------|:-----------
| `JsonElement.DeepEquals` | Performs a deep equals comparison
| `JsonElementEqualityDeepComparer` | A deep equals equality comparer

### Property Diving

| Method | Description
|:-----------------------------------|:-----------
| `JsonElement.GetPropertyFromKey` | Dives for properties using absolute bracket location keys like `$['store']['book'][2]['author']`

### JsonElement Helpers

| Method | Description
|:-----------------------------------|:-----------
| `JsonPathBuilder` | Returns the JsonPath location string for a given element

## Acknowlegements

This project builds on the work of:

[Stefan G&ouml;ssner - Original JSONPath specification dated 2007-02-21](http://goessner.net/articles/JsonPath/#e2)
[Atif Aziz - .NET JSONPath](https://github.com/atifaziz/JSONPath)
[Christoph Burgmer - Parser Consensus tests](https://cburgmer.github.io/json-path-comparison)
* [Stefan G&ouml;ssner - Original JSONPath specification dated 2007-02-21](http://goessner.net/articles/JsonPath/#e2)
* [Atif Aziz - .NET JSONPath](https://github.com/atifaziz/JSONPath)
* [Christoph Burgmer - Parser Consensus tests](https://cburgmer.github.io/json-path-comparison)
Loading
Loading