ExodusMovement / schemasafe

A reasonably safe JSON Schema validator with draft-04/06/07/2019-09/2020-12 support.
https://npmjs.com/@exodus/schemasafe
MIT License
161 stars 12 forks source link

How to create a heterogeneous array of allOf? #137

Closed ferologics closed 4 years ago

ferologics commented 4 years ago

I'm trying to figure out a way to represent an array of enum. Could you point me to an example?

I tried to verify

[
  { "type": "foo", "a": "string" },
  { "type": "bar", "b": 0 }
]

against my schema

{
  "definitions": {
    "foo": { 
      "type": "object",
      "properties": { "a": "string" },
      "required": ["a"]
    },
    "bar": { 
      "type": "object",
      "properties": { "b": "number" },
      "required": ["b"]
    }
  },
  "allOf":[
    { "$ref": "#/definitions/foo" },
    { "$ref": "#/definitions/bar" }
  ]
}
with JSON Schema Lint, but I get an error 🛑 Field Error Value
.definitions['foo'].properties['a'] should be object,boolean "string"
.definitions['bar'].properties['b'] should be object,boolean "number"
ferologics commented 4 years ago

Just to illustrate I thought maybe something like this would work but it's not a valid JSON:

"allOf":[
+  "enum": [
+   { "$ref": "#/definitions/foo" },
+   { "$ref": "#/definitions/bar" }
+  ]
]
ferologics commented 4 years ago

Okay I got the schema:

{
  "definitions": {
    "foo" : { "a" : "string" },
    "bar" : { "b":"integer" }
  },
  "allOf":[
    { "$ref": "#/definitions/foo" }, 
    { "$ref": "#/definitions/bar" },
    {
      "properties" : {
        "type" : {
          "enum" : [
            "foo",
            "bar"
          ]
        }
      }
    }
  ]
}

and it validates to:

[
  { "type": "foo", "a": "string" },
  { "type": "bar", "b": 0 }
]

now I'm concerned with how to make it "schemasafe". Right now even this is "valid":

[
  { "type": "bar", "a": "string" },
  { "type": "foo", "b": 0 }
]

How can I restrict foo to always have "a" : "string" and bar to have a "b":"integer"?

ferologics commented 4 years ago

I tried to use the array type with items schema:

{
  "definitions":{
    "foo":{ "a":"string" },
    "bar":{ "b":"integer" }
  },
  "type":"array",
  "items":{
    "oneOf":[
      { "$ref":"#/definitions/foo" },
      { "$ref":"#/definitions/bar" }
    ]
  }
}

but this doesn't match my output

[
  {"type": "foo", "a":"" },
  {"type": "bar", "b":1 }  
]
and I get the error 🛑 Field Error Value
[0] should match exactly one schema in oneOf { "type": "foo", "a": "" }
[1] should match exactly one schema in oneOf { "type": "bar", "b": 1 }
ferologics commented 4 years ago

This type of schema also does not work against the required JSON.

{
  "definitions":{
    "foo":{ "a":"string" },
    "bar":{ "b":"integer" }
  },
  "type":"array",
  "items":{
    "oneOf":[
      {
        "$ref":"#/definitions/foo", 
        "properties": { "type": { "enum": ["foo"] } }, "required": ["type"] 
      },
      { 
        "$ref":"#/definitions/bar", 
        "properties": { "type": { "enum": ["bar"] } }, "required": ["type"] 
      }
    ]
  }
}

How can I match the type requirement?

ferologics commented 4 years ago

I think I've finally got the hang of it. This would be an example using the discriminator:

{
   "schemas": {
      "Baz": {
         "type": "object",
         "discriminator": {
            "propertyName": "type",
            "mapping": {
               "foo": "#/schemas/Foo",
               "bar": "#/schemas/Bar"
            }
         },
         "properties": {
            "type": {
               "description": "Type of an object",
               "type": "string"
            }
         }
      },
      "Foo": {
         "allOf": [
            {
               "$ref": "#schemas/Baz"
            },
            {
               "type": "object",
               "properties": {
                  "a": "string"
               },
               "required": [
                  "a"
               ]
            }
         ]
      },
      "Bar": {
         "allOf": [
            {
               "$ref": "#schemas/Baz"
            },
            {
               "type": "object",
               "properties": {
                  "b": "int"
               },
               "required": [
                  "b"
               ]
            }
         ]
      }
   }
}

However I'm feel like I'm still lacking the full understanding to produce an idiomatic formula for representing a heterogeneous collection of either Foo or Bar objects. There seems to be many ways of representing such a collection but I'd like it to be 'schemasafe'.

ferologics commented 4 years ago

I've got an answer to the issue elsewhere.