json-schema 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  h1. Ruby JSON Schema Validator
2
2
 
3
- This library is intended to provide Ruby with an interface for validating JSON objects against a JSON schema conforming to "JSON Schema Draft 3":http://tools.ietf.org/html/draft-zyp-json-schema-03. The project originally started as a fork of "Constellation's ruby-jsonschema":https://github.com/Constellation/ruby-jsonchema project, but differences in the JSON schema draft versions implemented as well as assumptions made by the ruby-jsonschema library forced this to become a new project.
3
+ This library is intended to provide Ruby with an interface for validating JSON objects against a JSON schema conforming to "JSON Schema Draft 3":http://tools.ietf.org/html/draft-zyp-json-schema-03.
4
4
 
5
5
  h2. Usage
6
6
 
@@ -13,8 +13,8 @@ gem install json-schema
13
13
  If downloading the git repo, build the gem and install it:
14
14
 
15
15
  <pre>
16
- $ rake package
17
- $ gem install pkg/json-schema-0.1.6.gem
16
+ $ gem build json-schema.gemspec
17
+ $ gem install json-schema-0.1.13.gem
18
18
  </pre>
19
19
 
20
20
  <pre>
@@ -51,56 +51,21 @@ end
51
51
  </pre>
52
52
 
53
53
 
54
- h2. Currently implemented
55
-
56
- The following core schema definitions are currently implemented:
57
-
58
- * type
59
- * properties
60
- * patternProperties
61
- * additionalProperties
62
- * items
63
- * additionalItems
64
- * required
65
- * dependencies
66
- * minimum
67
- * maximum
68
- * exclusiveMinimum
69
- * exclusiveMaximum
70
- * minItems
71
- * maxItems
72
- * uniqueItems
73
- * pattern
74
- * minLength
75
- * maxLength
76
- * enum
77
- * title
78
- * description
79
- * divisibleBy
80
- * disallow
81
- * extends
82
- * id
83
- * $ref (this implementation only follows slash-delimited fragment resolution)
84
-
85
-
86
- h2. Not implemented
87
-
88
- The following core schema definitions are not implemented:
89
-
90
- * format - Some of the formats listed will be supported, others are fairly vague in their definition and may be left out
54
+ h2. Notes
55
+
56
+ The following core schema attributes are not implemented:
57
+
91
58
  * default - This library aims to solely be a validator and does not modify an object it is validating.
92
59
  * $schema - Support for this will come later, possibly with the ability to validate against other JSON Schema draft versions
93
60
 
94
- In addition, the following hyper schema attributes are not implemented at this time:
95
-
96
- * links
97
- * fragmentResolution (only handles default slash-delimited)
98
- * contentEncoding
99
- * pathStart
61
+ The 'format' attribute is only validated for the following values:
100
62
 
63
+ * date-time
64
+ * date
65
+ * time
66
+ * ip-address
67
+ * ipv6
101
68
 
102
- h2. To Do
69
+ All other 'format' attribute values are simply checked to ensure the instance value is of the correct datatype (e.g., an instance value is validated to be an integer or a float in the case of 'utc-millisec').
103
70
 
104
- * (Much) More testing
105
- * Documentation
106
- * Breaking the current validator out into a subclass, acting as the foundation for supporting multiple versions of validators
71
+ Additionally, JSON::Validator does not handle any json hyperschema attributes.
@@ -3,6 +3,7 @@ require 'open-uri'
3
3
  require 'pathname'
4
4
  require 'bigdecimal'
5
5
  require 'digest/sha1'
6
+ require 'date'
6
7
 
7
8
  module JSON
8
9
 
@@ -48,6 +49,7 @@ module JSON
48
49
  "additionalItems",
49
50
  "dependencies",
50
51
  "extends",
52
+ "format",
51
53
  "$ref"
52
54
  ]
53
55
 
@@ -175,6 +177,108 @@ module JSON
175
177
  end
176
178
 
177
179
 
180
+ # Validate the format of an item
181
+ def validate_format(current_schema, data, fragments)
182
+ case current_schema.schema['format']
183
+
184
+ # Timestamp in restricted ISO-8601 YYYY-MM-DDThh:mm:ssZ
185
+ when 'date-time'
186
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a date/time in the ISO-8601 format of YYYY-MM-DDThh:mm:ssZ"
187
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
188
+ r = Regexp.new('^\d\d\d\d-\d\d-\d\dT(\d\d):(\d\d):(\d\d)Z$')
189
+ if (m = r.match(data))
190
+ parts = data.split("T")
191
+ begin
192
+ Date.parse(parts[0])
193
+ rescue Exception
194
+ raise ValidationError.new(error_message, fragments, current_schema)
195
+ end
196
+ begin
197
+ raise ValidationError.new(error_message, fragments, current_schema) if m[1].to_i > 23
198
+ raise ValidationError.new(error_message, fragments, current_schema) if m[2].to_i > 59
199
+ raise ValidationError.new(error_message, fragments, current_schema) if m[3].to_i > 59
200
+ rescue Exception
201
+ raise ValidationError.new(error_message, fragments, current_schema)
202
+ end
203
+ else
204
+ raise ValidationError.new(error_message, fragments, current_schema)
205
+ end
206
+
207
+ # Date in the format of YYYY-MM-DD
208
+ when 'date'
209
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a date in the format of YYYY-MM-DD"
210
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
211
+ r = Regexp.new('^\d\d\d\d-\d\d-\d\d$')
212
+ if (m = r.match(data))
213
+ begin
214
+ Date.parse(data)
215
+ rescue Exception
216
+ raise ValidationError.new(error_message, fragments, current_schema)
217
+ end
218
+ else
219
+ raise ValidationError.new(error_message, fragments, current_schema)
220
+ end
221
+
222
+ # Time in the format of HH:MM:SS
223
+ when 'time'
224
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a time in the format of hh:mm:ss"
225
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
226
+ r = Regexp.new('^(\d\d):(\d\d):(\d\d)$')
227
+ if (m = r.match(data))
228
+ raise ValidationError.new(error_message, fragments, current_schema) if m[1].to_i > 23
229
+ raise ValidationError.new(error_message, fragments, current_schema) if m[2].to_i > 59
230
+ raise ValidationError.new(error_message, fragments, current_schema) if m[3].to_i > 59
231
+ else
232
+ raise ValidationError.new(error_message, fragments, current_schema)
233
+ end
234
+
235
+ # IPv4 in dotted-quad format
236
+ when 'ip-address', 'ipv4'
237
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a valid IPv4 address"
238
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
239
+ r = Regexp.new('^(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}$')
240
+ if (m = r.match(data))
241
+ 1.upto(4) do |x|
242
+ raise ValidationError.new(error_message, fragments, current_schema) if m[x].to_i > 255
243
+ end
244
+ else
245
+ raise ValidationError.new(error_message, fragments, current_schema)
246
+ end
247
+
248
+ # IPv6 in standard format (including abbreviations)
249
+ when 'ipv6'
250
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a valid IPv6 address"
251
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
252
+ r = Regexp.new('^[a-f0-9:]+$')
253
+ if (m = r.match(data))
254
+ # All characters are valid, now validate structure
255
+ parts = data.split(":")
256
+ raise ValidationError.new(error_message, fragments, current_schema) if parts.length > 8
257
+ condensed_zeros = false
258
+ parts.each do |part|
259
+ if part.length == 0
260
+ raise ValidationError.new(error_message, fragments, current_schema) if condensed_zeros
261
+ condensed_zeros = true
262
+ end
263
+ raise ValidationError.new(error_message, fragments, current_schema) if part.length > 4
264
+ end
265
+ else
266
+ raise ValidationError.new(error_message, fragments, current_schema)
267
+ end
268
+
269
+ # Milliseconds since the epoch. Must be an integer or a float
270
+ when 'utc-millisec'
271
+ error_message = "The property '#{build_fragment(fragments)}' must be an integer or a float"
272
+ raise ValidationError.new(error_message, fragments, current_schema) if (!data.is_a?(Numeric))
273
+
274
+ # Must be a string
275
+ when 'regex','color','style','phone','uri','email','host-name'
276
+ error_message = "The property '#{build_fragment(fragments)}' must be a string"
277
+ raise ValidationError.new(error_message, fragments, current_schema) if (!data.is_a?(String))
278
+ end
279
+ end
280
+
281
+
178
282
  # Validate the minimum value of a number
179
283
  def validate_minimum(current_schema, data, fragments)
180
284
  if data.is_a?(Numeric)
@@ -744,5 +744,206 @@ class JSONSchemaTest < Test::Unit::TestCase
744
744
 
745
745
  end
746
746
 
747
+
748
+ def test_self_reference
749
+ schema = {
750
+ "type" => "object",
751
+ "properties" => { "a" => {"type" => "integer"}, "b" => {"$ref" => "#"}}
752
+ }
753
+
754
+ data = {"a" => 5, "b" => {"b" => {"a" => 1}}}
755
+ assert(JSON::Validator.validate(schema,data))
756
+ data = {"a" => 5, "b" => {"b" => {"a" => 'taco'}}}
757
+ assert(!JSON::Validator.validate(schema,data))
758
+ end
759
+
760
+
761
+ def test_format_ipv4
762
+ schema = {
763
+ "type" => "object",
764
+ "properties" => { "a" => {"type" => "string", "format" => "ip-address"}}
765
+ }
766
+
767
+ data = {"a" => "1.1.1.1"}
768
+ assert(JSON::Validator.validate(schema,data))
769
+ data = {"a" => "1.1.1"}
770
+ assert(!JSON::Validator.validate(schema,data))
771
+ data = {"a" => "1.1.1.300"}
772
+ assert(!JSON::Validator.validate(schema,data))
773
+ data = {"a" => 5}
774
+ assert(!JSON::Validator.validate(schema,data))
775
+ data = {"a" => "1.1.1"}
776
+ assert(!JSON::Validator.validate(schema,data))
777
+ data = {"a" => "1.1.1.1b"}
778
+ assert(!JSON::Validator.validate(schema,data))
779
+ data = {"a" => "b1.1.1.1"}
780
+ end
781
+
782
+
783
+ def test_format_ipv6
784
+ schema = {
785
+ "type" => "object",
786
+ "properties" => { "a" => {"type" => "string", "format" => "ipv6"}}
787
+ }
788
+
789
+ data = {"a" => "1111:2222:8888:9999:aaaa:cccc:eeee:ffff"}
790
+ assert(JSON::Validator.validate(schema,data))
791
+ data = {"a" => "1111:0:8888:0:0:0:eeee:ffff"}
792
+ assert(JSON::Validator.validate(schema,data))
793
+ data = {"a" => "1111:2222:8888::eeee:ffff"}
794
+ assert(JSON::Validator.validate(schema,data))
795
+ data = {"a" => "1111:2222:8888:99999:aaaa:cccc:eeee:ffff"}
796
+ assert(!JSON::Validator.validate(schema,data))
797
+ data = {"a" => "1111:2222:8888:9999:aaaa:cccc:eeee:gggg"}
798
+ assert(!JSON::Validator.validate(schema,data))
799
+ data = {"a" => "1111:2222::9999::cccc:eeee:ffff"}
800
+ assert(!JSON::Validator.validate(schema,data))
801
+ data = {"a" => "1111:2222:8888:9999:aaaa:cccc:eeee:ffff:bbbb"}
802
+ assert(!JSON::Validator.validate(schema,data))
803
+ end
804
+
805
+ def test_format_time
806
+ schema = {
807
+ "type" => "object",
808
+ "properties" => { "a" => {"type" => "string", "format" => "time"}}
809
+ }
810
+
811
+ data = {"a" => "12:00:00"}
812
+ assert(JSON::Validator.validate(schema,data))
813
+ data = {"a" => "12:00"}
814
+ assert(!JSON::Validator.validate(schema,data))
815
+ data = {"a" => "12:00:60"}
816
+ assert(!JSON::Validator.validate(schema,data))
817
+ data = {"a" => "12:60:00"}
818
+ assert(!JSON::Validator.validate(schema,data))
819
+ data = {"a" => "24:00:00"}
820
+ assert(!JSON::Validator.validate(schema,data))
821
+ data = {"a" => "0:00:00"}
822
+ assert(!JSON::Validator.validate(schema,data))
823
+ data = {"a" => "-12:00:00"}
824
+ assert(!JSON::Validator.validate(schema,data))
825
+ data = {"a" => "12:00:00b"}
826
+ assert(!JSON::Validator.validate(schema,data))
827
+ end
828
+
829
+
830
+ def test_format_date
831
+ schema = {
832
+ "type" => "object",
833
+ "properties" => { "a" => {"type" => "string", "format" => "date"}}
834
+ }
835
+
836
+ data = {"a" => "2010-01-01"}
837
+ assert(JSON::Validator.validate(schema,data))
838
+ data = {"a" => "2010-01-32"}
839
+ assert(!JSON::Validator.validate(schema,data))
840
+ data = {"a" => "n2010-01-01"}
841
+ assert(!JSON::Validator.validate(schema,data))
842
+ data = {"a" => "2010-1-01"}
843
+ assert(!JSON::Validator.validate(schema,data))
844
+ data = {"a" => "2010-01-1"}
845
+ assert(!JSON::Validator.validate(schema,data))
846
+ data = {"a" => "2010-01-01n"}
847
+ assert(!JSON::Validator.validate(schema,data))
848
+ end
849
+
850
+ def test_format_datetime
851
+ schema = {
852
+ "type" => "object",
853
+ "properties" => { "a" => {"type" => "string", "format" => "date-time"}}
854
+ }
855
+
856
+ data = {"a" => "2010-01-01T12:00:00Z"}
857
+ assert(JSON::Validator.validate(schema,data))
858
+ data = {"a" => "2010-01-32T12:00:00Z"}
859
+ assert(!JSON::Validator.validate(schema,data))
860
+ data = {"a" => "2010-13-01T12:00:00Z"}
861
+ assert(!JSON::Validator.validate(schema,data))
862
+ data = {"a" => "2010-01-01T24:00:00Z"}
863
+ assert(!JSON::Validator.validate(schema,data))
864
+ data = {"a" => "2010-01-01T12:60:00Z"}
865
+ assert(!JSON::Validator.validate(schema,data))
866
+ data = {"a" => "2010-01-01T12:00:60Z"}
867
+ assert(!JSON::Validator.validate(schema,data))
868
+ data = {"a" => "2010-01-01T12:00:00"}
869
+ assert(!JSON::Validator.validate(schema,data))
870
+ data = {"a" => "2010-01-01T12:00:00z"}
871
+ assert(!JSON::Validator.validate(schema,data))
872
+ data = {"a" => "2010-01-0112:00:00Z"}
873
+ assert(!JSON::Validator.validate(schema,data))
874
+ end
875
+
876
+
877
+ def test_format_strings
878
+ data1 = {"a" => "boo"}
879
+ data2 = {"a" => 5}
880
+
881
+ schema = {
882
+ "type" => "object",
883
+ "properties" => { "a" => {"format" => "regex"}}
884
+ }
885
+ assert(JSON::Validator.validate(schema,data1))
886
+ assert(!JSON::Validator.validate(schema,data2))
887
+
888
+ schema = {
889
+ "type" => "object",
890
+ "properties" => { "a" => {"format" => "color"}}
891
+ }
892
+ assert(JSON::Validator.validate(schema,data1))
893
+ assert(!JSON::Validator.validate(schema,data2))
894
+
895
+ schema = {
896
+ "type" => "object",
897
+ "properties" => { "a" => {"format" => "style"}}
898
+ }
899
+ assert(JSON::Validator.validate(schema,data1))
900
+ assert(!JSON::Validator.validate(schema,data2))
901
+
902
+ schema = {
903
+ "type" => "object",
904
+ "properties" => { "a" => {"format" => "phone"}}
905
+ }
906
+ assert(JSON::Validator.validate(schema,data1))
907
+ assert(!JSON::Validator.validate(schema,data2))
908
+
909
+ schema = {
910
+ "type" => "object",
911
+ "properties" => { "a" => {"format" => "uri"}}
912
+ }
913
+ assert(JSON::Validator.validate(schema,data1))
914
+ assert(!JSON::Validator.validate(schema,data2))
915
+
916
+ schema = {
917
+ "type" => "object",
918
+ "properties" => { "a" => {"format" => "email"}}
919
+ }
920
+ assert(JSON::Validator.validate(schema,data1))
921
+ assert(!JSON::Validator.validate(schema,data2))
922
+
923
+ schema = {
924
+ "type" => "object",
925
+ "properties" => { "a" => {"format" => "host-name"}}
926
+ }
927
+ assert(JSON::Validator.validate(schema,data1))
928
+ assert(!JSON::Validator.validate(schema,data2))
929
+ end
930
+
931
+
932
+ def test_format_numeric
933
+ data1 = {"a" => "boo"}
934
+ data2 = {"a" => 5}
935
+ data3 = {"a" => 5.4}
936
+
937
+ schema = {
938
+ "type" => "object",
939
+ "properties" => { "a" => {"format" => "utc-millisec"}}
940
+ }
941
+ assert(!JSON::Validator.validate(schema,data1))
942
+ assert(JSON::Validator.validate(schema,data2))
943
+ assert(JSON::Validator.validate(schema,data3))
944
+ end
945
+
946
+
947
+
747
948
  end
748
949
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-schema
3
3
  version: !ruby/object:Gem::Version
4
- hash: 3
4
+ hash: 1
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 12
10
- version: 0.1.12
9
+ - 13
10
+ version: 0.1.13
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kenny Hoxworth
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-15 00:00:00 -05:00
18
+ date: 2011-02-16 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -45,9 +45,9 @@ files:
45
45
  - lib/json-schema/uri/file.rb
46
46
  - lib/json-schema/validator.rb
47
47
  - lib/json-schema.rb
48
+ - README.textile
48
49
  - test/test_files.rb
49
50
  - test/test_jsonschema.rb
50
- - README.textile
51
51
  has_rdoc: true
52
52
  homepage: http://github.com/hoxworth/json-schema/tree/master
53
53
  licenses: []