santhosh-tekuri/jsonschema

Cannot override jsonschema.Formats

alexberdnik opened this issue · 4 comments

In some cases there is a need to override some built-in format functions. Good example is uri-reference function, sometimes I want to check that target file actually exists.
It is possible to assign my custom format function implementation to jsonschema.Formats["uri-reference"], but it won't work properly:

  • for json files defined by draft schemas (e.g. precompiled) validator uses default implementation.
  • for json files defined by custom schemas validator behaves as expected.
    The root cause seems to be that Schema pre-caches format function and uses it instead of fetching the function from Formats map.

for user defined schemas you can override format definitions.

draft schemas are used internally to avoid type checks during compilation and are preloaded in init. you cannot override them.

I don't understand why you want to override them in draft compilation.

Here the example:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/example1",
  "type": "object",
  "properties": {
     "prop1": {
        "$ref": "https://example.com/example2"
     },
    "target": {
       "type": "string",
       "format": "uri-reference"
    }
  }
}

This file is defined by draft schema. I want to validate "/properties/prop1/$ref" according to my custom uri-reference function as $ref is defined as uri-reference (e.g. to check that it actually exists). It doesn't work.
However, for another json file:

{
  "$schema": "https://example.com/example1",
  "target": "https://example.com/example2"
}

"/target" is actually checked by custom "uri-reference" function.

you can manually validate with draft schema as follows:

func Example_Issue() {
	resp, err := http.Get("https://json-schema.org/draft/2020-12/schema")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	jsonschema.Formats["uri-reference"] = func(v interface{}) bool {
		s, ok := v.(string)
		if !ok {
			return true
		}
		return s != "https://example.com/example2"
	}

	c := jsonschema.NewCompiler()
	c.AssertFormat = true
	err = c.AddResource("draft2020.json", resp.Body)
	if err != nil {
		panic(err)
	}
	sch, err := c.Compile("draft2020.json")
	if err != nil {
		panic(err)
	}

	s := `{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/example1",
  "type": "object",
  "properties": {
     "prop1": {
        "$ref": "https://example.com/example2"
     },
    "target": {
       "type": "string",
       "format": "uri-reference"
    }
  }
}`
	var doc interface{}
	err = json.Unmarshal([]byte(s), &doc)
	if err != nil {
		panic(err)
	}
	err = sch.Validate(doc)
	fmt.Println(err)
	// Output:
	// jsonschema: '/properties/prop1/$ref' does not validate with https://json-schema.org/draft/2020-12/schema#/allOf/1/$ref/properties/properties/additionalProperties/$dynamicRef/allOf/0/$ref/properties/$ref/$ref/format: 'https://example.com/example2' is not valid 'uri-reference'
}

This might work. Thank you!