diff --git a/README.textile b/README.textile index 2755e8c8..8ae964cd 100644 --- a/README.textile +++ b/README.textile @@ -22,7 +22,7 @@ From the git repo:
 $ gem build json-schema.gemspec
-$ gem install json-schema-2.0.4.gem
+$ gem install json-schema-2.1.0.gem
 
@@ -149,6 +149,34 @@ errors = JSON::Validator.fully_validate(schema, data, :errors_as_objects => true +h3. Validate against a fragment of a supplied schema + +
+  require 'rubygems'
+  require 'json-schema'
+
+  schema = {
+    "type" => "object",
+    "required" => ["a","b"],
+    "properties" => {
+      "a" => {"type" => "integer"},
+      "b" => {"type" => "string"},
+      "c" => {
+        "type" => "object",
+        "properties" => {
+          "z" => {"type" => "integer"}
+        }
+      }
+    }
+  }
+
+  data = {
+    "z" => 1
+  }
+
+  JSON::Validator.validate(schema, data, :fragment => "#/properties/c")
+
+ h3. Validate a JSON object against a JSON schema object, while also validating the schema itself
diff --git a/VERSION.yml b/VERSION.yml
index deefdc7a..ab713c02 100644
--- a/VERSION.yml
+++ b/VERSION.yml
@@ -1,3 +1,3 @@
 major: 2
-minor: 0
-patch: 4
+minor: 1
+patch: 0
diff --git a/lib/json-schema/attributes/additionalproperties.rb b/lib/json-schema/attributes/additionalproperties.rb
index c4419ce9..93e767a7 100644
--- a/lib/json-schema/attributes/additionalproperties.rb
+++ b/lib/json-schema/attributes/additionalproperties.rb
@@ -4,12 +4,16 @@ module JSON
   class Schema
     class AdditionalPropertiesAttribute < Attribute
       def self.validate(current_schema, data, fragments, processor, validator, options = {})
-        if data.is_a?(Hash)
+        if data.is_a?(Hash) && (
+          current_schema.schema['type'].nil? || (
+            current_schema.schema['type'].is_a?(String) &&
+            current_schema.schema['type'].downcase == 'object'
+          )
+        )
           extra_properties = data.keys
-
           extra_properties = remove_valid_properties(extra_properties, current_schema, validator)
 
-          addprop= current_schema.schema['additionalProperties']
+          addprop = current_schema.schema['additionalProperties']
           if addprop.is_a?(Hash)
             matching_properties= extra_properties # & addprop.keys
             matching_properties.each do |key|
@@ -57,6 +61,7 @@ def self.remove_valid_properties(extra_properties, current_schema, validator)
 
           extra_properties
       end
+
     end
   end
 end
diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb
index 453ca5fc..c2cd6225 100644
--- a/lib/json-schema/validator.rb
+++ b/lib/json-schema/validator.rb
@@ -170,8 +170,39 @@ def initialize(schema_data, data, opts={})
       @@mutex.synchronize { @base_schema = initialize_schema(schema_data) }
       @data = initialize_data(data)
       @@mutex.synchronize { build_schemas(@base_schema) }
+
+      # If the :fragment option is set, try and validate against the fragment
+      if opts[:fragment]
+        @base_schema = schema_from_fragment(@base_schema, opts[:fragment])
+      end
     end
 
+    def schema_from_fragment(base_schema, fragment)
+      fragments = fragment.split("/")
+
+      # ensure the first element was a hash, per the fragment spec
+      if fragments.shift != "#"
+        raise JSON::Schema::SchemaError.new("Invalid fragment syntax in :fragment option")
+      end
+
+      fragments.each do |f|
+        if base_schema.is_a?(Hash)
+          if !base_schema.has_key?(f)
+            raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
+          end
+          base_schema = base_schema[f]
+        elsif base_schema.is_a?(Array)
+          if base_schema[f.to_i].nil?
+            raise JSON::Schema::SchemaError.new("Invalid fragment resolution for :fragment option")
+          end
+          base_schema = base_schema[f.to_i]
+        else
+          raise JSON::Schema::SchemaError.new("Invalid schema encountered when resolving :fragment option")
+        end
+      end
+
+      base_schema
+    end
 
     # Run a simple true/false validation of data against a schema
     def validate()
diff --git a/test/schemas/test_fragment_resolution.rb b/test/schemas/test_fragment_resolution.rb
new file mode 100644
index 00000000..2ef8ebb2
--- /dev/null
+++ b/test/schemas/test_fragment_resolution.rb
@@ -0,0 +1,30 @@
+require 'test/unit'
+require File.dirname(__FILE__) + '/../lib/json-schema'
+
+class FragmentResolution < Test::Unit::TestCase
+  def test_fragment_resolution
+    schema = {
+      "$schema" => "http://json-schema.org/draft-04/schema#",
+      "properties" => {
+        "a" => {
+          "type" => "object",
+          "properties" => {
+            "b" => {"type" => "integer" }
+          }
+        }
+      }
+    }
+
+    data = {"b" => 5}
+    assert(!JSON::Validator.validate(schema,data))
+    assert(JSON::Validator.validate(schema,data,:fragment => "#/properties/a"))
+
+    assert_raise JSON::Schema::SchemaError do
+      JSON::Validator.validate!(schema,data,:fragment => "/properties/a")
+    end
+
+    assert_raise JSON::Schema::SchemaError do
+      JSON::Validator.validate!(schema,data,:fragment => "#/properties/b")
+    end
+  end
+end
diff --git a/test/test_jsonschema_draft3.rb b/test/test_jsonschema_draft3.rb
index 6bbba94a..a8fbdc51 100644
--- a/test/test_jsonschema_draft3.rb
+++ b/test/test_jsonschema_draft3.rb
@@ -204,9 +204,27 @@ def test_types
 
     data["a"] = {"b" => "taco"}
     assert(!JSON::Validator.validate(schema,data))
-   end
 
+    # Test an array of unioned-type objects that prevent additionalProperties
+    schema["properties"]["a"] = {
+      'type' => 'array',
+      'items' => {
+        'type' => [
+          { 'type' => 'object', 'properties' => { "b" => { "type" => "integer" } } },
+          { 'type' => 'object', 'properties' => { "c" => { "type" => "string" } } }
+        ],
+        'additionalProperties' => false
+      }
+    }
+
+    data["a"] = [{"b" => 5}, {"c" => "foo"}]
+    errors = JSON::Validator.fully_validate(schema, data)
+    assert(errors.empty?, errors.join("\n"))
 
+    # This should actually pass, because this matches the first schema in the union
+    data["a"] << {"c" => false}
+    assert(JSON::Validator.validate(schema,data))
+  end
 
   def test_required
     # Set up the default datatype