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

JSON: function "query" #157

Merged
merged 55 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
d83c0b6
Function "query"
ralfhandl Sep 27, 2021
1641c49
Added "value" function, defined processing rules
ralfhandl Sep 28, 2021
b41aad8
Recommended and optional expressions
ralfhandl Sep 29, 2021
5f153de
Aligned terminology with draft RFC
ralfhandl Sep 29, 2021
b1e1ea9
Examples
ralfhandl Oct 1, 2021
ec1ec9c
Long description of the vocabulary
ralfhandl Oct 1, 2021
778d44c
Update Org.OData.JSON.V1.Schema-sample.xml
ralfhandl Oct 1, 2021
d761c8d
Minor edits
ralfhandl Oct 1, 2021
5ede455
Update Org.OData.JSON.V1.xml
ralfhandl Oct 1, 2021
f9d2a37
Function "value" isn't composable
ralfhandl Oct 1, 2021
b23a8bd
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Oct 14, 2021
aa6cb39
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Oct 15, 2021
c372fbc
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Nov 10, 2021
a3b0ed4
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Dec 6, 2021
7f02bee
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Dec 13, 2021
f8523ba
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Dec 17, 2021
c5b853f
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Feb 3, 2022
c98a1e6
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Feb 10, 2022
82e83fc
Merge remote-tracking branch 'origin/main' into ODATA-1336/json-query
HeikoTheissen May 6, 2022
270b5b4
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Aug 31, 2022
806188c
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Sep 7, 2022
d1848a3
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Dec 20, 2022
a661ad0
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Jan 10, 2023
bd12070
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Mar 21, 2023
470a277
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Apr 27, 2023
ab4a20f
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Jul 4, 2023
8b62d83
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Sep 7, 2023
99fa5ee
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Sep 7, 2023
c5b8c7f
Use JSONPath draft 20
ralfhandl Sep 22, 2023
b83f15a
Updated URLs
ralfhandl Sep 22, 2023
d9c8a69
Additional JSONPath constructs are examples only, no restriction
ralfhandl Sep 22, 2023
1b52e7f
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Dec 1, 2023
3fa98e6
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Feb 29, 2024
6f9faac
Typo
ralfhandl Feb 29, 2024
873715f
Typo
ralfhandl Feb 29, 2024
bb8577f
JSONPath is now an RFC
ralfhandl Feb 29, 2024
5b389f2
Update links
ralfhandl Feb 29, 2024
f2c28dd
Align with RFC text
ralfhandl Feb 29, 2024
d3a7336
MUST for minimum functionality
ralfhandl Mar 7, 2024
e2ac582
JSON data instead of JSON stream
ralfhandl Mar 7, 2024
24d78ab
name=value syntax for function calls
ralfhandl Mar 14, 2024
6c8dc89
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Mar 14, 2024
d41ef8d
Update vocabularies/Org.OData.JSON.V1.xml
ralfhandl Mar 14, 2024
ac67e0b
Update Org.OData.JSON.V1.xml
ralfhandl Mar 14, 2024
e0c93ac
Heiko's comment
ralfhandl Mar 14, 2024
1b5c571
Return-type-specific value functions
HeikoTheissen Apr 4, 2024
269ac57
Example for valueNumber
HeikoTheissen Apr 4, 2024
882237a
Apply suggestions from code review
ralfhandl Apr 4, 2024
f581770
Rebuilt
ralfhandl Apr 4, 2024
b1276f0
Formatting
ralfhandl Apr 4, 2024
b61160b
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Apr 8, 2024
52288cf
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Apr 11, 2024
ab134d0
Merge branch 'main' into ODATA-1336/json-query
ralfhandl Apr 11, 2024
f232900
Revert unintended line breaks
ralfhandl Apr 12, 2024
38c4827
Reference example, don't copy it
HeikoTheissen Apr 18, 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
24 changes: 23 additions & 1 deletion examples/Org.OData.JSON.V1.Schema-sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -37,5 +58,6 @@
}
}
}
}
},
"$EntityContainer": "json.schema.sample.container"
}
18 changes: 15 additions & 3 deletions examples/Org.OData.JSON.V1.Schema-sample.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml">
<edmx:Include Namespace="Org.OData.Core.V1" Alias="Core" />
Expand All @@ -7,7 +7,19 @@
<edmx:Include Namespace="Org.OData.JSON.V1" Alias="JSON" />
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="json.schema.sample" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Schema Namespace="json.schema.sample" Alias="this" xmlns="http://docs.oasis-open.org/odata/ns/edm">

<EntityContainer Name="container">
<EntitySet Name="Employees" EntityType="this.Employee" />
</EntityContainer>

<EntityType Name="Employee">
<Key>
<PropertyRef Name="empid" />
</Key>
<Property Name="empid" Type="Edm.Int32" Nullable="false" />
<Property Name="resume" Type="JSON.JSON" Nullable="true" />
</EntityType>

<ComplexType Name="example">
<Property Name="CodeDictionary" Type="JSON.JSON" Nullable="false">
Expand All @@ -22,4 +34,4 @@

</Schema>
</edmx:DataServices>
</edmx:Edmx>
</edmx:Edmx>
120 changes: 118 additions & 2 deletions vocabularies/Org.OData.JSON.V1.json
Original file line number Diff line number Diff line change
Expand Up @@ -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`:\n```json\n\"container\": {\n \"$Kind\": \"EntityContainer\",\n \"Employees\": { \"$Collection\": true, \"$Type\": \"this.Employee\" }\n},\n\"Employee\": {\n \"$Kind\": \"EntityType\",\n \"$Key\": [\"empid\"],\n \"empid\": { \"$Type\": \"Edm.Int32\" },\n \"resume\": { \"$Type\": \"JSON.JSON\", \"$Nullable\": true }\n}\n```\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",
Expand Down Expand Up @@ -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,
ralfhandl marked this conversation as resolved.
Show resolved Hide resolved
"@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",
Expand All @@ -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`[<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]`\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`[<selector>]` | index selector with negative integer array index (counts from the end of the array) | `$.bar[-1]`\n`[<selectors>]` | non-empty, comma-separated sequence of selectors | `$.foo['bar','baz']`, `$.bar[0,1,2,3,5,7,11]`\n`..[<selectors>]` | [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`[?<logical-expr>]` | [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[[email protected]==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 "
}
}
}
Loading