Search code examples
jsonvalidationjsonschemaajv

Json schema validation not working with multiple if else conditions


Hi I am pretty sure I am doing something wrong with my json schema, my usecase is that I add some elements to a table using documents and in this case the elements are added through name, value key pair. Now if the value of name is 'name' then it's value pair should follow a regex validation, except for 1 particular scenario if the tableName where these are being added is header, in that case it should not follow this regex pattern.

my schema is ->

{
    "properties": {
        "elements": {
            "type": "array",
            "items": {
                "$ref": "#arrayElement"
            },
            "minItems": 1
        }
    },
"definitions": {
        "arrayElement": {
            "$id": "#arrayElement",
            "type": "object",
            "required": ["tableName"],
            "properties": {
                "tableName": {
                    "type": "string"
                },
                "elms": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "required": ["name", "value"],
                        "properties": {
                            "name": {
                                "type": "string"
                            },
                            "value": {
                                "anyOf": [
                                    {
                                        "type": "string"
                                    },
                                    {
                                        "type": "number"
                                    },
                                    {
                                        "type": "boolean"
                                    }
                                ]
                            }
                        }
                    },
                    "minItems": 0
                },
                "ele2": {
                    "type": "object",
                    "$ref": "#objectElement"
                },
                "ele3": {
                    "type": "array",
                    "items": {
                        "$ref": "#arrayElement"
                    },
                    "minItems": 0
                }
            },
            "allOf": [
                {
                    "if": {
                        "properties": {
                            "elms": {
                                "items": {
                                    "properties": {
                                        "name": {
                                            "const": "name"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "then": {
                        "allOf": [
                            {
                                "if": {
                                    "properties": {
                                        "tableName": {
                                            "const": "header"
                                        }
                                    }
                                },
                                "then": {
                                    "properties": {
                                        "elms": {
                                            "items": {
                                                "properties": {
                                                    "value": {
                                                        "type": "string"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                },
                                "else": {
                                    "properties": {
                                        "elms": {
                                            "items": {
                                                "properties": {
                                                    "value": {
                                                        "type": "string",
                                                        "pattern": "^[A-Za-z][A-Za-z0-9_.#/-]*$"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        ]
                    },
                    "else": {
                        "properties": {
                            "elms": {
                                "items": {
                                    "properties": {
                                        "value": {
                                            "anyOf": [
                                                {
                                                    "type": "string"
                                                },
                                                {
                                                    "type": "number"
                                                },
                                                {
                                                    "type": "boolean"
                                                }
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            ]
        }
    }
}

Now this payload is passing through which should fail due to regex validation on name

"elements": [
        {
            "tableName": "row",
            "elms": [
                {
                    "name": "name",
                    "value": "__________________________"
                },
                {
                    "name": "format",
                    "value": "string"
                },
                {
                    "name": "length",
                    "value": 15
                },
                {
                    "name": "active",
                    "value": true
                }
            ]
        }
    ]

Thanks in Advance.

EDIT: To debug more I removed the condition on tableName all together just to see, if the basic condition on name is working or not, seems like that itself is not working. Could it be that the way I have represented properties.elms.items.properties.name is incorrect?

{
  "properties": {
    "elements": {
      "type": "array",
      "items": {
        "$ref": "#arrayElement"
      },
      "minItems": 1
    }
  },
  "definitions": {
    "arrayElement": {
      "$id": "#arrayElement",
      "type": "object",
      "required": ["tableName"],
      "properties": {
        "tableName": {
          "type": "string"
        },
        "elms": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["name", "value"],
            "properties": {
              "name": {
                "type": "string"
              },
              "value": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "boolean"
                  }
                ]
              }
            }
          },
          "minItems": 0
        },
        "ele2": {
          "type": "object",
          "$ref": "#objectElement"
        },
        "ele3": {
          "type": "array",
          "items": {
            "$ref": "#arrayElement"
          },
          "minItems": 0
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "elms": {
                "items": {
                  "properties": {
                    "name": {
                      "const": "name"
                    }
                  }
                }
              }
            }
          },
          "then": {
            "properties": {
              "elms": {
                "items": {
                  "properties": {
                    "value": {
                      "type": "string",
                      "pattern": "^[A-Za-z][A-Za-z0-9_.#/-]*$"
                    }
                  }
                }
              }
            }
          },
          "else": {
            "properties": {
              "elms": {
                "items": {
                  "properties": {
                    "value": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "number"
                        },
                        {
                          "type": "boolean"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      ]
    }
  }
}

EDIT -> Schema after Jason's suggestion.

"allOf": [
                {
                    "if": {
                        "properties": {
                            "tableName": {
                                "not": { "const": "header" }
                            },
                            "elms": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "properties": {
                                        "name": { "const": "name" }
                                    },
                                    "required": ["name"]
                                }
                            }
                        },
                        "required": ["tableName", "elms"]
                    },
                    "then": {
                        "properties": {
                            "elms": {
                                "items": {
                                    "properties": {
                                        "value": {
                                            "type": "string",
                                            "pattern": "^[A-Za-z][A-Za-z0-9_.#/-]*$"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            ]

Solution

  • I managed to do it by segregating the if conditions.

    Would like to thank Jason for guiding me to the solution.

    The "if" and "then" clauses seem to check both the tableName and the elms array, but since I am working with arrays and objects within arrays, I need to apply conditions individually inside the items definition for elms.

    {
      "properties": {
        "elements": {
          "type": "array",
          "items": {
            "$ref": "#arrayElement"
          },
          "minItems": 1
        }
      },
      "definitions": {
        "arrayElement": {
          "$id": "#arrayElement",
          "type": "object",
          "required": ["tableName"],
          "properties": {
            "tableName": {
              "type": "string"
            },
            "elms": {
              "type": "array",
              "items": {
                "type": "object",
                "required": ["name", "value"],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "value": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "number"
                      },
                      {
                        "type": "boolean"
                      }
                    ]
                  }
                }
              },
              "minItems": 0
            }
          },
          "allOf": [
            {
              "if": {
                "properties": {
                  "tableName": {
                    "not": { "const": "header" }
                  }
                }
              },
              "then": {
                "properties": {
                  "elms": {
                    "items": {
                      "if": {
                        "properties": {
                          "name": { "const": "name" }
                        },
                        "required": ["name"]
                      },
                      "then": {
                        "properties": {
                          "value": {
                            "type": "string",
                            "pattern": "^[A-Za-z][A-Za-z0-9_.#/-]*$"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          ]
        }
      }
    }