Skip to content

Commit 0227cea

Browse files
committed
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 json-schema-org#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 json-schema-org#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.
1 parent 356af3b commit 0227cea

File tree

2 files changed

+153
-134
lines changed

2 files changed

+153
-134
lines changed

hyper-schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@
8686
"description": "a URI template, as defined by RFC 6570, with the addition of the $, ( and ) characters for pre-processing",
8787
"type": "string"
8888
},
89+
"hrefVars": {
90+
"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",
91+
"type": "object",
92+
"additionalProperties": {"type": "string"}
93+
},
8994
"rel": {
9095
"description": "relation to the target resource of the link",
9196
"type": "string"

jsonschema-hyperschema.xml

Lines changed: 148 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
It is therefore the first URI Reference resolved, regardless of which order it was found in.
188188
</t>
189189
<t>
190-
The URI is computed from the provided URI template using the same process described for the <xref target="href">"href"</xref> property of a Link Description Object.
190+
The URI is computed from the provided URI template using the same process described for the <xref target="href">"href"</xref> and <xref target="href-vars">"hrefVars"</xref> properties of a Link Description Object.
191191
</t>
192192
<figure>
193193
<preamble>An example of a JSON schema using "base":</preamble>
@@ -354,150 +354,155 @@
354354
"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.
355355
</t>
356356

357-
<section title="href" anchor="href">
358-
<t>
359-
The value of the "href" link description property is a template used to determine the target URI of the related resource.
360-
The value of the instance property MUST be resolved as a <xref target="RFC3986">URI-reference</xref> against the base URI of the instance.
361-
</t>
362-
<t>
363-
This property is REQUIRED.
364-
</t>
365-
366-
<section title="URI Templating">
357+
<section title="URI Templating">
358+
<section title="href" anchor="href">
367359
<t>
368-
The value of "href" is to be used as a URI Template, as defined in <xref target="RFC6570">RFC 6570</xref>. However, some special considerations apply:
360+
The value of the "href" link description property is a template
361+
used to determine the target URI of the related resource.
369362
</t>
370-
371-
<section title="Pre-processing">
372-
<t>
373-
<cref>This pre-processing section is subject to significant change in upcoming drafts.</cref>
374-
</t>
375-
<t>
376-
The <xref target="RFC6570">URI Template specification</xref> restricts the set of characters available for variable names.
377-
Property names in JSON, however, can be any UTF-8 string.
378-
</t>
379-
380-
<t>
381-
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:
382-
</t>
383-
384-
<section title="Bracket escaping">
385-
<t>
386-
The purpose of this step is to allow the use of brackets to percent-encode variable names inside curly brackets.
387-
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 "))".
388-
Since the empty string is not a valid variable name in RFC 6570, an empty pair of brackets is replaced with "%65mpty".
389-
</t>
390-
391-
<t>
392-
The rules are as follows:
393-
</t>
394-
395-
<t>
396-
Find the largest possible sections of the text such that:
397-
<list>
398-
<t>do not contain an odd number of close-rounded-bracket characters ")" in sequence in that section of the text</t>
399-
<t>are surrounded by a pair of rounded brackets: ( ), where</t>
400-
<t>the surrounding rounded brackets are themselves contained within a pair of curly brackets: { }</t>
401-
</list>
402-
</t>
403-
<t>
404-
Each of these sections of the text (including the surrounding rounded brackets) MUST be replaced, according to the following rules:
405-
<list>
406-
<t>If the brackets contained no text (the empty string), then they are replaced with "%65mpty" (which is "empty" with a percent-encoded "e")</t>
407-
<t>Otherwise, the enclosing brackets are removed, and the inner text used after the following modifications
408-
<list>
409-
<t>all pairs of close-brackets "))" are replaced with a single close bracket</t>
410-
<t>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 "!")</t>
411-
</list>
412-
</t>
413-
</list>
414-
</t>
415-
</section>
416-
417-
<section title="Replacing $">
418-
<t>
419-
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").
420-
</t>
421-
<t>
422-
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".
423-
</t>
424-
</section>
425-
426-
<section title="Choice of special-case values">
427-
<t>
428-
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.
429-
</t>
430-
</section>
431-
432-
<section title="Examples">
433-
<texttable>
434-
<preamble>For example, here are some possible values for "href", followed by the results after pre-processing:</preamble>
435-
<ttcol>Input</ttcol>
436-
<ttcol>Output</ttcol>
437-
<c>"no change"</c> <c>"no change"</c>
438-
<c>"(no change)"</c> <c>"(no change)"</c>
439-
<c>"{(escape space)}"</c> <c>"{escape%20space}"</c>
440-
<c>"{(escape+plus)}"</c> <c>"{escape%2Bplus}"</c>
441-
<c>"{(escape*asterisk)}"</c> <c>"{escape%2Aasterisk}"</c>
442-
<c>"{(escape(bracket)}"</c> <c>"{escape%28bracket}"</c>
443-
<c>"{(escape))bracket)}"</c> <c>"{escape%29bracket}"</c>
444-
<c>"{(a))b)}"</c> <c>"{a%29b}</c>
445-
<c>"{(a (b)))}"</c> <c>"{a%20%28b%29}</c>
446-
<c>"{()}"</c> <c>"{%65mpty}</c>
447-
<c>"{+$*}"</c> <c>"{+%73elf*}</c>
448-
<c>"{+($)*}"</c> <c>"{+%24*}</c>
449-
<postamble>
450-
Note that in the final example, because the "+" was outside the brackets, it remained unescaped, whereas in the fourth example the "+" was escaped.
451-
</postamble>
452-
</texttable>
453-
</section>
454-
</section>
455-
456-
<section title="Values for substitution">
457-
<t>
458-
After pre-processing, the URI Template is filled out using data from the instance.
459-
To allow the use of any object property (including the empty string), array index, or the instance value itself, the following rules are defined:
460-
</t>
461-
363+
<t>
364+
The value of "href" is to be used as a URI Template, as defined in
365+
<xref target="RFC6570">RFC 6570</xref>. Template variables are
366+
filled out using data from the identically named property of the instance.
367+
For JSON property names that are not allowed by the URI Template
368+
specification, or instance data other than a simple object, see the
369+
<xref target="href-vars">"hrefVars"</xref> keyword.
370+
</t>
371+
<t>
372+
After the template is filled out, the resulting value MUST be
373+
resolved as a <xref target="RFC3986">URI-reference</xref> against
374+
the base URI of the instance.
375+
</t>
376+
<t>
377+
This property is REQUIRED.
378+
</t>
379+
</section>
380+
<section title="hrefVars" anchor="href-vars">
381+
<t>
382+
The value of the "hrefVars" link description property MUST be an object.
383+
The property names in the object SHOULD each be identical to a template
384+
variable from the "href" value in the same link description.
385+
The value of each property MUST be a
386+
<xref target="relative-json-pointer">Relative JSON Pointer</xref>.
387+
</t>
388+
<t>
389+
The <xref target="RFC6570">URI Template specification</xref> restricts
390+
the set of characters available for variable names. Property names in
391+
JSON, however, can be any UTF-8 string. Additionally, JSON instances
392+
may have nested array or object values, or may be nested within an
393+
array or object, or may be of a type other than object. The "hrefVars"
394+
keyword maps flat UTF-8 template variable names into any legal JSON
395+
name at any point in the instance.
396+
</t>
397+
<t>
398+
Template variables from "href" which do not appear as properties can be
399+
considered to be present with a relative JSON pointer to the identically
400+
named instance property at the current level.
401+
</t>
402+
<figure>
403+
<preamble>
404+
For example, if "href" has a single template variable "foo",
405+
and "hrefVars" is absent or an empty object, it can be considered
406+
to be present as follows:
407+
</preamble>
408+
<artwork>
409+
<![CDATA[{
410+
"href": "/foos/{foo}",
411+
"hrefVars": {
412+
"foo": "0/foo"
413+
}
414+
}]]>
415+
</artwork>
416+
</figure>
417+
<t>
418+
To locate the instance data to fill out the variable named by
419+
a property in "hrefVars", apply the relative JSON pointer to the instance
420+
and use the resulting value.
421+
</t>
422+
<figure>
423+
<preamble>
424+
For example, if a schema is defined:
425+
</preamble>
426+
<artwork>
427+
<![CDATA[{
428+
"type": "object",
429+
"properties": {
430+
"": {"enum": ["x", "y"]}
431+
"foos": {
432+
"type": "array",
433+
"items": {"type": "number"},
434+
},
435+
"bar": {
436+
"type": "string"
437+
"links": [{
438+
"rel": "example1",
439+
"href": "/{emptyKey}/{bar}",
440+
"hrefVars": {
441+
"emptyKey": "1/",
442+
"bar": "0"
443+
}
444+
}]
445+
},
446+
},
447+
"links": [{
448+
"rel": "example2",
449+
"href": "/stuff/{bar}/{firstFoo}",
450+
"hrefVars": {
451+
"firstFoo": "0/foos/0"
452+
}
453+
}]
454+
}]]>
455+
</artwork>
456+
</figure>
457+
<figure>
458+
<preamble>
459+
And if it is applied to an instance:
460+
</preamble>
461+
<artwork>
462+
<![CDATA[{
463+
"foos": [99, 98, 97],
464+
"": "x",
465+
"bar": "buzz"
466+
}]]>
467+
</artwork>
468+
<postamble>
469+
It produces URI References of "/x/buzz" for the example1 link, and
470+
"/stuff/buzz/99" for the example2 link.
471+
</postamble>
472+
</figure>
473+
</section>
474+
<section title="Values for substitution">
475+
<t>
476+
After applying "hrefVars", each variable in the URI Template in "href"
477+
MUST be filled out using the resulting value (if it exists).
478+
</t>
479+
<section title="Converting to strings">
462480
<t>
463-
For a given variable name in the URI Template, the value to use is determined as follows:
481+
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:
464482
<list>
465-
<t>If the variable name is "%73elf", then the instance value itself MUST be used.</t>
466-
<t>If the variable name is "%65mpty", then the instances's empty-string ("") property MUST be used (if it exists).</t>
467-
<t>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).</t>
468-
<t>Otherwise, the variable name should be percent-decoded, and the corresponding object property MUST be used (if it exists).</t>
483+
<t>null values SHOULD be replaced by the text "null"</t>
484+
<t>boolean values SHOULD be replaced by their lower-case equivalents: "true" or "false"</t>
485+
<t>numbers SHOULD be replaced with their original JSON representation.</t>
469486
</list>
470487
</t>
471-
472-
<section title="Converting to strings">
473-
<t>
474-
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:
475-
<list>
476-
<t>null values SHOULD be replaced by the text "null"</t>
477-
<t>boolean values SHOULD be replaced by their lower-case equivalents: "true" or "false"</t>
478-
<t>numbers SHOULD be replaced with their original JSON representation.</t>
479-
</list>
480-
</t>
481-
<t>
482-
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.
483-
Schema and API authors should bear this in mind, and use other types (such as string or boolean) if the exact representation is important.
484-
</t>
485-
</section>
486-
</section>
487-
488-
<section title="Missing values">
489-
<t>
490-
Sometimes, the appropriate values will not be available.
491-
For example, the template might specify the use of object properties, but the instance is an array or a string.
492-
</t>
493-
494488
<t>
495-
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).
496-
Otherwise, the link definition SHOULD be considered not to apply to the instance.
489+
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.
490+
Schema and API authors should bear this in mind, and use other types (such as string or boolean) if the exact representation is important.
497491
</t>
498492
</section>
499493
</section>
500494

495+
<section title="Missing values">
496+
<t>
497+
Sometimes, the appropriate values will not be available.
498+
For example, the template might specify the use of object properties, but the instance is an array or a string.
499+
</t>
500+
501+
<t>
502+
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).
503+
Otherwise, the link definition SHOULD be considered not to apply to the instance.
504+
</t>
505+
</section>
501506
</section>
502507

503508
<section title="rel">
@@ -854,6 +859,15 @@ GET /foo/
854859
</front>
855860
<seriesInfo name="Internet-Draft" value="draft-wright-json-schema-00" />
856861
</reference>
862+
<reference anchor="relative-json-pointer"
863+
target="http://tools.ietf.org/html/draft-luff-relative-json-pointer-00">
864+
<front>
865+
<title>Relative JSON Pointers (work in progress)</title>
866+
<author initials="G." surname="Luff"></author>
867+
<date year="2013" month="July"/>
868+
</front>
869+
<seriesInfo name="Internet-Draft" value="draft-luff-relative-json-pointer-00" />
870+
</reference>
857871
</references>
858872
<references title="Informative References">
859873
&rfc2046;

0 commit comments

Comments
 (0)