Skip to content
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,18 @@ JSON::Validator.validate(schema, { "a" => 1 }, :parse_data => false)
# => false
JSON::Validator.validate(schema, '{ "a": 1 }', :parse_data => false)

#
# with the `:nullable` option set to true, any key with null value will be evaluated to true
#
schema = {
"type": "object",
"properties": {
"github_url": { "type": "string", "nullable": true }
}
}
# => true
JSON::Validator.validate!(schema, { "github_url" => null })

#
# with the `:parse_integer` option set to false, the integer value given as string will not be parsed.
#
Expand Down
4 changes: 3 additions & 1 deletion lib/json-schema/attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ def self.validation_errors(validator)
'any' => Object,
}

def self.data_valid_for_type?(data, type)
def self.data_valid_for_type?(data, type, nullable: false)
valid_classes = TYPE_CLASS_MAPPINGS.fetch(type) { return true }
valid_classes = [valid_classes, NilClass] if nullable
Copy link

@belinskidima belinskidima Sep 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case when type is boolean, you need to flatten the valid_classes

type = "boolean"
valid_classes = TYPE_CLASS_MAPPINGS.fetch(type) # => [TrueClass, FalseClass]
valid_classes = [valid_classes, NilClass] if nullable # =>  [[TrueClass, FalseClass], NilClass]
Array(valid_classes).any? { |c| data.is_a?(c) } # => #<TypeError: class or module required>

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking this @belinskidima

valid_classes.flatten! if valid_classes.is_a?(Array)
Array(valid_classes).any? { |c| data.is_a?(c) }
end

Expand Down
4 changes: 3 additions & 1 deletion lib/json-schema/attributes/type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ class Schema
class TypeAttribute < Attribute
def self.validate(current_schema, data, fragments, processor, validator, options = {})
union = true
nullable = false
if options[:disallow]
types = current_schema.schema['disallow']
else
types = current_schema.schema['type']
nullable = current_schema.schema['nullable']
end

if !types.is_a?(Array)
Expand All @@ -22,7 +24,7 @@ def self.validate(current_schema, data, fragments, processor, validator, options

types.each_with_index do |type, type_index|
if type.is_a?(String)
valid = data_valid_for_type?(data, type)
valid = data_valid_for_type?(data, type, nullable: nullable)
elsif type.is_a?(Hash) && union
# Validate as a schema
schema = JSON::Schema.new(type, current_schema.uri, validator)
Expand Down
3 changes: 2 additions & 1 deletion lib/json-schema/attributes/type_v4.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ class TypeV4Attribute < Attribute
def self.validate(current_schema, data, fragments, processor, validator, options = {})
union = true
types = current_schema.schema['type']
nullable = current_schema.schema['nullable']
if !types.is_a?(Array)
types = [types]
union = false
end

return if types.any? { |type| data_valid_for_type?(data, type) }
return if types.any? { |type| data_valid_for_type?(data, type, nullable: nullable) }

types = types.map { |type| type.is_a?(String) ? type : '(schema)' }.join(', ')
message = format(
Expand Down
11 changes: 11 additions & 0 deletions test/initialize_data_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -379,4 +379,15 @@ def test_parse_hash_with_instantiated_validator
assert_raises(TypeError) { v.validate(data) }
assert_raises(TypeError) { v.validate(data) }
end

def test_nullable_param
schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string', 'nullable' => true } } }
data = { 'a' => nil }

assert(JSON::Validator.validate(schema, data))

schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string' } } }

refute(JSON::Validator.validate(schema, data))
end
end