web-dev-qa-db-ja.com

JSONスキーマ:「additionalProperties」を含む「allof」

スキーマに続くスキーマがあると仮定します(チュートリアル ここ から):

{
  "$schema": "http://json-schema.org/draft-04/schema#",

  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city":           { "type": "string" },
        "state":          { "type": "string" }
      },
      "required": ["street_address", "city", "state"]
    }
  },

  "type": "object",

  "properties": {
    "billing_address": { "$ref": "#/definitions/address" },
    "shipping_address": {
      "allOf": [
        { "$ref": "#/definitions/address" },
        { "properties":
          { "type": { "enum": [ "residential", "business" ] } },
          "required": ["type"]
        }
      ]
    } 

  }
}

有効なインスタンスは次のとおりです。

{
      "shipping_address": {
        "street_address": "1600 Pennsylvania Avenue NW",
        "city": "Washington",
        "state": "DC",
        "type": "business"
      }
}

shipping_addressの追加フィールドが無効になることを確認する必要があります。この目的でadditionalPropertiesが存在することを知っています。これは「false」に設定する必要があります。ただし、次のように"additionalProprties":falseを設定する場合:

"shipping_address": {
          "allOf": [
            { "$ref": "#/definitions/address" },
            { "properties":
              { "type": { "enum": [ "residential", "business" ] } },
              "required": ["type"]
            }
          ],
          "additionalProperties":false
        } 

検証エラーが表示されます(チェック ここ ):

[ {
  "level" : "error",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : "/properties/shipping_address"
  },
  "instance" : {
    "pointer" : "/shipping_address"
  },
  "domain" : "validation",
  "keyword" : "additionalProperties",
  "message" : "additional properties are not allowed",
  "unwanted" : [ "city", "state", "street_address", "type" ]
} ] 

質問は次のとおりです。shipping_address部分のみのフィールドを制限するにはどうすればよいですか?前もって感謝します。

49
lm.

[v4検証仕様のドラフトの著者はこちら]

JSONスキーマで最も一般的な問題、つまり、ユーザーが期待するとおりに継承を実行できないという根本的な問題に遭遇しました。しかし、同時にそれはその中核機能の1つです。

あなたがするとき:

"allOf": [ { "schema1": "here" }, { "schema2": "here" } ]

schema1schema2は、互いの知識noを持っています。それらは独自のコンテキストで評価されます。

多くの人が遭遇するシナリオでは、schema1で定義されたプロパティがschema2に認識されることを期待しています。しかし、これは事実ではありません。

この問題が、私がドラフトv5について次の2つの提案をした理由です。

shipping_addressのスキーマは次のようになります。

{
    "merge": {
        "source": { "$ref": "#/definitions/address" },
        "with": {
            "properties": {
                "type": { "enum": [ "residential", "business" ] }
            }
        }
    }
}

strictPropertiestrueaddressに定義します。


ちなみに、私はあなたが言及しているウェブサイトの著者でもあります。

次に、ドラフトv3に戻ります。ドラフトv3はextendsを定義し、その値はスキーマまたはスキーマの配列のいずれかでした。このキーワードの定義により、インスタンスは現在のスキーマおよびextendsで指定されたすべてのスキーマに対して有効でなければならないことを意味しました。基本的に、ドラフトv4のallOfはドラフトv3のextendsです。

これを考慮してください(ドラフトv3):

{
    "extends": { "type": "null" },
    "type": "string"
}

そして今、それ:

{
    "allOf": [ { "type": "string" }, { "type": "null" } ]
}

それらは同じです。それとも多分それ?

{
    "anyOf": [ { "type": "string" }, { "type": "null" } ]
}

またはその?

{
    "oneOf": [ { "type": "string" }, { "type": "null" } ]
}

全体として、これは、ドラフトv3のextendsが、人々が期待したことを実際に実行したことがないことを意味します。ドラフトv4では、*Ofキーワードが明確に定義されています。

しかし、あなたが抱えている問題は、最も一般的に遭遇する問題です。したがって、この誤解の原因を完全に解消する私の提案です!

70
fge

additionalPropertiesは、イミディエートスキーマpropertiesまたはpatternPropertiesによって考慮されないすべてのプロパティに適用されます。

これは、次の場合に意味します:

    {
      "allOf": [
        { "$ref": "#/definitions/address" },
        { "properties":
          { "type": { "enum": [ "residential", "business" ] } },
          "required": ["type"]
        }
      ],
      "additionalProperties":false
    }

ここでadditionalPropertiesは、兄弟レベルのpropertiesエントリがないため、allプロパティに適用されます。allOf内のエントリはカウントされません。

できることの1つは、properties定義を1レベル上に移動し、インポートするプロパティのスタブエントリを提供することです。

    {
      "allOf": [{"$ref": "#/definitions/address"}],
      "properties": {
        "type": {"enum": ["residential", "business"]},
        "addressProp1": {},
        "addressProp2": {},
        ...
      },
      "required": ["type"],
      "additionalProperties":false
    }

これは、additionalPropertiesが必要なプロパティに適用されないことを意味します。

7
cloudfeet

Yves-Mのソリューション のわずかに簡略化されたバージョンを次に示します。

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "state": {
          "type": "string"
        }
      },
      "required": [
        "street_address",
        "city",
        "state"
      ]
    }
  },
  "type": "object",
  "properties": {
    "billing_address": {
      "$ref": "#/definitions/address"
    },
    "shipping_address": {
      "allOf": [
        {
          "$ref": "#/definitions/address"
        }
      ],
      "properties": {
        "type": {
          "enum": [
            "residential",
            "business"
          ]
        },
        "street_address": {},
        "city": {},
        "state": {}
      },
      "required": [
        "type"
      ],
      "additionalProperties": false
    }
  }
}

これにより、ベースaddressスキーマの必須プロパティの検証が保持され、必要なtypeプロパティがshipping_addressに追加されます。

additionalPropertiesが直接の兄弟レベルのプロパティのみを考慮することは残念です。たぶんこれには理由があります。しかし、これが継承されたプロパティを繰り返す必要がある理由です。

ここでは、空のオブジェクト構文を使用して、単純化された形式で継承されたプロパティを繰り返しています。つまり、これらの名前のプロパティは、含まれる値の種類に関係なく有効です。しかし、ベースallOfスキーマで宣言された型制約(およびその他の制約)を強制するために、addressキーワードに依存できます。

5
Ted Epstein

定義レベルでadditionalProperties = falseを設定しないでください

そして、すべてがうまくいきます:

{    
    "definitions": {
        "address": {
            "type": "object",
            "properties": {
                "street_address": { "type": "string" },
                "city":           { "type": "string" },
                "state":          { "type": "string" }
            }
        }
    },

    "type": "object",
    "properties": {

        "billing_address": {
            "allOf": [
                { "$ref": "#/definitions/address" }
            ],
            "properties": {
                "street_address": {},
                "city": {},
                "state": {}                 
            },          
            "additionalProperties": false
            "required": ["street_address", "city", "state"] 
        },

        "shipping_address": {
            "allOf": [
                { "$ref": "#/definitions/address" },
                {
                    "properties": {
                        "type": {
                            "enum": ["residential","business"]
                        }
                    }
                }
            ],
            "properties": {
                "street_address": {},
                "city": {},
                "state": {},
                "type": {}                          
            },              
            "additionalProperties": false
            "required": ["street_address","city","state","type"] 
        }

    }
}

billing_addressおよびshipping_addressのそれぞれは、独自の必須プロパティを指定する必要があります。

彼のプロパティを他のプロパティと組み合わせたい場合、定義には"additionalProperties": falseを含めないでください。

2
Yves M.