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 10 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>
66 changes: 64 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 streams",
"@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 convenince [type for JSON streams](#JSON) streams as well as a term for referencing a [JSON Schema](#Schema) describing the structure of the JSON stream. \n\nIn addition it defines two functions for [querying](#query) JSON streams and using a [primitive value](#value) extracted from a JSON stream in common expressions, for example in `$filter`, `$orderby`, or `$compute`.\n\n**Example**\n\nThe `Employees` entity set has a JSON stream 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 stream property\n```http\nGET http://www.example.com/mycompany/Employees\n ?$filter=resume/JSON.value('$.lastname') eq 'Doe'\n &$orderby=resume/JSON.value('$.experience')\n &$compute=resume/JSON.query('$.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 All @@ -39,8 +40,63 @@
],
"@Core.RequiresType": "Edm.Stream",
"@Core.Description": "The JSON Schema for JSON values of the annotated 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 selector followed by member 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 an OData primitive value",
"@Core.LongDescription": "Extracts a single OData primitive value from the `input` JSON value:\n- If `path` only consists of the root selector followed by member 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 an OData primitive value (see below).\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`.\n\nIf a single non-null scalar JSON value is identified by `path` within `input`, the function returns that value as an OData primitive value of type\n- `Edm.String` if the value is a JSON string\n- `Edm.Boolean` if the value is `true` or `false`\n- `Edm.Decimal` with unspecified precision and floating scale if the value is a JSON number\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": "Edm.PrimitiveType",
"$Nullable": true,
ralfhandl marked this conversation as resolved.
Show resolved Hide resolved
"@Core.Description": "OData primitive value resulting from applying `path` to `input`"
}
}
],
"JSON": {
"$Kind": "TypeDefinition",
"$UnderlyingType": "Edm.Stream",
Expand All @@ -49,6 +105,12 @@
"@Core.AcceptableMediaTypes": [
"application/json"
]
},
"Path": {
"$Kind": "TypeDefinition",
"$UnderlyingType": "Edm.String",
"@Core.Description": "[JSONPath](https://datatracker.ietf.org/doc/html/draft-ietf-jsonpath-base-01) expression",
"@Core.LongDescription": "Implementations SHOULD support at least the following subset of JSONPath:\n\nJSONPath | Description | Examples\n---------|-------------|--------\n`$` | Root selector | `$`\n`.` | Member selector | `$.foo`, `$.foo.bar`\n`[]` | Index selector with member name (single- or double-quoted string using JSON escaping rules) or zero-based array index (non-negative base-10 integer) | `$['foo']`, `$.foo[\"bar\"]`, `$.bar[0]`, `$.bar[42]`\n\nImplementations MAY support in addition:\n\nJSONPath | Description | Examples\n---------|-------------|--------\n`[]` | Index selector with negative integer array index (counts from the end of the array) | `$.bar[-1]`\n`..` | Descendant selector: searches for the specified member name recursively and returns an array of all values with this member name | `$..foo`\n`*` | Wildcard selector matching all elements in an object or array | `$.foo.*`, `$.bar[*]`, `$..*`\n`[,]` | Union selector for alternate member names or array indices as a set | `$.foo['bar','baz']`, `$.bar[0,1,2,3,5,7,11]`\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/draft-ietf-jsonpath-base-01#section-3.5.6) |\n`[?()]` | Filter selector | `$.bar[?(@.baz==42)]`\n`()` | Static expression | `$.bar[(@.length-1)]`\n`@` | in expressions: the current node being processed\n\n**References for JSONPath**\n- Current IETF draft: https://datatracker.ietf.org/doc/html/draft-ietf-jsonpath-base-01\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