santhosh-tekuri/jsonschema

`Compiler.AddResource` for adding pre-compiled schemas

Opened this issue · 5 comments

Is it possible to perform something equivalent to Compiler.AddResource but for a pre-compiled schema? In my application, I have a number of schemas already compiled with jsonschema.MustCompileString for other uses. It would be good to be able to reuse these in another schema with $ref references pointing at them without needing to re-add and recompile their schemas from strings.

I'm current implementing the solutions described at #89 (comment), but each operates on the schema's raw characters.

Adding the following to compiler.go allows my cross-referenced schema to compile, though I don't know if it's sufficiently creating a resource internally:

// AddResourceSchema adds a pre-compiled *Schema to the compiler.
func (c *Compiler) AddResourceSchema(url string, sch *Schema) {
	c.resources[url] = &resource{
		url:    url,
		draft:  sch.Draft,
		schema: sch,
	}
}

This function probably wants to be checking for sch == nil and return an error.

you can not do that. But you can do the following.

add all your resources into single compiler and then compile each one sequentially; for example:

resources = map[string]string { ....}
c := jsonschema.NewCompiler()
for url, content := range resources {
    if err:=c.AddResource(url, strings.NewReader(content)); err!=nil {
        panic(err)
    }
}

schemas := map[string]*jsonschema.Schema{}
for url := range resources {
    sch, err := c.Compile(url)
    if err!=nil {
        panic(err)
    }
    schemas[url] = sch
}

there is no restrictions that all resources should be added before compile if they are independent. you can compile and then add new resources which has $ref to previously compiled and then compile the newly added resources.

as long as you use same Compiler instance, any new resources added can reference to previously compiled schemas

c := jsonschema.NewCompiler()

if err := c.AddResource("schema1.json", strings.newReader("{}"); err!=nil {
    panic(err)
}

sch1, err := c.Compile("schema1.json")
if err!=nil {
    panic(err)
}

sch1_again, err := c.Compile("schema1.json") // compiling same schema simply returns previously compiled schema
if err!=nil {
    panic(err)
}
// now sch1 and sch1_again both point to same schema struct

// now you can add another resource which has $ref to schema1.json
if err := c.AddResource("schema2.json", strings.newReader(`{"$ref": "schema1.json"}`); err!=nil {
    panic(err)
}

sch2, err := c.Compile("schema2.json")
if err!=nil {
    panic(err)
}

Thanks @santhosh-tekuri, I really appreciate it - the first is effectively what I've implemented at the moment but the second is an interesting idea - I didn't realise the internals of "re-compiling" the same schema.

In my case, I was endeavouring to have separation between compilation of schemas where possible so each is a clean environment, and then referencing the the pre-compiled schemas in separate code. Also, I'd aimed to do as much as possible at the top level in a module, avoiding the need for init() if possible and the boilerplate for checking err and panicking.

Is something like AddResourceSchema feasible? As I say, I've only tested the compilation process with the example above but the $ref entries do appear to resolve.

it is not that straight forward for what you are asking. let us say:

schema1.json is:

{
    "$def": {
        "a": ...,
        "b": ...,
    }
}

here when you compile schema1.json then only schema1.json is compiled.
schema1.json#/$def/a and schema1.json#/$def/a are not compiled

so if you add schema2.json:

{
    "$ref": "schema1.json#/$def/a"
}

now compiling schema2.json works correctly if you have used same Compiler

as you suggested, if you create new Compiler and somehow add schema1 compiled schema to it, still it won't resolve the $ref in schema2.json. I hope you got my point.

Thanks @santhosh-tekuri, I really appreciate it - the first is effectively what I've implemented at the moment but the second is an interesting idea - I didn't realise the internals of "re-compiling" the same schema.

it does not recompile, before compiling we check if it is already precompiled, if so we return the precompiled schema again.

see https://github.com/santhosh-tekuri/jsonschema/blob/master/compiler.go#L284-L289