santhosh-tekuri/jsonschema

unexpected validation error which LSP is fine with

Closed this issue · 10 comments

I have the following schema.json

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "patternProperties": {
    "(^{{[ ]+\\.[A-Za-z][A-Za-z0-9_-]+[ ]+}}$)|(^[A-Za-z][A-Za-z0-9_-]+$)": {
      "properties": {
        "Env": {
          "patternProperties": {
            "^[A-Za-z][A-Za-z0-9_-]+$": {
              "type": [
                "boolean",
                "number",
                "string"
              ]
            }
          },
          "type": "object"
        }
      },
      "patternProperties": {
        "(^{{[ ]+\\.[A-Za-z][A-Za-z0-9_-]+[ ]+}}$)|(^[A-Za-z][A-Za-z0-9_-]+$)": {
          "properties": {
            "Default": {
              "type": "string",
              "enum": [
                "ALLOW",
                "DENY"
              ]
            }
          },
          "patternProperties": {
            "(^{{[ ]+\\.[A-Za-z][A-Za-z0-9_-]+[ ]+}}$)|(^[A-Za-z][A-Za-z0-9_-]+$)": {
              "type": "string",
              "title": "Policy"
            }
          },
          "additionalProperties": false,
          "type": [
            "object",
            "null"
          ]
        }
      },
      "additionalProperties": false,
      "type": [
        "object",
        "null"
      ]
    }
  },
  "type": "object",
  "title": "Resourcename"
}

And this test.yaml file

# yaml-language-server: $schema=./schema.json

resourcename1:
    Env:
        "keyname0": "something"
        "keyname1": 42
    aname0:
      Default: ALLOW
      pname0: hello

I am using version 5.3.1 and run this code

func schemaValidationTest() {
	var v any

	if b, e := ioutil.ReadFile("/tmp/test.yaml"); e != nil {
		log.Fatal(e)
	} else if sch, e := jsonschema.Compile("/tmp/schema.json"); e != nil {
		log.Fatal(e)
	} else if e = yaml.Unmarshal(b, &v); e != nil {
		log.Fatal(e)
	} else if e = sch.Validate(v); e != nil {
		switch et := e.(type) {
		default:
			fmt.Println("not a model missing error")
		case *jsonschema.ValidationError:

			log.Fatalf("%#v", et)
		}
	}
}

I am getting this error:


[I#] [S#] doesn't validate with file:///tmp/schema.json#
   [I#/resourcename1/Env/keyname1] [S#/patternProperties/%28%5E%7B%7B%5B%20%5D+%5C.%5BA-Za-z%5D%5BA-Za-z0-9_-%5D+%5B%20%5D+%7D%7D$%29%7C%28%5E%5BA-Za-z%5D%5BA-Za-z0-9_-%5D+$%29/patternProperties/%28%5E%7B%7B%5B%20%5D+%5C.%5BA-Za-z%5D%5BA-Za-z0-9_-%5D+%5B%20%5D+%7D%7D$%29%7C%28%5E%5BA-Za-z%5D%5BA-Za-z0-9_-%5D+$%29/patternProperties/%28%5E%7B%7B%5B%20%5D+%5C.%5BA-Za-z%5D%5BA-Za-z0-9_-%5D+%5B%20%5D+%7D%7D$%29%7C%28%5E%5BA-Za-z%5D%5BA-Za-z0-9_-%5D+$%29/type] expected string, but got number

My Lsp which seem to work fine does not complain in the same way.

I've tried to experiment a little and apparently it goes into the patternProperties that is on the same level as the properties where the Env property is for the "keyname1", but not for "keyname0"

Your LSP is giving wrong result. it should give error. I tested your example with online validate at https://www.jsonschemavalidator.net. it also gives the same result.

to explain the error, I constructed following simplified example:

schema.json

{
  "properties": {
    "x": {
      "type": "string"
    }
  },
  "patternProperties": {
      "x*": {
        "type": "number"
      }
    }
}

instance.json

{
  "x": "y"
}

the above json gives following error:

[I#] [S#] doesn't validate with file:///Users/santhosh/gh/santhosh-tekuri/jsonschema/cmd/jv/sch.json#
  [I#/x] [S#/patternProperties/x%2A/type] expected number, but got string

if you change json to following:

{
  "x": 12
}

you get following error:

[I#] [S#] doesn't validate with file:///Users/santhosh/gh/santhosh-tekuri/jsonschema/cmd/jv/sch.json#
  [I#/x] [S#/properties/x/type] expected string, but got number

you can see that it fails in both cases where x value is string and number

as per the schema the property x matches two schemas /properties/x and patternProperties/x*. so the property value must satisfy both schemas.

/properties/x says value must be string.
patternProperties/x* says value must be number

thus it is impossible to add property x with valid value

Thank you for quick response!

Right, So I would need to not match on Env in patternProperties then or on x in your example.

Yes. You should use oneOf keyword for or behaviour

Ah, you mean instead of | in patternProperties? I guess if I got you right that would make it easier indeed. :)

{ "oneOf":[
{"properties": { "x": { "type": "string" } }},
{"patternProperties": { "x*": { "type": "number" } } }}
]}

I'm not sure if that will work in my example though? As I need both Env and aname0 to be valid

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "patternProperties": {
    "(^{{[ ]+\\.[A-Za-z][A-Za-z0-9_-]+[ ]+}}$)|(^[A-Za-z][A-Za-z0-9_-]+$)": {
      "oneOf": [
        {
          "properties": {
            "Env": {
              "patternProperties": {
                "^[A-Za-z][A-Za-z0-9_-]+$": {
                  "type": [
                    "boolean",
                    "number",
                    "string"
                  ]
                }
              },
              "type": "object"
            }
          }
        },
        {
          "patternProperties": {
            "(^{{[ ]+\\.[A-Za-z][A-Za-z0-9_-]+[ ]+}}$)|(^[A-Za-z][A-Za-z0-9_-]+$)": {
              "properties": {
                "Default": {
                  "type": "string",
                  "enum": [
                    "ALLOW",
                    "DENY"
                  ]
                }
              },
              "patternProperties": {
                "(^{{[ ]+\\.[A-Za-z][A-Za-z0-9_-]+[ ]+}}$)|(^[A-Za-z][A-Za-z0-9_-]+$)": {
                  "type": "string",
                  "title": "Policy"
                }
              },
              "additionalProperties": false,
              "type": [
                "object",
                "null"
              ]
            }
          }
        }
      ],
      "additionalProperties": false,
      "type": [
        "object",
        "null"
      ]
    }
  },
  "type": "object",
  "title": "Resourcename"
}

You are right. It does not seem straight forward

Thanks anyway, I know why its not working and should be able to find a solution now :)

I found the solution using propertyNames.

below is the working schema:

{
   "$schema":"https://json-schema.org/draft/2020-12/schema",
   "patternProperties":{
      "(^{{[ ]+\\.[A-Za-z][A-Za-z0-9_-]+[ ]+}}$)|(^[A-Za-z][A-Za-z0-9_-]+$)":{
         "propertyNames":{
            "pattern":"(^{{[ ]+\\.[A-Za-z][A-Za-z0-9_-]+[ ]+}}$)|(^[A-Za-z][A-Za-z0-9_-]+$)"
         },
         "properties":{
            "Env":{
               "patternProperties":{
                  "^[A-Za-z][A-Za-z0-9_-]+$":{
                     "type":[
                        "boolean",
                        "number",
                        "string"
                     ]
                  }
               },
               "type":"object"
            }
         },
         "additionalProperties":{
            "propertyNames":{
               "pattern":"(^{{[ ]+\\.[A-Za-z][A-Za-z0-9_-]+[ ]+}}$)|(^[A-Za-z][A-Za-z0-9_-]+$)"
            },
            "properties":{
               "Default":{
                  "type":"string",
                  "enum":[
                     "ALLOW",
                     "DENY"
                  ]
               }
            },
            "additionalProperties":{
               "type":"string",
               "title":"Policy"
            },
            "type":[
               "object",
               "null"
            ]
         },
         "type":[
            "object",
            "null"
         ]
      }
   },
   "type":"object",
   "title":"Resourcename"
}

Cool thanks will check it out.. on my phone atm. Thanks again :)