Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

2 oneOf in json schema: 1 required 1 optional

I have a json schema that works when I use 2 oneOf for 2 choices between required fields. But I don't know how to do the same with optional fields.

My schema:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "properties": {
    "a": {
      "type": "number"
    },
    "b": {
      "type": "number"
    },
    "c": {
      "type": "number"
    },
    "d": {
      "type": "number"
    },
    "e": {
      "type": "number"
    }
  },
  "required": ["a"],
  "oneOf": [
    {"required": ["b"]},
    {"required": ["c"]}
  ],
  "oneOf": [
    {"required": ["d"]},
    {"required": ["e"]}
  ]
}

How can I transform to make d and e optional (while never having both at once)?

like image 679
lovelace63 Avatar asked Dec 14 '16 15:12

lovelace63


1 Answers

Here's a gist with my solution, shown in http://jsonschemalint.com. (Very handy tool!)

Properties d and e are already defined, and allowed (but not required) in any combination. To make these two properties mutually exclusive, we say that the instance must not match a schema that has both properties:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "properties": {
    "a": {"type": "number"},
    "b": {"type": "number"},
    "c": {"type": "number"},
    "d": {"type": "number"},
    "e": {"type": "number"}
  },
  "required": ["a"],
  "oneOf": [
      {"required": ["b"]},
      {"required": ["c"]}
    ],
  "not" : {"required" : ["d","e"]}
}

This works nicely, but it starts getting messy if you have more than two properties in your mutually exclusive group. For example, if we wanted to add property f and make it mutually exclusive with respect to d and e, we'd have to disallow three distinct combinations:

  "not" : {
    "anyOf"{
      {"required" : ["d","e"]},
      {"required" : ["d","f"]},
      {"required" : ["e","f"]},
    }
  }

Here's an alternative that's more verbose, but scales up to larger sets of mutually exclusive properties:

  "oneOf" : [
    {"required" : ["d"]},
    {"required" : ["e"]},
    {"required" : ["f"]},
    {
      "not" : {
        "anyOf" : [
          {"required" : ["d"]},
          {"required" : ["e"]},
          {"required" : ["f"]}
        ]
      }  
    }
  ]

Each property added to the group requires exactly two lines.

Since our schema now has two oneOf constraints, we need to combine them using allOf:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "properties": {
    "a": {"type": "number"},
    "b": {"type": "number"},
    "c": {"type": "number"},
    "d": {"type": "number"},
    "e": {"type": "number"},
    "f": {"type": "number"}
  },
  "required": ["a"],
  "allOf": [
    {
      "oneOf": [
          {"required": ["b"]},
          {"required": ["c"]}
        ]
    },
    {
      "oneOf" : [
        {"required" : ["d"]},
        {"required" : ["e"]},
        {"required" : ["f"]},
        {
          "not" : {
            "anyOf" : [
              {"required" : ["d"]},
              {"required" : ["e"]},
              {"required" : ["f"]}
            ]
          }  
        }
      ]
    }
  ]
}
like image 183
Ted Epstein Avatar answered Nov 01 '22 05:11

Ted Epstein