Techioz Blog

ドライスキーマ gem を使用してさまざまなオブジェクトの配列を検証する方法

概要

次のようなさまざまなオブジェクトの配列を持つ JSON オブジェクトがあるとします。

{
  "array": [
    {
      "type": "type_1",
      "value": 5
    },
    {
      "type": "type_2",
      "kind": "person"
    }
  ]
}

JSON スキーマの検証によると、次の JSON スキーマ定義を使用してスキーマを検証できます。

{
  "type": "object",
  "properties": {
    "array": {
      "type": "array",
      "items": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "type_1"
                ]
              },
              "value": {
                "type": "integer",
                "enum": [
                  5
                ]
              }
            }
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "type_2"
                ]
              },
              "kind": {
                "type": "string",
                "enum": [
                  "person"
                ]
              }
            }
          }
        ]
      }
    }
  }
}

dry-schema gem を使用して入力 JSON を検証するにはどうすればよいですか?あなたはなにか考えはありますか?

解決策

問題に対処するには、次のコードを試してください。

class TestContract < Dry::Validation::Contract
  FirstHashSchema = Dry::Schema.Params do
    required(:type).filled(:string)
    required(:value).filled(:integer)
  end

  SecondHashSchema = Dry::Schema.Params do
    required(:type).filled(:string)
    required(:kind).filled(:string)
  end

  params do
    required(:array).array do
      # FirstHashSchema.or(SecondHashSchema) also works
      schema FirstHashSchema | SecondHashSchema
    end
  end
end

valid_input = {
  array: [
    {
      type: 'type_1',
      value: 5
    },
    {
      type: 'type_2',
      kind: 'person'
    }
  ]
}

TestContract.new.call(valid_input) #=> Dry::Validation::Result{:array=>[...] errors={}}


invalid_input = {
  array: [
    {
      type: 'type_1',
      bad_key: 5
    },
    {
      type: 'type_2',
      kind: 'person'
    }
  ]
}

TestContract.new.call(invalid_input) #=> Dry::Validation::Result{:array=>[...] errors={:array=>{0=>{:or=>[...]}}}

ここで重要なのはメソッド #schema です。ドキュメンテーション