From 06dc8f0dcd0ee51a4593c290653c367bed400e64 Mon Sep 17 00:00:00 2001 From: Henry Andrews Date: Fri, 4 Nov 2016 14:40:03 -0700 Subject: [PATCH] Replace template preprocessing with re-mapping. This change attempts to minimally address the awkwardness and shortcomings of the current URI template preprocessing approach. In addition to its dubious aesthetics, preprocessing does not help with using values other than the current instance or an immediate property (for an object instance) or element (for an array instance). Instead, use an object to map the variable names to locations in the instance with relative JSON pointers. This neatly solves both the UTF-8/illegal variable name problem and the complex data structure problem with the same mechanism. This is close to what is proposed in #52, but that proposal includes several other related features which are understandably more controversial. I think this is actually closer to the original proposal that preceded #52 in the old repo. The change from `vars` to `hrefVars` was inspired by the terminology being used in the JSON Home project. While compatibility with JSON Home is not a goal, I like how this clarifies exactly what the keyword is intended to do. Since `base` is also a template which was defined to use the same preprocessing as `href`, it likewise gets a corresponding `baseVars`. --- hyper-schema.json | 10 ++ jsonschema-hyperschema.xml | 307 +++++++++++++++++++++---------------- 2 files changed, 183 insertions(+), 134 deletions(-) diff --git a/hyper-schema.json b/hyper-schema.json index ec9c1441..fa518316 100644 --- a/hyper-schema.json +++ b/hyper-schema.json @@ -50,6 +50,11 @@ "description": "URI Template resolved as for the 'href' keyword in the Link Description Object. The resulting URI Reference is resolved against the current URI base and sets the new URI base for URI references within the instance.", "type": "string" }, + "baseVars": { + "description": "a mapping from URI template names in \"base\" to relative JSON pointers to be applied to the instance to find the template variable's value", + "type": "object", + "additionalProperties": {"type": "string"} + }, "links": { "type": "array", "items": {"$ref": "#/definitions/linkDescription"} @@ -82,6 +87,11 @@ "description": "a URI template, as defined by RFC 6570, with the addition of the $, ( and ) characters for pre-processing", "type": "string" }, + "hrefVars": { + "description": "a mapping from URI template names in \"href\" to relative JSON pointers to be applied to the instance to find the template variable's value", + "type": "object", + "additionalProperties": {"type": "string"} + }, "rel": { "description": "relation to the target resource of the link", "type": "string" diff --git a/jsonschema-hyperschema.xml b/jsonschema-hyperschema.xml index f691694f..d49c28e2 100644 --- a/jsonschema-hyperschema.xml +++ b/jsonschema-hyperschema.xml @@ -187,7 +187,11 @@ It is therefore the first URI Reference resolved, regardless of which order it was found in. - The URI is computed from the provided URI template using the same process described for the "href" property of a Link Description Object. + The URI is computed from the provided URI template, along with + "baseVars", using the same + process described for the "href" + and "hrefVars" properties of + a Link Description Object.
An example of a JSON schema using "base": @@ -232,6 +236,27 @@ +
+ + The value of this keyword MUST be an object. + The property names in the object SHOULD each be identical to + a template variable from the "base" value in this schema. + The value of each property MUST be a + Relative JSON Pointer. + + + The "baseVars" object is used to resolve "base" template + variable names that do not meet the restrictions of the + URI Template specification, + or that need to resolve to values other than immediate + properties of the instance. + + + See the "hrefVars" link + description keyword for the rules for using the mappings. + +
+
The "links" property of schemas is used to associate Link Description Objects with instances. The value of this property MUST be an array, and the items in the array must be Link Description Objects, as defined below. @@ -354,150 +379,155 @@ "Form"-like functionality can be defined by use of the "method" and "schema" keywords, which supplies a schema describing the data to supply to the server. -
- - The value of the "href" link description property is a template used to determine the target URI of the related resource. - The value of the instance property MUST be resolved as a URI-reference against the base URI of the instance. - - - This property is REQUIRED. - - -
+
+
- The value of "href" is to be used as a URI Template, as defined in RFC 6570. However, some special considerations apply: + The value of the "href" link description property is a template + used to determine the target URI of the related resource. - -
- - This pre-processing section is subject to significant change in upcoming drafts. - - - The URI Template specification restricts the set of characters available for variable names. - Property names in JSON, however, can be any UTF-8 string. - - - - To allow the use of any JSON property name in the template, before using the value of "href" as a URI Template, the following pre-processing rules MUST be applied, in order: - - -
- - The purpose of this step is to allow the use of brackets to percent-encode variable names inside curly brackets. - Variable names to be escaped are enclosed within rounded brackets, with the close-rounded-bracket character ")" being escaped as a pair of close-rounded-brackets "))". - Since the empty string is not a valid variable name in RFC 6570, an empty pair of brackets is replaced with "%65mpty". - - - - The rules are as follows: - - - - Find the largest possible sections of the text such that: - - do not contain an odd number of close-rounded-bracket characters ")" in sequence in that section of the text - are surrounded by a pair of rounded brackets: ( ), where - the surrounding rounded brackets are themselves contained within a pair of curly brackets: { } - - - - Each of these sections of the text (including the surrounding rounded brackets) MUST be replaced, according to the following rules: - - If the brackets contained no text (the empty string), then they are replaced with "%65mpty" (which is "empty" with a percent-encoded "e") - Otherwise, the enclosing brackets are removed, and the inner text used after the following modifications - - all pairs of close-brackets "))" are replaced with a single close bracket - after that, the text is replaced with its percent-encoded equivalent, such that the result is a valid RFC 6570 variable name (note that this requires encoding characters such as "*" and "!") - - - - -
- -
- - After the above substitutions, if the character "$" (dollar sign) appears within a pair of curly brackets, then it MUST be replaced with the text "%73elf" (which is "self" with a percent-encoded "s"). - - - The purpose of this stage is to allow the use of the instance value itself (instead of its object properties or array items) in the URI Template, by the special value "%73elf". - -
- -
- - The special-case values of "%73elf" and "%65mpty" were chosen because they are unlikely to be accidentally generated by either a human or automated escaping. - -
- -
- - For example, here are some possible values for "href", followed by the results after pre-processing: - Input - Output - "no change" "no change" - "(no change)" "(no change)" - "{(escape space)}" "{escape%20space}" - "{(escape+plus)}" "{escape%2Bplus}" - "{(escape*asterisk)}" "{escape%2Aasterisk}" - "{(escape(bracket)}" "{escape%28bracket}" - "{(escape))bracket)}" "{escape%29bracket}" - "{(a))b)}" "{a%29b} - "{(a (b)))}" "{a%20%28b%29} - "{()}" "{%65mpty} - "{+$*}" "{+%73elf*} - "{+($)*}" "{+%24*} - - Note that in the final example, because the "+" was outside the brackets, it remained unescaped, whereas in the fourth example the "+" was escaped. - - -
-
- -
- - After pre-processing, the URI Template is filled out using data from the instance. - To allow the use of any object property (including the empty string), array index, or the instance value itself, the following rules are defined: - - + + The value of "href" is to be used as a URI Template, as defined in + RFC 6570. Template variables are + filled out using data from the identically named property of the instance. + For JSON property names that are not allowed by the URI Template + specification, or instance data other than a simple object, see the + "hrefVars" keyword. + + + After the template is filled out, the resulting value MUST be + resolved as a URI-reference against + the base URI of the instance. + + + This property is REQUIRED. + +
+
+ + The value of the "hrefVars" link description property MUST be an object. + The property names in the object SHOULD each be identical to a template + variable from the "href" value in the same link description. + The value of each property MUST be a + Relative JSON Pointer. + + + The URI Template specification restricts + the set of characters available for variable names. Property names in + JSON, however, can be any UTF-8 string. Additionally, JSON instances + may have nested array or object values, or may be nested within an + array or object, or may be of a type other than object. The "hrefVars" + keyword maps flat UTF-8 template variable names into any legal JSON + name at any point in the instance. + + + Template variables from "href" which do not appear as properties can be + considered to be present with a relative JSON pointer to the identically + named instance property at the current level. + +
+ + For example, if "href" has a single template variable "foo", + and "hrefVars" is absent or an empty object, it can be considered + to be present as follows: + + + + +
+ + To locate the instance data to fill out the variable named by + a property in "hrefVars", apply the relative JSON pointer to the instance + and use the resulting value. + +
+ + For example, if a schema is defined: + + + + +
+
+ + And if it is applied to an instance: + + + + + + It produces URI References of "/x/buzz" for the example1 link, and + "/stuff/buzz/99" for the example2 link. + +
+
+
+ + After applying "hrefVars", each variable in the URI Template in "href" + MUST be filled out using the resulting value (if it exists). + +
- For a given variable name in the URI Template, the value to use is determined as follows: + When any value referenced by the URI template is null, a boolean or a number, then it should first be converted into a string as follows: - If the variable name is "%73elf", then the instance value itself MUST be used. - If the variable name is "%65mpty", then the instances's empty-string ("") property MUST be used (if it exists). - If the instance is an array, and the variable name is a representation of a non-negative integer, then the value at the corresponding array index MUST be used (if it exists). - Otherwise, the variable name should be percent-decoded, and the corresponding object property MUST be used (if it exists). + null values SHOULD be replaced by the text "null" + boolean values SHOULD be replaced by their lower-case equivalents: "true" or "false" + numbers SHOULD be replaced with their original JSON representation. - -
- - When any value referenced by the URI template is null, a boolean or a number, then it should first be converted into a string as follows: - - null values SHOULD be replaced by the text "null" - boolean values SHOULD be replaced by their lower-case equivalents: "true" or "false" - numbers SHOULD be replaced with their original JSON representation. - - - - In some software environments the original JSON representation of a number will not be available (there is no way to tell the difference between 1.0 and 1), so any reasonable representation should be used. - Schema and API authors should bear this in mind, and use other types (such as string or boolean) if the exact representation is important. - -
-
- -
- - Sometimes, the appropriate values will not be available. - For example, the template might specify the use of object properties, but the instance is an array or a string. - - - If any of the values required for the template are not present in the JSON instance, then substitute values MAY be provided from another source (such as default values). - Otherwise, the link definition SHOULD be considered not to apply to the instance. + In some software environments the original JSON representation of a number will not be available (there is no way to tell the difference between 1.0 and 1), so any reasonable representation should be used. + Schema and API authors should bear this in mind, and use other types (such as string or boolean) if the exact representation is important.
+
+ + Sometimes, the appropriate values will not be available. + For example, the template might specify the use of object properties, but the instance is an array or a string. + + + + If any of the values required for the template are not present in the JSON instance, then substitute values MAY be provided from another source (such as default values). + Otherwise, the link definition SHOULD be considered not to apply to the instance. + +
@@ -857,6 +887,15 @@ GET /foo/ + + + Relative JSON Pointers (work in progress) + + + + + &rfc2046;