-
-
Notifications
You must be signed in to change notification settings - Fork 266
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
What is "$ref" and how does it work? #514
Comments
Another interesting concept is the one raised by @darrelmiller in OAI/OpenAPI-Specification#556 (comment), which classifies This would put make it somewhat like In the OpenAPI issue, there's some discussion of |
Regarding For the particular case of schema transformations, I think that there should be no visibility whatsoever inside the target schema for the purpose of transformation. It's essentially a function call. Schemas wishing to transform the target should use a different keyword for that purpose (e.g. |
As a note for readers here, if you want to discuss @erayd's points about schema transformations, that is going on in #515, so please join the discussion over there. I'd like to keep this issue focused on delegation vs inclusion (and in particular, see if anyone wants to advocate for inclusion as the sentiment so far is definitely trending towards delegation). |
Two additional arguments for delegation:
|
@epoberezkin thanks for the comment. I agree on the extensibility thing and #322. I'm still not necessarily sold on that (or |
Why is "lazy evaluation" associated to inclusion and not delegation? Couldn't you do either lazily? |
I also think lazily evaluating a schema is a stupid idea and an edge case that no really need or wants. I.e. just compile the schema first instead of lazily evaluating. Alternate Alternate Approach JSON schema should be completely independent of JSON Ref, except to possibly require that JSON Refs shall be resolved prior to evaluation. |
@sam-at-github Lazy evaluation makes implementation of recursive schemas easy. That's not an edge case; recursive schemas are common. Also, non-lazy evaluation necessarily implies the evaluation of lots of schema paths which may not be applicable to the document instance at hand. This is a significant waste of resources, particularly for complex schema definitions being run against a simple document. |
Resolve / compile it to a native reference.
That's why you do it once - compile. Just makes things so much simpler if JSON Ref is a independent standard and JSON Schema stacks on top just like TCP/IP etc. Last time I checked in with JSON Schema spec was over a year ago. They were arguing about $ref then! |
@sam-at-github I think we may have been talking about two different things then. But my point still stands - whether you're evaluating it or compiling it, the end result is you're still following lots of schema branches that may not make sense to follow, particularly in the case of dynamically generated schema which may not be able to be compiled in advance, or non-compiling implementations. Assuming compilation happens where practical in either case, what advantages does non-lazy handling of |
|
@sam-at-github
This is a JSON Specification. There are no native references. For some implementations that will make sense, for some it will not. But it's all irrelevant to the specification: We work with JSON, and with the underlying data model of JSON. That is all.
It's easy to statically pre-check that all
Putting complexity somewhere else doesn't make it go away. Also, even when it was a separate specification, you still needed to perform lazy evaluation in the context of schemas. Validating the meta-schema against itself requires this. You don't even have to look far. Even when JSON Reference was separate, this was always a concern that had to be dealt with (from draft-pbryan-zyp-json-ref-03, the last separate I-D for JSON Reference):
This has always been done by resolving in the context of the instance. Your schema references may cycle, but given a finite instance document you will always find an end to the recursion. You haven't made a single compelling statement for your position. You've just kind of dropped inhere and called us all stupid, and pointed out that it's easier to implement if it's less powerful. Which is no surprise, but does absolutely nothing to address the many real-world uses of cyclic references and lazy evaluation. I've worked with very large sets of schemas- there is no way that project could have possibly worked without lazily evaluated I've also inherited a project that forbid schema authors from using any recursive Removing the very long-standing (pre-draft-04) lazy evaluation nature of |
Didn't call you stupid ... I'm arguing for inclusion as it makes dealing with refs outside of JSON schema and refs possible. I still think lazy evaluation is stupid. As long as it's not mandatory.
No, but it can certainly make things less complex. |
You still haven't made a case for how your approach solves all use cases. You've just decided many of our use cases are unimportant. You've asserted that things that you care about are easier (probably true) but haven't offered any alternatives for what the community as a whole needs. I do not see this line of reasoning going anywhere. |
Not claiming to solve all use cases. Let's put it this way. You asked:
In response to this is this. |
@sam-at-github any serious proposal needs to solve all use cases. This thread seems done. |
OK Fine ... but just to summarize my position:
This is exactly the crux of my argument. Modularity, and Separation of concerns. An author of a JSON Schema implementation can re-use "JSON Schema Ref" or what ever de-refer lib they want. |
@sam-at-github if you prefer inclusion, then you should consider the fact that de-refernecing to native references is not something that's possible in all languages. This is a general specification that isn't language specific, and as such shouldn't create something that will be an issue for some languages. |
Some libraries allow for specifying to use inclusion, in the case of a few, simple, known schemas, where it will be faster and you know it will be 100% resolveable. I don't see any issue with this being something implementors can allow as non default behaviour. It's useful in my use case where I might have MANY JSON instances to validate, and I don't want an HTTP call for every validation call, and I know they fully resolve (because the schemas are mine). Could we specify that implementors MAY do this, as long as they document the potential issues with doing so, and it MUST NOT be default behaviour? |
@Relequestual depending on exactly where we go with vocabularies and If an implementation realizes that it can produce the correct behavior through inlining, that's fine. The point of this decision is that we've had proposals come up that will work with one behavior but not the other, so we need to pick which behavior we endorse. This is similar to "flattening" |
@Relequestual actually, IF the
As in my last comment, I think this is more of a tutorial guide sort of thing rather than an "implementations MAY do this" sort of thing. Informally, implementations can do any transformations that they want as long as the outcome is computably the same. |
It seems contradictory that JSON Schema can be language agnostic while considering the languages in which it's implemented. |
Just to put in my vote, I am also in favor of inclusion, however I understand the necessity to define how Edit: I don't know what I'm talking about here. Keep reading. |
I know some languages don't support a native ref type, but struggle to see the barrier still. Python comes to mind - sort of, but I'm not convinced there is not a simple work around in any language actually supporting a de-serializing a JSON string into some native object type in the first place. By "native ref" I'm not saying literal built-in ref type. For example consider:
Now
"all languages must be supported" gets raised a bit. But what are the entry level requirements of a language to support JSON and JSON Schema exactly? There is two requirements for the above:
The above sort of hinges on the second requirement because it is literally impossible to de-ref a document with loops if it doesn't. But I don't of a language that supports requirement 1. and not 2. |
I've re-read this, skipping over my own comments, and eached the exact same conclusion. I feel, we should specify:
As I said, this allows people with small schemas, that are fully known to them, to save time when doing batch validation of JSON. I would suggest no further comments, and a general thumbs up or down on this comment, and move forward to work on phrasing for making this clear. |
=O As a rare event, I'm going to self assign and plan to make a Pull Request to solve this issue (unless anyone objects). |
I would also suggest to preserve the decorating properties
But isn't the sense of a "decorator" that if you reuse references to decorate them differently. http://json-schema.org/latest/json-schema-core.html#rfc.section.8 says:
could this become something like |
@sebilasse see #523 |
Updated phrasing to align with current wording. Resolves json-schema-org#514 and json-schema-org#523
The question of whether
$ref
behaves like inclusion or delegation has come up several times.$ref
object can be replaced with its target (in a lazily evaluated process).$ref
is processed against the current instance location, and the "results" (boolean assertion outcome and optionally the collected annotations) of$ref
are simply the results of the target schema.Inclusion
In its original form,
$ref
in draft-03 and in the separate JSON Reference I-D is explicitly defined as inclusion. Implementations MAY choose to replace the reference object with its target.There are some subtleties involved in replacement. You definitely need to adjust the
$id
when you do the replacement or else base URIs get messed up. Tools such as JSON Schema Ref Parser that "dereference"$ref
s do just that.You also need to deal with
$schema
, which (when the target is in a different schema document) can be different in the source and target schemas. The obvious solution is to set$schema
in the copied-over replacement, however @epoberezkin has observed that this conflicts with how non-schema instances work with meta-schemas.We cannot make any assumption about instance documents. A key feature of JSON Schema is that it works with plain old
application/json
documents. There is no way for a plain JSON document to change what schema is used to process it. Changing$schema
in the middle of validating a schema against its meta-schema introduces behavior that is not possible with other instance documents.Delegation
With delegation, these problems do not exist. Each subschema is evaluated in the context of its containing schema document, regardless of whether processing reached it from elsewhere in that same document or from a
$ref
in a separate document. Since the processing is done per-document, each document can use a different$schema
.Results are returned in the form of a single overall boolean assertion outcome (so it doesn't matter to the referencing document what the assertions were or how they were processed) and optionally a set of annotation data (which is a set of name-value pairs of some sort).
The only subtlety is in combining data for the same annotation that appeared in both a local document subschema and a remote
$ref
'd document subschema. However, this is easily addressed: once the annotation data is "returned" across the$ref
, it is combined with other annotation data by the rules of the schema containing the$ref
. This keeps all processing consistent within each schema document, such that the rules can change independently on each side (for instance if they are upgraded to a new draft at different times).Another nice property of delegation (also from @epoberezkin) is that
$ref
can become a "normal" keyword that has assertion and/or annotation results that are combined with adjacent schema keywords just like everything else.Alternate approaches
NOTE: This section is about showing that it is possible to handle the limitations in other ways. Neither of these approaches is a serious proposal for recommendation!
An alternate approach to inclusion
The one use case that is not well-handled by delegation is that of packing multiple schema documents into a single distribution unit (file, resource, whatever). There's some debate as to how valid or important this use case is, but it does come up. This is only done by replacing specific non-cyclic
$ref
s, and does not involve trying to "dereference" all$ref
s in the system.For those who really want to do this, it occurs to me that there is another way to handle it:
data:
URIs:Let's say that this is our reference target:
Here we see how it can be inlined into a draft-04 schema using a
data:
URI:Of course this looks horrible and would never be suitable for human consumption. But it would work.
I'm not seriously advocating this as a recommendation, but it does illustrate that the problem is solvable in the delegation model if you are willing to make some tradeoffs.
An alternate way to change processing rules
The limitation of replacement is that changing
$schema
in the middle of a file make validating schemas against meta-schemas different from validating other instances against schemas, as we cannot specify a schema change mechanism for plainapplication/json
instance documents.However, we could extend the instance-to-schema linking process to allow associating an instance JSON Pointer with each linked schema. For schemas-as-instances, this would be equivalent to setting
$schema
at the location identified by the JSON Pointer.This would have to be done as a link attribute/media type parameter thing of some sort. Like the
data:
URI solution, this gets ugly pretty quickly, and makes it more likely to hit HTTP header length limitations.It's also arguably a lot harder to hold in one's head than simply saying that
$schema
is scoped to a JSON document rather than an individual schema object. While the data URI alternative given above is ugly, the ugliness comes from a separate standard, and is purely opt-in. Schema authors have to choose to use those URIs, and implementations would choose whether to support data URIs or not.This approach of targeting different schemas at different portions of the instance would place a significant burden on all implementations.
Conclusions
Based on this write-up, I am inclined to formalize
$ref
as delegation. It is a more flexible model, and the technique for working around its limitations correctly restricts the burden of implementation to only those who choose to use or support it. Inclusion is less flexible, and the workaround (if we did anything about it at all) is burdensome to all implementations.The text was updated successfully, but these errors were encountered: