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

Compiler.AddResource for adding pre-compiled schemas #150

Open
davidjb opened this issue Feb 22, 2024 · 5 comments
Open

Compiler.AddResource for adding pre-compiled schemas #150

davidjb opened this issue Feb 22, 2024 · 5 comments

Comments

@davidjb
Copy link
Contributor

davidjb commented Feb 22, 2024

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.

@santhosh-tekuri
Copy link
Owner

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.

@santhosh-tekuri
Copy link
Owner

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)
}

@davidjb
Copy link
Contributor Author

davidjb commented Feb 22, 2024

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.

@santhosh-tekuri
Copy link
Owner

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.

@santhosh-tekuri
Copy link
Owner

santhosh-tekuri commented Feb 22, 2024

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

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