Description
NOTE: I'm splitting this out from #52 in order to get more attention on this specific point and the related pull request #129 . I'll probably split other stuff out from #52 and then close it. In hindsight, it was not a good idea to dump everything together there.
URI Template pre-processing is confusing, significantly complicates implementation, and does not address all limitations in the current rules for filling out templates with instance data.
The current approach tries to circumvent the URI Template spec's variable name limitations within the actual string used to express the URI Template. A simpler yet more powerful approach is to make the URI Template strings normal URI Templates, and use a mapping object to translate legal URI Template names to expressions that can identify any part of the instance. The proposed keyword for the map is hrefVars
.
Relative JSON Pointers come closest to meeting the necessary requirements: starting from any point in the instance (specifically, the point from which the LDO including the template is defined), they can identify nearly any other point in the instance (see #115 for limitations with respect to arrays). See #126 for a discussion of whether Relative JSON Pointers should be a separate I-D or should start as part of JSON Schema. Either would work fine for this proposal.
To preserve the current behavior when preprocessing is not needed, if a template variable "x" does not appear in hrefVars
, it may be considered present with a relative pointer of "0/x".
Examples based on current pre-processing features
Here is a subset of the table of examples for pre-processing, followed by a schema showing links using these variable names.
Input | Output |
---|---|
"{(escape space)}" | "{escape%20space}" |
"{(a (b)))}" | "{a%20%28b%29} |
"{()}" | "{%65mpty} |
"{+$*}" | "{+%73elf*} |
{
"links": [
{
"rel": "foo",
"href": "/{(escape space)}/{(a (b))}/{()}"
},
{
"rel": "bar",
"href": "/{+$*}"
}
]
}
Note that making use of $ in the "self" case with the "bar" link requires using the URI Template "+" operator to allow percent-encoded sequences, as the $ is replaced by a percent-encoded sequence during pre-processing. This is particularly confusing since, without pre-processing, the "+" would make the "$" a literal dollar sign that did not need to be percent-encoded.
Given this instance:
{
"escape space": "x",
"a (b)": "y",
"": "z"
}
the "foo" link would expand to "/x/y/z"
Given an instance of [1, 2, 3, 4]
the "bar" link would expand to "/1/2/3/4" (the "*" suffix is a URI Template "explode" operator which interprets each list element as a path component).
Here is what a mapping approach might look like, which would produce the same results when applied to the same two example instances.
{
"links": [
{
"rel": "foo",
"href": "/{space}/{complicated}/{empty}",
"hrefVars": {
"space": "0/escape space",
"complicated": "0/a (b)",
"empty": "0/"
}
},
{
"rel": "bar",
"href": "/{self*}",
"hrefVars": {
"self": "0"
}
}
]
}
The hrefVars
keyword defines the map for href
. Note that we no longer need the "+" operator at all. URI Template operators such as the "*" suffix are not part of the template variable name and therefore do not appear in the map.
Examples that are impossible with current pre-processing
Given this schema:
{
"type": "object",
"properties": {
"foo": {"type": "string"},
"bar": {
"type": "array",
"items": {
"type": "object",
"properties": {
"x": {"type": "number"},
"y": {
"type": "object",
"properties": {"z": {"type": "boolean"}}
}
},
"links": [{
"rel": "item",
"href": "/{foo}/{x}/{z}",
"hrefVars": {
"foo": "1/foo",
"z": "0/y/z"
}
}]
}
}
}
}
Applying it to this instance:
{
"foo": "oof",
"bar": [{"x": 42, "y": {"z": true}}, {"x": 0, "y": {"z": false}}]
}
would produce an item link of "/oof/42/true" for the first array element, and "/oof/0/false" for the second. Note that since "x" was not in hrefVars
it is treated as if mapped to "0/x", which produces the same behavior as the current specification for non-preprocessed variables.
This demonstrates the support for referencing data in enclosing and nested instances.
In conclusion
Mapping is much easier to implement and much easier to read than pre-processing. As proposed, it offers a superset of the current functionality. The main implementation concern would be introducing Relative JSON Pointers, but they are required for several proposals currently under consideration.
In my experience on past projects, schema authors found variable mapping easy to work with. It was not a significant source of confusion or bugs, either in schemas or in the code that processed the schemas using this feature.