3

I have spent all day trying to get this to work, will post a list of references and things I have tried after the question.

So here is my jsonschema:

{
    "data": [{
        "required": "effort",
        "decisive": "maybe",
        "field1": 7
    },
    {
        "required": "effort",
        "decisive": "no",
        "field1": 6
    }],
    "schema": {
        "$schema": "http://json-schema.org/draft-04/schema#",
        "type": "array",
        "items": {
            "type": "object",
            "properties": {
                "field1": {
                    "type": "string",
                    "pattern": "[A-Z]",
                    "title": "field1"
                },
                "required": {
                    "type": "string",
                    "title": "required",
                    "readonly": true
                },
                "decisive": {
                    "type": "string",
                    "title": "Decisive",
                    "enum": ["yes", "no", "maybe", "not now"]
                }

            }
        }
    }
}

Consider the exact piece of jsonschema but with the field1 element as follows:

"field1": {
    "type": "integer",
    "minimum": 5,
    "maximum": 10,
    "title": "field1"
}
  • The first example validates only capital letters in its field1
  • The second wants an integer between 5 and 10.

How can you make it validate either of these, so both are accepted -

  • either only capital letters
  • or an integer between 5 and 10?

Oh - the field1 in the data section above is not that important, it is a desired default value.

I have tried all kinds of ideas - with oneOf - here, here, here

param - here

additionalProperties - here

required - here

The intuitive thing was to use the oneOf on pattern, but oneOf, as is mentioned in many questions, does not do anything inside the properties section, only outside it. So I tried to have the exact same properties inside a oneOf with just the one difference as described above. That did not work either, and contains a lot of repetition which must somehow be avoidable.

Does anyone know how to solve this? Am out of ideas..

cardamom
  • 6,873
  • 11
  • 48
  • 102
  • Out of interest, is this an assignment of some kind, or work? Seen a few very similar questions over the past few days? – Relequestual Nov 08 '18 at 13:56
  • It's work. I think everyone is trying to use jsonschema for forms even if it wasn't made for that. I have a [question with a bounty](https://stackoverflow.com/questions/52990430/render-jsonschema-as-a-form-with-django-jsonschema-form-or-django-schemulator) at the moment to try to save us from alpaca so we can stay more within Python. – cardamom Nov 08 '18 at 14:02

1 Answers1

5

You were one the right track with oneOf, except what you actually want is anyOf. Almost every time you think you want oneOf, you really want anyOf. Remember that the values of properties are schemas just like any other. You can use the boolean keywords there just like anywhere else.

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "field1": {
                "title": "field1"
                "anyOf": [
                    {
                        "type": "string",
                        "pattern": "[A-Z]"
                    },
                    {
                        "type": "integer",
                        "minimum": 5,
                        "maximum": 10
                    }
                ]
            },
            "required": {
                "type": "string",
                "title": "required",
                "readonly": true
            },
            "decisive": {
                "type": "string",
                "title": "Decisive",
                "enum": ["yes", "no", "maybe", "not now"]
            }

        }
    }
}

Edit 1

When you hear that oneOf can't be used inside properties, this is the kind of thing they are talking about.

{
  "type": "object",
  "properties": {
    "anyOf": [
      {
        "field1": { ... }
      },
      {
        "field1": { ... }
      }
    ],
    "required": { ... },
    "decisive": { ... }
  }
}

Edit 2

Because it came up in the comments, here's a better explanation of why oneOf is almost never the right choice. To be clear, oneOf will always work in place of anyOf. If anyOf didn't exist, JSON Schema wouldn't loose any expressive power.

However, anyOf is a more precise tool. Using oneOf when anyOf will do is like using a sledge hammer to drive a nail when you have a simple claw hammer in your toolbox.

anyOf is the boolean OR operation. oneOf is the boolean "exclusive OR" (XOR) operation. "XOR" has so little usefulness, that modern languages don't even have support for it. OR is usually represented with the operator ||. XOR has no analog.

anyOf means any of the items can be true. oneOf means one and only one of the items can be true. When you use oneOf, the validator needs to test all of the schemas to ensure that one schema validates as true and the rest validate as false. When you use anyOf, the validator can stop as soon as it finds a schema that validates as true. This is called "short circuiting" and all modern programming languages do this when evaluating OR operations. When the schemas are mutually exclusive (which they almost always are), continuing to validate schemas after one is found is pure waste and therefore should be avoided.

I think oneOf is overused because from a natural language perspective, it sounds right.

Jason Desrosiers
  • 22,479
  • 5
  • 47
  • 53
  • 1
    While this schema will work, I'm not sure why you think `oneOf` is not the correct solution here. @cardamom what happens if you use this schema but replace the `anyOf` with `oneOf`? Does it still behave how you expect? In this instance, it should do, but `anyOf` and `oneOf` will behave differently in other situations. – Relequestual Nov 08 '18 at 09:32
  • @JasonDesrosiers thanks for the answer. @Relequestual am still struggling a bit to get everything to work in Alpaca form validation, but if you both think this should work the problem is probably something else.Asked [another](https://stackoverflow.com/questions/53204615/inform-alpaca-js-of-the-draft-version-number-of-your-jsonschema) question re alpaca. What I don't get here is, `anyOf` is **inside** properties and everything tells us not to do that, like [this](https://stackoverflow.com/a/25021445/4288043) and [this](https://stackoverflow.com/a/31841897/4288043). Full of contradictions... – cardamom Nov 08 '18 at 09:44
  • Guys, thanks, it works! It does exactly the correct validation if I run it with the Python library [fastjsonschema](https://horejsek.github.io/python-fastjsonschema/). It's with alpaca that there is some kind of bug. Need to know who to blame.. – cardamom Nov 08 '18 at 11:11
  • @cardamom Those links regarding `anyOf` insdie properties... What you're seeing there is, any key of the `properties` object is relating to a key in the JSON instance. The value of each key in a `properties` object, is a JSON Schema. So therefore what you have above is OK. Make sense? – Relequestual Nov 08 '18 at 11:52
  • 1
    There's nothing in the documentation that suggests alpaca.js supports `anyOf` or any `*Of` keywords from JSON Schema. JSON Schema is not designed for creation of forms, so libraries which generate forms tend to kind of do their own thing, as they see fit. There are github issues. – Relequestual Nov 08 '18 at 11:57
  • Thanks, was thinking about that, may open a github issue for it. – cardamom Nov 08 '18 at 14:05
  • I updated my answer to address some of this conversation. – Jason Desrosiers Nov 09 '18 at 04:30
  • GitHub issue since 2013 https://github.com/gitana/alpaca/issues/82 and people are not very happy about it.. – cardamom Nov 09 '18 at 12:51