Atrybut jsonSchema jest warunkowo wymagany

104

W jsonSchema możesz wskazać, czy zdefiniowane pola są obowiązkowe, czy nie za pomocą requiredatrybutu:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "properties": {
        "header": {
            "type": "object",
            "properties": {
                "messageName": {
                    "type": "string"
                },
                "messageVersion": {
                    "type": "string"
                }
            },
            "required": [
                "messageName",
                "messageVersion"
            ]
        }
    },
    "required": [
        "header"
    ]
}

W niektórych przypadkach chciałbym, aby to messageVersionpole nie było obowiązkowe. Czy istnieje sposób, aby uzależnić obowiązkowość tego pola?

tom redfern
źródło
Tak, powinno być możliwe. Jakie informacje zawarte w danych wywołałyby obowiązek?
jruizaranguren
@SarveswaranMeenakshiSundaram - Nie wiem, że korzystałem tylko z v4 schematu json
tom redfern
Czy to w ogóle możliwe w wersji 3?
Sarvesh,
@SarveswaranMeenakshiSundaram - nie wiem. Wypróbuj i daj nam znać, proszę!
tom redfern

Odpowiedzi:

275

W zależności od Twojej sytuacji istnieje kilka różnych podejść. Przychodzą mi do głowy cztery różne sposoby warunkowego wymagania pola.

Zależności

Słowo dependencieskluczowe jest warunkową odmianą requiredsłowa kluczowego. Foreach w dependencies, jeśli właściwość jest obecna w walidowanym formacie JSON, schemat skojarzony z tym kluczem również musi być prawidłowy. Jeśli występuje właściwość „foo”, wymagana jest właściwość „bar”

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "dependencies": {
    "foo": { "required": ["bar"] }
  }
}

Istnieje również krótka forma, jeśli schemat zawiera tylko requiredsłowo kluczowe.

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "dependencies": {
    "foo": ["bar"]
  }
}

Implikacja

Jeśli twój stan zależy od wartości pola, możesz użyć logiki logicznej zwanej implikacją. „A implikuje B” faktycznie oznacza, że ​​jeśli A jest prawdziwe, to B również musi być prawdziwe. Implikację można również wyrazić jako „! A lub B”. Właściwość „foo” nie jest równa właściwości „bar” lub właściwość „bar” jest wymagana . Lub innymi słowy: jeśli właściwość „foo” jest równa „bar”, wówczas właściwość „bar” jest wymagana

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "anyOf": [
    {
      "not": {
        "properties": {
          "foo": { "const": "bar" }
        },
        "required": ["foo"]
      }
    },
    { "required": ["bar"] }
  ]
}

Jeśli „foo” nie jest równe „bar”, #/anyOf/0dopasowania i walidacja powiodły się. Jeśli „foo” równa się „bar”, #/anyOf/0nie powiedzie się i #/anyOf/1musi być ważne, aby anyOfwalidacja zakończyła się pomyślnie.

Enum

Jeśli warunek jest oparty na wyliczeniu, jest to trochę prostsze. „foo” może być „bar” lub „baz”. Jeśli „foo” równa się „bar”, to „bar” jest wymagany. Jeśli „foo” równa się „baz”, to „baz” jest wymagane.

{
  "type": "object",
  "properties": {
    "foo": { "enum": ["bar", "baz"] },
    "bar": { "type": "string" },
    "baz": { "type": "string" }
  },
  "anyOf": [
    {
      "properties": {
        "foo": { "const": "bar" }
      },
      "required": ["bar"]
    },
    {
      "properties": {
        "foo": { "const": "baz" }
      },
      "required": ["baz"]
    }
  ]
}

Jeśli-to-inaczej

Stosunkowo nowy dodatek do JSON Schema (projekt-07) dodaje if, thenoraz elsesłowa kluczowe. Jeśli właściwość „foo” równa się „bar”, wówczas właściwość „bar” jest wymagana

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "if": {
    "properties": {
      "foo": { "const": "bar" }
    },
    "required": ["foo"]
  },
  "then": { "required": ["bar"] }
}

EDYCJA 23.12.2017: Zaktualizowano sekcję Implikacje i dodano sekcję Jeśli-to-inaczej.

EDYCJA 04.06.2018: Poprawka błędu dla Jeśli-To-Inaczej i zaktualizuj singletony enumdo użycia const.

Jason Desrosiers
źródło
7
@scubbo Nie jestem fanem if-then-elsesłów kluczowych i odmawiam ich używania. Jeśli jednak zdecydujesz się go użyć, sugeruję zawsze umieszczanie ich w polu allOfzawierającym tylko te trzy słowa kluczowe. { ...other_keywords..., "allOf": [{ "if": ..., "then": ..., "else": ... }], ...more_keywords... }
Jason Desrosiers
2
@Jason Dlaczego nie fanem if...? Myślę, że krótka opinia na ten temat w Twojej odpowiedzi byłaby całkowicie uzasadniona. A może to długa historia?
Clay Bridges
7
@ClayBridges Sekcja komentarzy nie jest odpowiednim miejscem do tej dyskusji, ale oto krótka wersja. Zasadniczo słowa kluczowe schematu JSON są bezstanowe. Żadne informacje inne niż wartość słowa kluczowego nie mogą być używane do sprawdzania poprawności wystąpienia. if, theni elsenaruszają tę zasadę, ponieważ są od siebie zależne.
Jason Desrosiers
3
@GGirard, to jest najlepsze podejście do użycia tych wzorców w schemacie JSON, o którym wiem. Operacje boolowskie są oficjalnie udokumentowane, ale reszta to tylko matematyka. allOf== I, anyOf== LUB, oneOf== XOR i not== NIE. Możesz wyszukać w Google „algebrę boolowską”, aby uzyskać więcej zasobów na temat matematyki (np. Implikacji).
Jason Desrosiers
2
@AlexeyShrub Od jakiegoś czasu chciałem o tym napisać, ale inne rzeczy mnie rozproszyły. Jestem fanem idei warunkowej. Ułatwia to ludziom zrozumienie. Mój sprzeciw dotyczy sposobu, w jaki został zdefiniowany jako trzy oddzielne stanowe słowa kluczowe (patrz poprzedni komentarz). Posiadanie słów kluczowych, które naruszają właściwości architektoniczne, które są stosowane przez inne słowa kluczowe, sprawia, że ​​walidatory schematu JSON są trudniejsze do wdrożenia i mniej wydajne. Gdyby warunki warunkowe zostały zdefiniowane w inny sposób niż bezpaństwowiec, nie miałbym sprzeciwu.
Jason Desrosiers