diff --git a/examples/Org.OData.JSON.V1.Schema-sample.json b/examples/Org.OData.JSON.V1.Schema-sample.json index 962d6fdb..32cf3079 100644 --- a/examples/Org.OData.JSON.V1.Schema-sample.json +++ b/examples/Org.OData.JSON.V1.Schema-sample.json @@ -19,6 +19,27 @@ } }, "json.schema.sample": { + "$Alias": "this", + "container": { + "$Kind": "EntityContainer", + "Employees": { + "$Collection": true, + "$Type": "this.Employee" + } + }, + "Employee": { + "$Kind": "EntityType", + "$Key": [ + "empid" + ], + "empid": { + "$Type": "Edm.Int32" + }, + "resume": { + "$Type": "JSON.JSON", + "$Nullable": true + } + }, "example": { "$Kind": "ComplexType", "CodeDictionary": { @@ -37,5 +58,6 @@ } } } - } + }, + "$EntityContainer": "json.schema.sample.container" } \ No newline at end of file diff --git a/examples/Org.OData.JSON.V1.Schema-sample.xml b/examples/Org.OData.JSON.V1.Schema-sample.xml index 6a7999b7..4cd067de 100644 --- a/examples/Org.OData.JSON.V1.Schema-sample.xml +++ b/examples/Org.OData.JSON.V1.Schema-sample.xml @@ -1,4 +1,4 @@ - + @@ -7,7 +7,19 @@ - + + + + + + + + + + + + + @@ -22,4 +34,4 @@ - \ No newline at end of file + diff --git a/vocabularies/Org.OData.JSON.V1.json b/vocabularies/Org.OData.JSON.V1.json index 2b6cefb6..953b52c6 100644 --- a/vocabularies/Org.OData.JSON.V1.json +++ b/vocabularies/Org.OData.JSON.V1.json @@ -12,7 +12,8 @@ }, "Org.OData.JSON.V1": { "$Alias": "JSON", - "@Core.Description": "Terms for JSON properties", + "@Core.Description": "Terms, types, and functions for JSON data", + "@Core.LongDescription": "OData [stream properties](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ManagingStreamProperties) allow embedding data of arbitrary media types,\nand the OData JSON format allows [direct embedding of JSON data](https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#sec_StreamProperty) in request and response payloads.\n\nThis vocabulary defines a convenience [type for JSON data](#JSON) as well as a term for referencing a [JSON Schema](#Schema) describing the structure of the JSON data.\n\nIn addition it defines two functions for [querying](#query) JSON data and using a [primitive value](#value) extracted from JSON data in common expressions, for example in `$filter`, `$orderby`, or `$compute`.\n\n**Example**\n\nThe `Employees` entity set has a JSON data property `resume` (see [CSDL JSON](../examples/Org.OData.JSON.V1.Schema-sample.json) or [CSDL XML](../examples/Org.OData.JSON.V1.Schema-sample.xml)).\n\nOne of its entities has a `resume` value of\n```json\n{ \n \"ssn\": \"1234\", \n \"lastname\": \"Doe\", \n \"address\": {\n \"zipcode\": \"10022\", \n \"street\": \"ABC st\"\n },\n \"experience\": \"excellent\"\n}\n```\n\nThis allows to filter and sort by values in that resume, and extract parts of the resume as a dynamic JSON data property\n```http\nGET http://www.example.com/mycompany/Employees\n ?$filter=resume/JSON.value(path='$.lastname') eq 'Doe'\n &$orderby=resume/JSON.valueNumber(path='$.experience')\n &$compute=resume/JSON.query(path='$.address') as address\n &$expand=address\n```\nreceiving\n```json\n{ \n \"@odata.context\": \"$metadata#Employees\", \n \"value\": [ \n {\n \"empid\": 4711,\n \"address\": {\n \"zipcode\": \"10022\", \n \"street\": \"ABC st\"\n }\n },\n ...\n ]\n}\n```\n ", "@Core.Links": [ { "rel": "alternate", @@ -40,8 +41,117 @@ ], "@Core.RequiresType": "Edm.Stream", "@Core.Description": "The JSON Schema for JSON values of the annotated media entity type, property, parameter, return type, term, or type definition", - "@Core.LongDescription": "The schema can be a schema reference, i.e. `{\"$ref\":\"url/of/schemafile#/path/to/schema/within/schemafile\"}`" + "@Core.LongDescription": "The schema can be a reference, i.e. `{\"$ref\":\"url/of/schemafile#/path/to/schema/within/schemafile\"}`" }, + "query": [ + { + "$Kind": "Function", + "$IsBound": true, + "$IsComposable": true, + "@Core.Description": "Query a stream value of media type `application/json`, returning a stream value of media type `application/json`", + "@Core.LongDescription": "Extracts a JSON value, such as an array, object, or a JSON scalar value (string, number, boolean, or `null`) from the `input` JSON value:\n- If `path` only consists of the root identifier followed by name and index selectors, it returns the identified single node within `input`, or `null` if no node is identified. \n- If `path` potentially identifies multiple nodes within `input` (by using descendant, wildcard, union, array subset, or filter selectors), it returns an array containing the identified nodes, or an empty array if no node is identified. \n- If `input` is not a valid JSON value, the function returns `null`.\n- If `path` is `null`, not a valid [JSONPath expression](#Path), or does not match the structure of `input` (for example applying an index selector to a scalar value), the function returns `null`. \n ", + "$Parameter": [ + { + "$Name": "input", + "$Type": "JSON.JSON", + "$Nullable": true, + "@Core.Description": "JSON input" + }, + { + "$Name": "path", + "$Type": "JSON.Path", + "$Nullable": true, + "@Core.Description": "JSONPath expression to be applied to value of `expr`" + } + ], + "$ReturnType": { + "$Type": "JSON.JSON", + "$Nullable": true, + "@Core.Description": "JSON value resulting from applying `path` to `input`" + } + } + ], + "value": [ + { + "$Kind": "Function", + "$IsBound": true, + "@Core.Description": "Query a stream value of media type `application/json`, returning a string", + "@Core.LongDescription": "Extracts a single OData primitive value from the `input` JSON value and casts it to a string:\n- If `path` only consists of the root identifier followed by name and index selectors and identifies a single scalar JSON value (string, number, boolean, or `null`) within `input`, it returns the identified single value, cast to a string.\n- If `path` identifies multiple nodes within `input` (by using descendant, wildcard, union, array subset, or filter selectors), identifies an object or array, or does not identify any node, the function returns `null`.\n- If `input` is not a valid JSON value, the function returns `null`.\n- If `path` is `null`, not a valid [JSONPath expression](#Path), or does not match the structure of `input` (for example applying an index selector to a scalar value), the function returns `null`.", + "$Parameter": [ + { + "$Name": "input", + "$Type": "JSON.JSON", + "$Nullable": true, + "@Core.Description": "JSON input" + }, + { + "$Name": "path", + "$Type": "JSON.Path", + "$Nullable": true, + "@Core.Description": "JSONPath expression to be applied to value of `expr`" + } + ], + "$ReturnType": { + "$Nullable": true, + "@Core.Description": "String value resulting from applying `path` to `input`" + } + } + ], + "valueNumber": [ + { + "$Kind": "Function", + "$IsBound": true, + "@Core.Description": "Query a stream value of media type `application/json`, returning a number", + "@Core.LongDescription": "Like [`value`](#value), but casts the extracted value to an `Edm.Decimal` with unspecified precision and floating scale.\n Returns null if that cast fails.", + "$Parameter": [ + { + "$Name": "input", + "$Type": "JSON.JSON", + "$Nullable": true, + "@Core.Description": "JSON input" + }, + { + "$Name": "path", + "$Type": "JSON.Path", + "$Nullable": true, + "@Core.Description": "JSONPath expression to be applied to value of `expr`" + } + ], + "$ReturnType": { + "$Type": "Edm.Decimal", + "$Nullable": true, + "$Scale": "floating", + "@Core.Description": "Numeric value resulting from applying `path` to `input`" + } + } + ], + "valueBoolean": [ + { + "$Kind": "Function", + "$IsBound": true, + "@Core.Description": "Query a stream value of media type `application/json`, returning a Boolean", + "@Core.LongDescription": "Like [`value`](#value), but casts the extracted value to an `Edm.Boolean`.\n Returns null if that cast fails.", + "$Parameter": [ + { + "$Name": "input", + "$Type": "JSON.JSON", + "$Nullable": true, + "@Core.Description": "JSON input" + }, + { + "$Name": "path", + "$Type": "JSON.Path", + "$Nullable": true, + "@Core.Description": "JSONPath expression to be applied to value of `expr`" + } + ], + "$ReturnType": { + "$Type": "Edm.Boolean", + "$Nullable": true, + "@Core.Description": "Boolean value resulting from applying `path` to `input`" + } + } + ], "JSON": { "$Kind": "TypeDefinition", "$UnderlyingType": "Edm.Stream", @@ -50,6 +160,12 @@ "@Core.AcceptableMediaTypes": [ "application/json" ] + }, + "Path": { + "$Kind": "TypeDefinition", + "$UnderlyingType": "Edm.String", + "@Core.Description": "[JSONPath](https://datatracker.ietf.org/doc/html/rfc9535) expression", + "@Core.LongDescription": "Implementations MUST support at least the following subset of JSONPath:\n\nSyntax Element | Description | Examples\n---------------|-------------|--------\n`$` | [root identifier](https://datatracker.ietf.org/doc/html/rfc9535#root-identifier) | `$`\n`[]` | [child segment](https://datatracker.ietf.org/doc/html/rfc9535#child-segment) selects one child of a node; contains one [name selector](https://datatracker.ietf.org/doc/html/rfc9535#name-selector) (single- or double-quoted string using JSON escaping rules) or [index selector](https://datatracker.ietf.org/doc/html/rfc9535#index-selector) (non-negative decimal integer) | `$['foo']`, `$.foo[\"bar\"]`, `$.bar[0]`, `$.bar[42]`\n`.name` | shorthand for `['name']` | `$.foo`, `$.foo.bar`, `$.bar[42].baz`\n\nImplementations MAY in addition support other JSONPath constructs, for example:\n\nSyntax Element | Description | Examples\n---------------|-------------|--------\n`[]` | index selector with negative integer array index (counts from the end of the array) | `$.bar[-1]`\n`[]` | non-empty, comma-separated sequence of selectors | `$.foo['bar','baz']`, `$.bar[0,1,2,3,5,7,11]`\n`..[]` | [descendant segment](https://datatracker.ietf.org/doc/html/rfc9535#descendant-segment): selects zero or more descendants of a node | `$.foo..[\"bar\"]`\n`..name` | shorthand for `..['name']` | `$.foo..bar`\n`*` | [wildcard selector](https://datatracker.ietf.org/doc/html/rfc9535#name-selector): selects all children of a node | `$.foo[*]`, `$[*]`\n`.*` | shorthand for `[*]` | `$.foo.*`, `$.*`\n`..*` | shorthand for `..[*]` | `$.foo..*`, `$..*`\n`[start:end]` | array subset by range of indices (including the item at _start_ and excluding the item at _end_ | `$.bar[2:5]`, same as `$.bar[2,3,4]`\n`[start:]` | array subset from _start_ to end of array | `$.bar[2:]`\n`[:n]` | the first _n_ array items | `$.bar[:4]`\n`[-n:]` | the last _n_ array items | `$.bar[-3:]`\n`[start:end:step]` | [array slice selector](https://datatracker.ietf.org/doc/html/rfc9535#slice) |\n`[?]` | [filter selector](https://datatracker.ietf.org/doc/html/rfc9535#filter-selector): selects particular children using a logical expression | \n`@` | [current node identifier](https://datatracker.ietf.org/doc/html/rfc9535#filter-selector) (valid only within filter selectors) | `$.bar[?@.baz==42]`\n\n**References for JSONPath**\n- RFC 9535: https://datatracker.ietf.org/doc/html/rfc9535\n- Historic site: https://goessner.net/articles/JsonPath/\n- Node.js implementation: https://www.npmjs.com/package/jsonpath\n- Java implementation: https://github.com/json-path/JsonPath\n- Online evaluator: https://jsonpath.com/\n " } } } \ No newline at end of file diff --git a/vocabularies/Org.OData.JSON.V1.md b/vocabularies/Org.OData.JSON.V1.md index d10e30ce..ddf60902 100644 --- a/vocabularies/Org.OData.JSON.V1.md +++ b/vocabularies/Org.OData.JSON.V1.md @@ -1,17 +1,178 @@ # JSON Vocabulary **Namespace: [Org.OData.JSON.V1](Org.OData.JSON.V1.xml)** -Terms for JSON properties +Terms, types, and functions for JSON data + +OData [stream properties](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ManagingStreamProperties) allow embedding data of arbitrary media types, +and the OData JSON format allows [direct embedding of JSON data](https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#sec_StreamProperty) in request and response payloads. + +This vocabulary defines a convenience [type for JSON data](#JSON) as well as a term for referencing a [JSON Schema](#Schema) describing the structure of the JSON data. + +In addition it defines two functions for [querying](#query) JSON data and using a [primitive value](#value) extracted from JSON data in common expressions, for example in `$filter`, `$orderby`, or `$compute`. + +**Example** + +The `Employees` entity set has a JSON data property `resume` (see [CSDL JSON](../examples/Org.OData.JSON.V1.Schema-sample.json) or [CSDL XML](../examples/Org.OData.JSON.V1.Schema-sample.xml)). + +One of its entities has a `resume` value of +```json +{ + "ssn": "1234", + "lastname": "Doe", + "address": { + "zipcode": "10022", + "street": "ABC st" + }, + "experience": "excellent" +} +``` + +This allows to filter and sort by values in that resume, and extract parts of the resume as a dynamic JSON data property +```http +GET http://www.example.com/mycompany/Employees + ?$filter=resume/JSON.value(path='$.lastname') eq 'Doe' + &$orderby=resume/JSON.valueNumber(path='$.experience') + &$compute=resume/JSON.query(path='$.address') as address + &$expand=address +``` +receiving +```json +{ + "@odata.context": "$metadata#Employees", + "value": [ + { + "empid": 4711, + "address": { + "zipcode": "10022", + "street": "ABC st" + } + }, + ... + ] +} +``` + ## Terms Term|Type|Description :---|:---|:---------- -[Schema](./Org.OData.JSON.V1.xml#L67:~:text=The JSON Schema for JSON values of the annotated media entity type, property, parameter, return type, term, or type definition
The schema can be a schema reference, i.e. `{"$ref":"url/of/schemafile#/path/to/schema/within/schemafile"}` +[Schema](./Org.OData.JSON.V1.xml#L119:~:text=The JSON Schema for JSON values of the annotated media entity type, property, parameter, return type, term, or type definition
The schema can be a reference, i.e. `{"$ref":"url/of/schemafile#/path/to/schema/within/schemafile"}` + + +## Functions + + +### [query](./Org.OData.JSON.V1.xml#L127:~:text= +### [value](./Org.OData.JSON.V1.xml#L150:~:text= +### [valueNumber](./Org.OData.JSON.V1.xml#L171:~:text= +### [valueBoolean](./Org.OData.JSON.V1.xml#L187:~:text= -## [JSON](./Org.OData.JSON.V1.xml#L75:~:text= +## [Path](./Org.OData.JSON.V1.xml#L215:~:text=]` | [child segment](https://datatracker.ietf.org/doc/html/rfc9535#child-segment) selects one child of a node; contains one [name selector](https://datatracker.ietf.org/doc/html/rfc9535#name-selector) (single- or double-quoted string using JSON escaping rules) or [index selector](https://datatracker.ietf.org/doc/html/rfc9535#index-selector) (non-negative decimal integer) | `$['foo']`, `$.foo["bar"]`, `$.bar[0]`, `$.bar[42]` +`.name` | shorthand for `['name']` | `$.foo`, `$.foo.bar`, `$.bar[42].baz` + +Implementations MAY in addition support other JSONPath constructs, for example: + +Syntax Element | Description | Examples +---------------|-------------|-------- +`[]` | index selector with negative integer array index (counts from the end of the array) | `$.bar[-1]` +`[]` | non-empty, comma-separated sequence of selectors | `$.foo['bar','baz']`, `$.bar[0,1,2,3,5,7,11]` +`..[]` | [descendant segment](https://datatracker.ietf.org/doc/html/rfc9535#descendant-segment): selects zero or more descendants of a node | `$.foo..["bar"]` +`..name` | shorthand for `..['name']` | `$.foo..bar` +`*` | [wildcard selector](https://datatracker.ietf.org/doc/html/rfc9535#name-selector): selects all children of a node | `$.foo[*]`, `$[*]` +`.*` | shorthand for `[*]` | `$.foo.*`, `$.*` +`..*` | shorthand for `..[*]` | `$.foo..*`, `$..*` +`[start:end]` | array subset by range of indices (including the item at _start_ and excluding the item at _end_ | `$.bar[2:5]`, same as `$.bar[2,3,4]` +`[start:]` | array subset from _start_ to end of array | `$.bar[2:]` +`[:n]` | the first _n_ array items | `$.bar[:4]` +`[-n:]` | the last _n_ array items | `$.bar[-3:]` +`[start:end:step]` | [array slice selector](https://datatracker.ietf.org/doc/html/rfc9535#slice) | +`[?]` | [filter selector](https://datatracker.ietf.org/doc/html/rfc9535#filter-selector): selects particular children using a logical expression | +`@` | [current node identifier](https://datatracker.ietf.org/doc/html/rfc9535#filter-selector) (valid only within filter selectors) | `$.bar[?@.baz==42]` + +**References for JSONPath** +- RFC 9535: https://datatracker.ietf.org/doc/html/rfc9535 +- Historic site: https://goessner.net/articles/JsonPath/ +- Node.js implementation: https://www.npmjs.com/package/jsonpath +- Java implementation: https://github.com/json-path/JsonPath +- Online evaluator: https://jsonpath.com/ + diff --git a/vocabularies/Org.OData.JSON.V1.xml b/vocabularies/Org.OData.JSON.V1.xml index 5b11dda6..bc37d7e2 100644 --- a/vocabularies/Org.OData.JSON.V1.xml +++ b/vocabularies/Org.OData.JSON.V1.xml @@ -45,8 +45,60 @@ - Terms for JSON properties + Terms, types, and functions for JSON data + + OData [stream properties](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ManagingStreamProperties) allow embedding data of arbitrary media types, +and the OData JSON format allows [direct embedding of JSON data](https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#sec_StreamProperty) in request and response payloads. + +This vocabulary defines a convenience [type for JSON data](#JSON) as well as a term for referencing a [JSON Schema](#Schema) describing the structure of the JSON data. + +In addition it defines two functions for [querying](#query) JSON data and using a [primitive value](#value) extracted from JSON data in common expressions, for example in `$filter`, `$orderby`, or `$compute`. + +**Example** + +The `Employees` entity set has a JSON data property `resume` (see [CSDL JSON](../examples/Org.OData.JSON.V1.Schema-sample.json) or [CSDL XML](../examples/Org.OData.JSON.V1.Schema-sample.xml)). + +One of its entities has a `resume` value of +```json +{ + "ssn": "1234", + "lastname": "Doe", + "address": { + "zipcode": "10022", + "street": "ABC st" + }, + "experience": "excellent" +} +``` + +This allows to filter and sort by values in that resume, and extract parts of the resume as a dynamic JSON data property +```http +GET http://www.example.com/mycompany/Employees + ?$filter=resume/JSON.value(path='$.lastname') eq 'Doe' + &$orderby=resume/JSON.valueNumber(path='$.experience') + &$compute=resume/JSON.query(path='$.address') as address + &$expand=address +``` +receiving +```json +{ + "@odata.context": "$metadata#Employees", + "value": [ + { + "empid": 4711, + "address": { + "zipcode": "10022", + "street": "ABC st" + } + }, + ... + ] +} +``` + + + @@ -68,10 +120,87 @@ - The schema can be a schema reference, i.e. `{"$ref":"url/of/schemafile#/path/to/schema/within/schemafile"}` + The schema can be a reference, i.e. `{"$ref":"url/of/schemafile#/path/to/schema/within/schemafile"}` + + + + Extracts a JSON value, such as an array, object, or a JSON scalar value (string, number, boolean, or `null`) from the `input` JSON value: +- If `path` only consists of the root identifier followed by name and index selectors, it returns the identified single node within `input`, or `null` if no node is identified. +- If `path` potentially identifies multiple nodes within `input` (by using descendant, wildcard, union, array subset, or filter selectors), it returns an array containing the identified nodes, or an empty array if no node is identified. +- If `input` is not a valid JSON value, the function returns `null`. +- If `path` is `null`, not a valid [JSONPath expression](#Path), or does not match the structure of `input` (for example applying an index selector to a scalar value), the function returns `null`. + + + + + + + + + + + + + + + + + Extracts a single OData primitive value from the `input` JSON value and casts it to a string: +- If `path` only consists of the root identifier followed by name and index selectors and identifies a single scalar JSON value (string, number, boolean, or `null`) within `input`, it returns the identified single value, cast to a string. +- If `path` identifies multiple nodes within `input` (by using descendant, wildcard, union, array subset, or filter selectors), identifies an object or array, or does not identify any node, the function returns `null`. +- If `input` is not a valid JSON value, the function returns `null`. +- If `path` is `null`, not a valid [JSONPath expression](#Path), or does not match the structure of `input` (for example applying an index selector to a scalar value), the function returns `null`. + + + + + + + + + + + + + + + Like [`value`](#value), but casts the extracted value to an `Edm.Decimal` with unspecified precision and floating scale. + Returns null if that cast fails. + + + + + + + + + + + + + + + Like [`value`](#value), but casts the extracted value to an `Edm.Boolean`. + Returns null if that cast fails. + + + + + + + + + + + + @@ -82,6 +211,48 @@ + + + + + Implementations MUST support at least the following subset of JSONPath: + +Syntax Element | Description | Examples +---------------|-------------|-------- +`$` | [root identifier](https://datatracker.ietf.org/doc/html/rfc9535#root-identifier) | `$` +`[<selector>]` | [child segment](https://datatracker.ietf.org/doc/html/rfc9535#child-segment) selects one child of a node; contains one [name selector](https://datatracker.ietf.org/doc/html/rfc9535#name-selector) (single- or double-quoted string using JSON escaping rules) or [index selector](https://datatracker.ietf.org/doc/html/rfc9535#index-selector) (non-negative decimal integer) | `$['foo']`, `$.foo["bar"]`, `$.bar[0]`, `$.bar[42]` +`.name` | shorthand for `['name']` | `$.foo`, `$.foo.bar`, `$.bar[42].baz` + +Implementations MAY in addition support other JSONPath constructs, for example: + +Syntax Element | Description | Examples +---------------|-------------|-------- +`[<selector>]` | index selector with negative integer array index (counts from the end of the array) | `$.bar[-1]` +`[<selectors>]` | non-empty, comma-separated sequence of selectors | `$.foo['bar','baz']`, `$.bar[0,1,2,3,5,7,11]` +`..[<selectors>]` | [descendant segment](https://datatracker.ietf.org/doc/html/rfc9535#descendant-segment): selects zero or more descendants of a node | `$.foo..["bar"]` +`..name` | shorthand for `..['name']` | `$.foo..bar` +`*` | [wildcard selector](https://datatracker.ietf.org/doc/html/rfc9535#name-selector): selects all children of a node | `$.foo[*]`, `$[*]` +`.*` | shorthand for `[*]` | `$.foo.*`, `$.*` +`..*` | shorthand for `..[*]` | `$.foo..*`, `$..*` +`[start:end]` | array subset by range of indices (including the item at _start_ and excluding the item at _end_ | `$.bar[2:5]`, same as `$.bar[2,3,4]` +`[start:]` | array subset from _start_ to end of array | `$.bar[2:]` +`[:n]` | the first _n_ array items | `$.bar[:4]` +`[-n:]` | the last _n_ array items | `$.bar[-3:]` +`[start:end:step]` | [array slice selector](https://datatracker.ietf.org/doc/html/rfc9535#slice) | +`[?<logical-expr>]` | [filter selector](https://datatracker.ietf.org/doc/html/rfc9535#filter-selector): selects particular children using a logical expression | +`@` | [current node identifier](https://datatracker.ietf.org/doc/html/rfc9535#filter-selector) (valid only within filter selectors) | `$.bar[?@.baz==42]` + +**References for JSONPath** +- RFC 9535: https://datatracker.ietf.org/doc/html/rfc9535 +- Historic site: https://goessner.net/articles/JsonPath/ +- Node.js implementation: https://www.npmjs.com/package/jsonpath +- Java implementation: https://github.com/json-path/JsonPath +- Online evaluator: https://jsonpath.com/ + + + +