0

I've been struggling with "switch" in JSON Schema. Went through couple of GitHub and SO discussions on this topic but haven't find solution. My intention is to vary "payload" object properties based on "id" enum that will have 30 different mappings ("payload" definitions per enum "id"). For example first message json object will have amount and other properties but for the demo purpose let's go only with one property (amout):

{
"message": {
    "id": 1,
    "correlationId": "a0011e83-280e-4085-b0f1-691059aaae61",
    "payload": {
        "amount": 100
    }
}

}

And second json:

{
"message": {
    "id": 2,
    "correlationId": "a0011e83-280e-4085-b0f1-691059aaae61",
    "payload": {
        "code": "xyz"
    }
}

}

Is there a way to build JSON Schema (draft 7 or any other) in this manner?

w0rm
  • 21
  • 2
  • What’s wrong with having a big `oneOf` in the `message` schema? Each `oneOf` would include the `"id": { "const": 1 }` and the respective `payload` schema’s `properties`. There are a couple of examples also here on SO. – Carsten Jun 17 '20 at 09:02
  • @Carsten So basically you are proposing something like this: `{ "message": { "type": "object", "required": ["subject"], "oneOf": [ {"properties": {"subject": {"const": 1}}}, {"properties": {"payload": {"$ref": "#/definitions/payload1"}}}, {"properties": {"subject": {"const": 2}}}, {"properties": {"payload": {"$ref": "#/definitions/payload2"}}}, ], "definitions": { "payload1": { "amount": "number" }, "payload2": { "name": "string" } } } }` – w0rm Jun 17 '20 at 10:08
  • The given schema is not quite right yet (just hard to correct via the phone now), but that’s the rough direction yes. – Carsten Jun 17 '20 at 10:19
  • Does this answer your question? [jsonSchema attribute conditionally required](https://stackoverflow.com/questions/38717933/jsonschema-attribute-conditionally-required) – Jason Desrosiers Jun 17 '20 at 14:59

1 Answers1

1

What you're asking for is a fairly common requirement. Using oneOf/anyOf should get you where you want.

In those cases where the alternatives are mutually exclusive (due to the different "id" values), I'm in favour of anyOf to allow Schema Validator to stop checking when encountering the first matching subschema – whereas oneOf implies that all other alternatives must not match, e.g. in case of "id": 1 a validator would only have to check against the first subschema in an anyOf to indicate that it is valid while for oneOf it'd have to check against the other 29 to ensure that those aren't also valid. But you may find oneOf more expressive for human consumers of your schema.

For your particular scenario, I'd imagine something along the lines of the following schema:

{
  "type": "object",
  "required": ["message"],
  "properties": {
    "message": {
      "type": "object",
      "required": ["id", "correlationId", "payload"],
      "properties": {
        "id": { "enum": [1, 2, 3] },
        "correlationId": { "type": "string" },
        "payload": { "type": "object" }
      },
      "anyOf": [
        {
          "properties": {
            "id": { "const": 1 },
            "payload": { "$ref": "#/definitions/payload1" }
          }
        },
        {
          "properties": {
            "id": { "const": 2 },
            "payload": { "$ref": "#/definitions/payload2" }
          }
        },
        {
          "properties": {
            "id": { "const": 3 },
            "payload": { "$ref": "#/definitions/payload3" }
          }
        },
      ]
    }
  },
  "definitions": {
    "payload1": {
      "type": "object",
      "required": ["amount"],
      "properties": {
        "amount": { "type": "integer" }
      }
    },
    "payload2": {
      "type": "object",
      "required": ["code"],
      "properties": {
        "code": { "type": "string" }
      }
    },
    "payload3": {
      "type": "object",
      "required": ["foo"],
      "properties": {
        "foo": { "type": "string" }
      }
    }
  }
}
Carsten
  • 2,047
  • 1
  • 21
  • 46
  • Thanks a lot! This is the first building block but it was important for me to understand it so I can reuse it in deeper (nested) levels. – w0rm Jun 18 '20 at 12:41