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

reference another schema #37

Open
acthp opened this issue May 14, 2015 · 6 comments
Open

reference another schema #37

acthp opened this issue May 14, 2015 · 6 comments

Comments

@acthp
Copy link

acthp commented May 14, 2015

Is there a way to reference another schema? Doing

var foo = schema(...);
var bar = schema({"foo": foo});

bar.toJSON()

gives a flattened schema: foo is incorporated into bar, rather than bar having a reference to foo. Is there a way, instead, to generate a reference to foo? The docs only cover references to self.

@molnarg
Copy link
Owner

molnarg commented May 14, 2015

No, there is no way to do that if you mean proper JSON Schema references :( It would be nice to have something like that.

I don't plan to put resources into this, but pull requests are welcome.

@acthp
Copy link
Author

acthp commented May 14, 2015

Yes, at least I think JSON Schema references would do. I want this for generating documentation. E.g. if there is a schema Person that is used in three other schemas, I want the docs to just say Person three times, rather than rendering the definition of Person three times.

It looks like there's already some code for generating JSON Schema $ref properties, but I'm unable to follow it. Looks like the references are always randomly generated in Schema.prototype.toJSON, rather than using the "#/..." syntax. The Schema extension is doing something with $ref, but I can't tell what. Also can't tell when the Schema extension is invoked. BaseSchema walks over the list of extensions & picks a matching one somehow?

If you can give me any hints, I could make a pull request.

@molnarg
Copy link
Owner

molnarg commented May 14, 2015

js-schema's referencing support was designed for references inside a single expression (e.g. the referencing self, referencing a schema object twice, etc.), but maybe it could be extended to support proper external referencing.

The relevant code is this: https://github.com/molnarg/js-schema/blob/master/lib/BaseSchema.js#L23 The session variable stores state when serializing a single expression. This state includes an array of already serialized sub-expressions, so that if they came up again, then we can assign an id to them (randomly) and reference them.

As a workaround, you could prefill this session object with an external reference like this (after executing the toJSON function, the session object is reset, so this is for one invocation only):

> var a = schema({x:Number})
> var b = schema({q:a, p:a})
> schema.Schema.session.serialized = { objects: [a.unwrap()], jsons: [{'id':'id_of_a'}], ids: [] }
> b.toJSON()
{ type: 'object',
  properties:
   { q: { '$ref': 'id_of_a', required: true },
     p: { '$ref': 'id_of_a', required: true } } }

When it comes to properly implementing it, I would probably add a plus argument to the schema() function to denote the id (but this conflicts with the current way of defining a description, so it would have to be differentiated somehow). If the resulting schema is serialized in itself, its toJSON function would return a JSON Schema containing it's id and the proper JSON Schema, but when it is serialized as part of a larger schema, its toJSON would only return something like {"$ref": "id_of_object"}.

@acthp
Copy link
Author

acthp commented May 15, 2015

I believe what I need is output like section 7.2.3 here:

http://json-schema.org/latest/json-schema-core.html

where the $ref refers to the 'definitions' in the current scope. So, seems like I could get close by having a mechanism to pass in 'definitions' to a schema, generate ids based on their keys, and call toJSON on them first, so any later references would be serialized as a ref. Perhaps like

var a = schema({x:Number});
var b = schema({a_id: a}, "schema b", {q:a, p:a})

@acthp
Copy link
Author

acthp commented May 16, 2015

I have this working as per my proposal, above, with the exception that the doc string is always first, definitions second, schema last, in the args to schema(). Doc string is required if passing definitions, to disambiguate the args.

Diff here:

acthp@e90bdf1?diff=unified

I tested the generated schema in a different tool (is-my-json-valid), and it appears to work, though I'm fuzzy on json schema refs & pointers.

Let me know if you want a pull request, or changes (unit test, docs, other).

@acthp
Copy link
Author

acthp commented May 16, 2015

A couple issues that have come up:

  • makeReference in BaseSchema.prototype.toJSON prevents this from working on types other than Object and Array. I'm not sure what this flag is for. Unit tests pass w/o it. Is there a case where making a reference is inappropriate?
  • It's up to subclasses to check for $ref and return, which is currently not done consistently across all the subclasses. regexp for example does not check it, resulting in json that includes both $ref and the subschema definition. Not sure if this mirrors makeReference (no need to check $ref if it can't be set), or if there's some other reason to not check for $ref. I could add the check to all subclasses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants