💎️
Ruby で使える JSON Schema ライブラリを試す
調べたもの
ライブラリ | 説明 | スター数 |
---|---|---|
json-schema | 高性能 | 1.5k |
json_schemer | 速い | 391 |
dry-schema | Schema を Ruby で書く | 412 |
jtd | JSON Schema ではないっぽい | 10 |
用意したデータ
json_schema = {
"type" => "object",
"properties" => {
"name" => {"type" => "string"},
"age" => {"type" => "integer", "minimum" => 0},
},
"required" => ["name", "age"],
}
ok_data = { "name" => "alice", "age" => 20 }
ng_data = { "name" => "alice", "age" => -1 }
json-schema gem
require "json-schema"
判定を真偽で受け取るなら、
JSON::Validator.validate(json_schema, ok_data) # => true
JSON::Validator.validate(json_schema, ng_data) # => false
例外を出すには、
JSON::Validator.validate!(json_schema, ok_data) rescue $! # => true
JSON::Validator.validate!(json_schema, ng_data) rescue $! # => #<JSON::Schema::ValidationError: The property '#/age' did not have a minimum value of 0, inclusively in schema 3a1266ff-f235-5379-8cf6-d4f88d9bb20a>
! をつけるルールなのはわかりやすい。
エラー文言が必要なときは、
errors = JSON::Validator.validate(json_schema, ng_data, record_errors: true)
errors # => ["The property '#/age' did not have a minimum value of 0, inclusively in schema 3a1266ff-f235-5379-8cf6-d4f88d9bb20a"]
とする。
json_schemer gem
require "json_schemer"
まず JSON Schema 自体の表記チェックができる。
JSONSchemer.valid_schema?(json_schema) # => true
使うには、いったん JSONSchemer::Schema 型にして、
schemer = JSONSchemer.schema(json_schema) # => #<JSONSchemer::Schema @value={"type"=>"object", "properties"=>{"name"=>{"type"=>"string"}, "age"=>{"type"=>"integer", "minimum"=>0}}, "required"=>["name", "age"]} @parent=nil @keyword=nil>
真偽で受け取るなら、
schemer.valid?(ok_data) # => true
schemer.valid?(ng_data) # => false
として、エラー文言を受けとるなら、
errors = schemer.validate(ng_data) # => #<Enumerator: #<Enumerator::Generator:0x0000000108475348>:each>
errors.to_a # => [{"data"=>-1, "data_pointer"=>"/age", "schema"=>{"type"=>"integer", "minimum"=>0}, "schema_pointer"=>"/properties/age", "root_schema"=>{"type"=>"object", "properties"=>{"name"=>{"type"=>"string"}, "age"=>{"type"=>"integer", "minimum"=>0}}, "required"=>["name", "age"]}, "type"=>"minimum", "error"=>"number at `/age` is less than: 0"}]
errors.collect { |e| e["error"] } # => ["number at `/age` is less than: 0"]
とする。
番外編
dry-schema gem
require "dry-schema"
これは dry の一部で使われているデータ検証ライブラリで少し方向性が異なる。
schema = Dry::Schema.Params do
required(:name).filled(:string)
required(:age).filled(:integer, "gteq?" => 0)
end
合っている場合↓
result = schema.call(ok_data) # => #<Dry::Schema::Result{:name=>"alice", :age=>20} errors={} path=[]>
result.success? # => true
間違っている場合↓
result = schema.call(ng_data) # => #<Dry::Schema::Result{:name=>"alice", :age=>-1} errors={:age=>["must be greater than or equal to 0"]} path=[]>
result.success? # => false
result.errors # => #<Dry::Schema::MessageSet messages=[#<Dry::Schema::Message text="must be greater than or equal to 0" path=[:age] predicate=:gteq? input=-1>] options={:failures=>true}>
result.errors.to_h # => {:age=>["must be greater than or equal to 0"]}
jtd gem
require "jtd"
required を書くと怒られたのでどうやら JSON Schema とは異なるようだ。
schema = {
"properties" => {
"name" => { "type" => "string" },
"age" => { "type" => "uint32" },
},
}
いったん JTD::Schema 型にする。
schema = JTD::Schema.from_hash(schema) # => #<JTD::Schema:0x00000001083d9c18 @ref=nil, @type=nil, @enum=nil, @properties={"name"=>#<JTD::Schema:0x00000001083d98d0 @ref=nil, @type="string", @enum=nil, @discriminator=nil>, "age"=>#<JTD::Schema:0x000000010837fc90 @ref=nil, @type="uint32", @enum=nil, @discriminator=nil>}, @discriminator=nil>
合っている場合↓
JTD::validate(schema, ok_data) # => []
間違っている場合↓
JTD::validate(schema, ng_data) # => [#<struct JTD::ValidationError instance_path=["age"], schema_path=["properties", "age", "type"]>]
json-schema と json_schemer の速度比較
json_schemer は速いのが特徴らしいがどのくらい速いのだろうか?
require "active_support/core_ext/benchmark"
def _ = "%.1f ms" % Benchmark.ms { 10000.times { yield } }
_ { JSON::Validator.validate(json_schema, ok_data) } # => "492.4 ms"
_ { JSONSchemer.schema(json_schema).valid?(ok_data) } # => "205.5 ms"
簡単に比較してみると 2.4 倍ほど速かった。
関連
Discussion