json_pure 1.0.0 → 1.4.6
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.
- data/CHANGES +155 -1
- data/COPYING +58 -0
- data/GPL +7 -7
- data/README +324 -45
- data/Rakefile +166 -124
- data/TODO +1 -1
- data/VERSION +1 -1
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log +52 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat +1000 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat +1001 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat +900 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat +901 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat +1000 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat +1001 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log +261 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat +1000 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat +1001 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat +1000 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat +1001 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat +1000 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat +1001 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log +262 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat +1000 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat +1001 -0
- data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log +82 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log +34 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat +900 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat +901 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log +81 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat +1000 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat +1001 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log +82 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat +1000 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat +1001 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log +82 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat +1000 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat +1001 -0
- data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log +82 -0
- data/benchmarks/generator2_benchmark.rb +222 -0
- data/benchmarks/generator_benchmark.rb +224 -0
- data/benchmarks/ohai.json +1216 -0
- data/benchmarks/ohai.ruby +1 -0
- data/benchmarks/parser2_benchmark.rb +251 -0
- data/benchmarks/parser_benchmark.rb +259 -0
- data/bin/edit_json.rb +1 -3
- data/bin/prettify_json.rb +75 -0
- data/data/index.html +5 -4
- data/data/prototype.js +2764 -1095
- data/ext/json/ext/generator/extconf.rb +14 -3
- data/ext/json/ext/generator/generator.c +1022 -334
- data/ext/json/ext/generator/generator.h +197 -0
- data/ext/json/ext/parser/extconf.rb +9 -3
- data/ext/json/ext/parser/parser.c +961 -577
- data/ext/json/ext/parser/parser.h +71 -0
- data/ext/json/ext/parser/parser.rl +400 -123
- data/install.rb +0 -0
- data/lib/json/add/core.rb +148 -0
- data/lib/json/add/rails.rb +58 -0
- data/lib/json/common.rb +254 -47
- data/lib/json/editor.rb +236 -72
- data/lib/json/ext.rb +2 -0
- data/lib/json/pure/generator.rb +235 -117
- data/lib/json/pure/parser.rb +124 -25
- data/lib/json/pure.rb +5 -3
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +2 -197
- data/tests/fixtures/fail18.json +1 -0
- data/tests/test_json.rb +181 -22
- data/tests/test_json_addition.rb +84 -16
- data/tests/test_json_encoding.rb +68 -0
- data/tests/test_json_fixtures.rb +9 -5
- data/tests/test_json_generate.rb +114 -14
- data/tests/test_json_rails.rb +144 -0
- data/tests/test_json_unicode.rb +35 -14
- data/tools/fuzz.rb +13 -7
- data/tools/server.rb +0 -1
- metadata +156 -122
- data/benchmarks/benchmark.txt +0 -133
- data/benchmarks/benchmark_generator.rb +0 -44
- data/benchmarks/benchmark_parser.rb +0 -22
- data/benchmarks/benchmark_rails.rb +0 -26
- data/ext/json/ext/generator/Makefile +0 -149
- data/ext/json/ext/generator/unicode.c +0 -184
- data/ext/json/ext/generator/unicode.h +0 -40
- data/ext/json/ext/parser/Makefile +0 -149
- data/ext/json/ext/parser/unicode.c +0 -156
- data/ext/json/ext/parser/unicode.h +0 -44
- data/tests/fixtures/pass18.json +0 -1
- data/tests/runner.rb +0 -24
- /data/tests/fixtures/{fail15.json → pass15.json} +0 -0
- /data/tests/fixtures/{fail16.json → pass16.json} +0 -0
- /data/tests/fixtures/{fail17.json → pass17.json} +0 -0
- /data/tests/fixtures/{fail26.json → pass26.json} +0 -0
data/lib/json/pure.rb
CHANGED
|
@@ -10,6 +10,9 @@ module JSON
|
|
|
10
10
|
# An iconv instance to convert from UTF16 Big Endian to UTF8.
|
|
11
11
|
UTF8toUTF16 = Iconv.new('utf-16be', 'utf-8') # :nodoc:
|
|
12
12
|
UTF8toUTF16.iconv('no bom')
|
|
13
|
+
rescue LoadError
|
|
14
|
+
raise MissingUnicodeSupport,
|
|
15
|
+
"iconv couldn't be loaded, which is required for UTF-8/UTF-16 conversions"
|
|
13
16
|
rescue Errno::EINVAL, Iconv::InvalidEncoding
|
|
14
17
|
# Iconv doesn't support big endian utf-16. Let's try to hack this manually
|
|
15
18
|
# into the converters.
|
|
@@ -51,9 +54,6 @@ module JSON
|
|
|
51
54
|
ensure
|
|
52
55
|
$VERBOSE = old_verbose
|
|
53
56
|
end
|
|
54
|
-
rescue LoadError
|
|
55
|
-
raise MissingUnicodeSupport,
|
|
56
|
-
"iconv couldn't be loaded, which is required for UTF-8/UTF-16 conversions"
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
# Swap consecutive bytes of _string_ in place.
|
|
@@ -72,4 +72,6 @@ module JSON
|
|
|
72
72
|
JSON.parser = Parser
|
|
73
73
|
JSON.generator = Generator
|
|
74
74
|
end
|
|
75
|
+
|
|
76
|
+
JSON_LOADED = true
|
|
75
77
|
end
|
data/lib/json/version.rb
CHANGED
data/lib/json.rb
CHANGED
|
@@ -1,202 +1,7 @@
|
|
|
1
1
|
require 'json/common'
|
|
2
|
-
# = json - JSON for Ruby
|
|
3
|
-
#
|
|
4
|
-
# == Description
|
|
5
|
-
#
|
|
6
|
-
# This is a implementation of the JSON specification according to RFC 4627
|
|
7
|
-
# (http://www.ietf.org/rfc/rfc4627.txt). Starting from version 1.0.0 on there
|
|
8
|
-
# will be two variants available:
|
|
9
|
-
#
|
|
10
|
-
# * A pure ruby variant, that relies on the iconv and the stringscan
|
|
11
|
-
# extensions, which are both part of the ruby standard library.
|
|
12
|
-
# * The quite a bit faster C extension variant, which is in parts implemented
|
|
13
|
-
# in C and comes with its own unicode conversion functions and a parser
|
|
14
|
-
# generated by the ragel state machine compiler
|
|
15
|
-
# (http://www.cs.queensu.ca/~thurston/ragel).
|
|
16
|
-
#
|
|
17
|
-
# Both variants of the JSON generator escape all non-ASCII an control
|
|
18
|
-
# characters with \uXXXX escape sequences, and support UTF-16 surrogate pairs
|
|
19
|
-
# in order to be able to generate the whole range of unicode code points. This
|
|
20
|
-
# means that generated JSON text is encoded as UTF-8 (because ASCII is a subset
|
|
21
|
-
# of UTF-8) and at the same time avoids decoding problems for receiving
|
|
22
|
-
# endpoints, that don't expect UTF-8 encoded texts. On the negative side this
|
|
23
|
-
# may lead to a bit longer strings than necessarry.
|
|
24
|
-
#
|
|
25
|
-
# All strings, that are to be encoded as JSON strings, should be UTF-8 byte
|
|
26
|
-
# sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8
|
|
27
|
-
# encoded, please use the to_json_raw_object method of String (which produces
|
|
28
|
-
# an object, that contains a byte array) and decode the result on the receiving
|
|
29
|
-
# endpoint.
|
|
30
|
-
#
|
|
31
|
-
# == Author
|
|
32
|
-
#
|
|
33
|
-
# Florian Frank <mailto:flori@ping.de>
|
|
34
|
-
#
|
|
35
|
-
# == License
|
|
36
|
-
#
|
|
37
|
-
# This is free software; you can redistribute it and/or modify it under the
|
|
38
|
-
# terms of the GNU General Public License Version 2 as published by the Free
|
|
39
|
-
# Software Foundation: www.gnu.org/copyleft/gpl.html
|
|
40
|
-
#
|
|
41
|
-
# == Download
|
|
42
|
-
#
|
|
43
|
-
# The latest version of this library can be downloaded at
|
|
44
|
-
#
|
|
45
|
-
# * http://rubyforge.org/frs?group_id=953
|
|
46
|
-
#
|
|
47
|
-
# Online Documentation should be located at
|
|
48
|
-
#
|
|
49
|
-
# * http://json.rubyforge.org
|
|
50
|
-
#
|
|
51
|
-
# == Speed Comparisons
|
|
52
|
-
#
|
|
53
|
-
# I have created some benchmark results (see the benchmarks subdir of the
|
|
54
|
-
# package) for the JSON-Parser to estimate the speed up in the C extension:
|
|
55
|
-
#
|
|
56
|
-
# JSON::Pure::Parser:: 28.90 calls/second
|
|
57
|
-
# JSON::Ext::Parser:: 505.50 calls/second
|
|
58
|
-
#
|
|
59
|
-
# This is ca. <b>17.5</b> times the speed of the pure Ruby implementation.
|
|
60
|
-
#
|
|
61
|
-
# I have benchmarked the JSON-Generator as well. This generates a few more
|
|
62
|
-
# values, because there are different modes, that also influence the achieved
|
|
63
|
-
# speed:
|
|
64
|
-
#
|
|
65
|
-
# * JSON::Pure::Generator:
|
|
66
|
-
# generate:: 35.06 calls/second
|
|
67
|
-
# pretty_generate:: 34.00 calls/second
|
|
68
|
-
# fast_generate:: 41.06 calls/second
|
|
69
|
-
#
|
|
70
|
-
# * JSON::Ext::Generator:
|
|
71
|
-
# generate:: 492.11 calls/second
|
|
72
|
-
# pretty_generate:: 348.85 calls/second
|
|
73
|
-
# fast_generate:: 541.60 calls/second
|
|
74
|
-
#
|
|
75
|
-
# * Speedup Ext/Pure:
|
|
76
|
-
# generate safe:: 14.0 times
|
|
77
|
-
# generate pretty:: 10.3 times
|
|
78
|
-
# generate fast:: 13.2 times
|
|
79
|
-
#
|
|
80
|
-
# The rails framework includes a generator as well, also it seems to be rather
|
|
81
|
-
# slow: I measured only 23.87 calls/second which is slower than any of my pure
|
|
82
|
-
# generator results. Here a comparison of the different speedups with the Rails
|
|
83
|
-
# measurement as the divisor:
|
|
84
|
-
#
|
|
85
|
-
# * Speedup Pure/Rails:
|
|
86
|
-
# generate safe:: 1.5 times
|
|
87
|
-
# generate pretty:: 1.4 times
|
|
88
|
-
# generate fast:: 1.7 times
|
|
89
|
-
#
|
|
90
|
-
# * Speedup Ext/Rails:
|
|
91
|
-
# generate safe:: 20.6 times
|
|
92
|
-
# generate pretty:: 14.6 times
|
|
93
|
-
# generate fast:: 22.7 times
|
|
94
|
-
#
|
|
95
|
-
# To achieve the fastest JSON text output, you can use the
|
|
96
|
-
# fast_generate/fast_unparse methods. Beware, that this will disable the
|
|
97
|
-
# checking for circular Ruby data structures, which may cause JSON to go into
|
|
98
|
-
# an infinite loop.
|
|
99
|
-
#
|
|
100
|
-
# == Examples
|
|
101
|
-
#
|
|
102
|
-
# To create a JSON text from a ruby data structure, you
|
|
103
|
-
# can call JSON.generate (or JSON.unparse) like that:
|
|
104
|
-
#
|
|
105
|
-
# json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
|
106
|
-
# # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
|
|
107
|
-
#
|
|
108
|
-
# It's also possible to call the #to_json method directly.
|
|
109
|
-
#
|
|
110
|
-
# json = [1, 2, {"a"=>3.141}, false, true, nil, 4..10].to_json
|
|
111
|
-
# # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
|
|
112
|
-
#
|
|
113
|
-
# To create a valid JSON text you have to make sure, that the output is
|
|
114
|
-
# embedded in either a JSON array [] or a JSON object {}. The easiest way to do
|
|
115
|
-
# this, is by putting your values in a Ruby Array or Hash instance.
|
|
116
|
-
#
|
|
117
|
-
# To get back a ruby data structure from a JSON text, you have to call
|
|
118
|
-
# JSON.parse on it:
|
|
119
|
-
#
|
|
120
|
-
# JSON.parse json
|
|
121
|
-
# # => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
|
|
122
|
-
#
|
|
123
|
-
# Note, that the range from the original data structure is a simple
|
|
124
|
-
# string now. The reason for this is, that JSON doesn't support ranges
|
|
125
|
-
# or arbitrary classes. In this case the json library falls back to call
|
|
126
|
-
# Object#to_json, which is the same as #to_s.to_json.
|
|
127
|
-
#
|
|
128
|
-
# It's possible to extend JSON to support serialization of arbitrary classes by
|
|
129
|
-
# simply implementing a more specialized version of the #to_json method, that
|
|
130
|
-
# should return a JSON object (a hash converted to JSON with #to_json)
|
|
131
|
-
# like this (don't forget the *a for all the arguments):
|
|
132
|
-
#
|
|
133
|
-
# class Range
|
|
134
|
-
# def to_json(*a)
|
|
135
|
-
# {
|
|
136
|
-
# 'json_class' => self.class.name, # = 'Range'
|
|
137
|
-
# 'data' => [ first, last, exclude_end? ]
|
|
138
|
-
# }.to_json(*a)
|
|
139
|
-
# end
|
|
140
|
-
# end
|
|
141
|
-
#
|
|
142
|
-
# The hash key 'json_class' is the class, that will be asked to deserialize the
|
|
143
|
-
# JSON representation later. In this case it's 'Range', but any namespace of
|
|
144
|
-
# the form 'A::B' or '::A::B' will do. All other keys are arbitrary and can be
|
|
145
|
-
# used to store the necessary data to configure the object to be deserialized.
|
|
146
|
-
#
|
|
147
|
-
# If a the key 'json_class' is found in a JSON object, the JSON parser checks
|
|
148
|
-
# if the given class responds to the json_create class method. If so, it is
|
|
149
|
-
# called with the JSON object converted to a Ruby hash. So a range can
|
|
150
|
-
# be deserialized by implementing Range.json_create like this:
|
|
151
|
-
#
|
|
152
|
-
# class Range
|
|
153
|
-
# def self.json_create(o)
|
|
154
|
-
# new(*o['data'])
|
|
155
|
-
# end
|
|
156
|
-
# end
|
|
157
|
-
#
|
|
158
|
-
# Now it possible to serialize/deserialize ranges as well:
|
|
159
|
-
#
|
|
160
|
-
# json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
|
161
|
-
# # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
|
|
162
|
-
# JSON.parse json
|
|
163
|
-
# # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
|
164
|
-
#
|
|
165
|
-
# JSON.generate always creates the shortest possible string representation of a
|
|
166
|
-
# ruby data structure in one line. This good for data storage or network
|
|
167
|
-
# protocols, but not so good for humans to read. Fortunately there's also
|
|
168
|
-
# JSON.pretty_generate (or JSON.pretty_generate) that creates a more
|
|
169
|
-
# readable output:
|
|
170
|
-
#
|
|
171
|
-
# puts JSON.pretty_generate([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
|
|
172
|
-
# [
|
|
173
|
-
# 1,
|
|
174
|
-
# 2,
|
|
175
|
-
# {
|
|
176
|
-
# "a": 3.141
|
|
177
|
-
# },
|
|
178
|
-
# false,
|
|
179
|
-
# true,
|
|
180
|
-
# null,
|
|
181
|
-
# {
|
|
182
|
-
# "json_class": "Range",
|
|
183
|
-
# "data": [
|
|
184
|
-
# 4,
|
|
185
|
-
# 10,
|
|
186
|
-
# false
|
|
187
|
-
# ]
|
|
188
|
-
# }
|
|
189
|
-
# ]
|
|
190
|
-
#
|
|
191
|
-
# There are also the methods Kernel#j for unparse, and Kernel#jj for
|
|
192
|
-
# pretty_unparse output to the console, that work analogous to Core Ruby's p
|
|
193
|
-
# and the pp library's pp methods.
|
|
194
|
-
#
|
|
195
|
-
# The script tools/server.rb contains a small example if you want to test, how
|
|
196
|
-
# receiving a JSON object from a webrick server in your browser with the
|
|
197
|
-
# javasript prototype library (http://www.prototypejs.org) works.
|
|
198
|
-
#
|
|
199
2
|
module JSON
|
|
3
|
+
require 'json/version'
|
|
4
|
+
|
|
200
5
|
begin
|
|
201
6
|
require 'json/ext'
|
|
202
7
|
rescue LoadError
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]
|
data/tests/test_json.rb
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
2
3
|
|
|
3
4
|
require 'test/unit'
|
|
4
|
-
|
|
5
|
+
case ENV['JSON']
|
|
6
|
+
when 'pure' then require 'json/pure'
|
|
7
|
+
when 'ext' then require 'json/ext'
|
|
8
|
+
else require 'json'
|
|
9
|
+
end
|
|
10
|
+
require 'stringio'
|
|
11
|
+
|
|
12
|
+
unless Array.method_defined?(:permutation)
|
|
13
|
+
begin
|
|
14
|
+
require 'enumerator'
|
|
15
|
+
require 'permutation'
|
|
16
|
+
class Array
|
|
17
|
+
def permutation
|
|
18
|
+
Permutation.for(self).to_enum.map { |x| x.project }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
rescue LoadError
|
|
22
|
+
warn "Skipping permutation tests."
|
|
23
|
+
end
|
|
24
|
+
end
|
|
5
25
|
|
|
6
26
|
class TC_JSON < Test::Unit::TestCase
|
|
7
27
|
include JSON
|
|
8
28
|
|
|
9
29
|
def setup
|
|
10
|
-
$KCODE = 'UTF8'
|
|
11
30
|
@ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true].map do
|
|
12
31
|
|x| [x]
|
|
13
32
|
end
|
|
@@ -25,10 +44,9 @@ class TC_JSON < Test::Unit::TestCase
|
|
|
25
44
|
'h' => 1000.0,
|
|
26
45
|
'i' => 0.001
|
|
27
46
|
}
|
|
28
|
-
@json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'
|
|
47
|
+
@json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\
|
|
29
48
|
'"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}'
|
|
30
49
|
end
|
|
31
|
-
suite << TC_JSON.suite
|
|
32
50
|
|
|
33
51
|
def test_construction
|
|
34
52
|
parser = JSON::Parser.new('test')
|
|
@@ -48,6 +66,7 @@ class TC_JSON < Test::Unit::TestCase
|
|
|
48
66
|
assert_equal([-23], parse('[-23]'))
|
|
49
67
|
assert_equal([23], parse('[23]'))
|
|
50
68
|
assert_equal([0.23], parse('[0.23]'))
|
|
69
|
+
assert_equal([0.0], parse('[0e0]'))
|
|
51
70
|
assert_raises(JSON::ParserError) { parse('[+23.2]') }
|
|
52
71
|
assert_raises(JSON::ParserError) { parse('[+23]') }
|
|
53
72
|
assert_raises(JSON::ParserError) { parse('[.23]') }
|
|
@@ -60,6 +79,12 @@ class TC_JSON < Test::Unit::TestCase
|
|
|
60
79
|
assert_equal_float [3.141], parse('[3141.0E-3]')
|
|
61
80
|
assert_equal_float [-3.141], parse('[-3141.0e-3]')
|
|
62
81
|
assert_equal_float [-3.141], parse('[-3141e-3]')
|
|
82
|
+
assert_raises(ParserError) { parse('[NaN]') }
|
|
83
|
+
assert parse('[NaN]', :allow_nan => true).first.nan?
|
|
84
|
+
assert_raises(ParserError) { parse('[Infinity]') }
|
|
85
|
+
assert_equal [1.0/0], parse('[Infinity]', :allow_nan => true)
|
|
86
|
+
assert_raises(ParserError) { parse('[-Infinity]') }
|
|
87
|
+
assert_equal [-1.0/0], parse('[-Infinity]', :allow_nan => true)
|
|
63
88
|
assert_equal([""], parse('[""]'))
|
|
64
89
|
assert_equal(["foobar"], parse('["foobar"]'))
|
|
65
90
|
assert_equal([{}], parse('[{}]'))
|
|
@@ -83,30 +108,24 @@ class TC_JSON < Test::Unit::TestCase
|
|
|
83
108
|
assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } '))
|
|
84
109
|
end
|
|
85
110
|
|
|
86
|
-
|
|
87
|
-
require 'permutation'
|
|
111
|
+
if Array.method_defined?(:permutation)
|
|
88
112
|
def test_parse_more_complex_arrays
|
|
89
113
|
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
json = pretty_generate(orig_ary)
|
|
94
|
-
assert_equal orig_ary, parse(json)
|
|
114
|
+
a.permutation.each do |perm|
|
|
115
|
+
json = pretty_generate(perm)
|
|
116
|
+
assert_equal perm, parse(json)
|
|
95
117
|
end
|
|
96
118
|
end
|
|
97
119
|
|
|
98
120
|
def test_parse_complex_objects
|
|
99
121
|
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
|
|
100
|
-
|
|
101
|
-
perms.each do |perm|
|
|
122
|
+
a.permutation.each do |perm|
|
|
102
123
|
s = "a"
|
|
103
|
-
orig_obj = perm.
|
|
124
|
+
orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
|
|
104
125
|
json = pretty_generate(orig_obj)
|
|
105
126
|
assert_equal orig_obj, parse(json)
|
|
106
127
|
end
|
|
107
128
|
end
|
|
108
|
-
rescue LoadError
|
|
109
|
-
warn "Skipping permutation tests."
|
|
110
129
|
end
|
|
111
130
|
|
|
112
131
|
def test_parse_arrays
|
|
@@ -139,6 +158,28 @@ class TC_JSON < Test::Unit::TestCase
|
|
|
139
158
|
, [2718.0E-3 ],\r[ null] , [[1, -2, 3 ]], [false ],[ true]\n ] }))
|
|
140
159
|
end
|
|
141
160
|
|
|
161
|
+
class SubArray < Array; end
|
|
162
|
+
|
|
163
|
+
class SubArray2 < Array
|
|
164
|
+
def to_json(*a)
|
|
165
|
+
{
|
|
166
|
+
JSON.create_id => self.class.name,
|
|
167
|
+
'ary' => to_a,
|
|
168
|
+
}.to_json(*a)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def self.json_create(o)
|
|
172
|
+
o.delete JSON.create_id
|
|
173
|
+
o['ary']
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def test_parse_array_custom_class
|
|
178
|
+
res = parse('[]', :array_class => SubArray)
|
|
179
|
+
assert_equal([], res)
|
|
180
|
+
assert_equal(SubArray, res.class)
|
|
181
|
+
end
|
|
182
|
+
|
|
142
183
|
def test_parse_object
|
|
143
184
|
assert_equal({}, parse('{}'))
|
|
144
185
|
assert_equal({}, parse(' { } '))
|
|
@@ -146,6 +187,54 @@ class TC_JSON < Test::Unit::TestCase
|
|
|
146
187
|
assert_equal({'foo'=>'bar'}, parse(' { "foo" : "bar" } '))
|
|
147
188
|
end
|
|
148
189
|
|
|
190
|
+
class SubHash < Hash
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
class SubHash2 < Hash
|
|
194
|
+
def to_json(*a)
|
|
195
|
+
{
|
|
196
|
+
JSON.create_id => self.class.name,
|
|
197
|
+
}.merge(self).to_json(*a)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def self.json_create(o)
|
|
201
|
+
o.delete JSON.create_id
|
|
202
|
+
self[o]
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_parse_object_custom_class
|
|
207
|
+
res = parse('{}', :object_class => SubHash2)
|
|
208
|
+
assert_equal({}, res)
|
|
209
|
+
assert_equal(SubHash2, res.class)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def test_generation_of_core_subclasses_with_new_to_json
|
|
213
|
+
obj = SubHash2["foo" => SubHash2["bar" => true]]
|
|
214
|
+
obj_json = JSON(obj)
|
|
215
|
+
obj_again = JSON(obj_json)
|
|
216
|
+
assert_kind_of SubHash2, obj_again
|
|
217
|
+
assert_kind_of SubHash2, obj_again['foo']
|
|
218
|
+
assert obj_again['foo']['bar']
|
|
219
|
+
assert_equal obj, obj_again
|
|
220
|
+
assert_equal ["foo"], JSON(JSON(SubArray2["foo"]))
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def test_generation_of_core_subclasses_with_default_to_json
|
|
224
|
+
assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"])
|
|
225
|
+
assert_equal '["foo"]', JSON(SubArray["foo"])
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def test_generation_of_core_subclasses
|
|
229
|
+
obj = SubHash["foo" => SubHash["bar" => true]]
|
|
230
|
+
obj_json = JSON(obj)
|
|
231
|
+
obj_again = JSON(obj_json)
|
|
232
|
+
assert_kind_of Hash, obj_again
|
|
233
|
+
assert_kind_of Hash, obj_again['foo']
|
|
234
|
+
assert obj_again['foo']['bar']
|
|
235
|
+
assert_equal obj, obj_again
|
|
236
|
+
end
|
|
237
|
+
|
|
149
238
|
def test_parser_reset
|
|
150
239
|
parser = Parser.new(@json)
|
|
151
240
|
assert_equal(@hash, parser.parse)
|
|
@@ -195,23 +284,27 @@ EOT
|
|
|
195
284
|
def test_backslash
|
|
196
285
|
data = [ '\\.(?i:gif|jpe?g|png)$' ]
|
|
197
286
|
json = '["\\\\.(?i:gif|jpe?g|png)$"]'
|
|
198
|
-
assert_equal json, JSON.
|
|
287
|
+
assert_equal json, JSON.generate(data)
|
|
199
288
|
assert_equal data, JSON.parse(json)
|
|
200
289
|
#
|
|
201
290
|
data = [ '\\"' ]
|
|
202
291
|
json = '["\\\\\""]'
|
|
203
|
-
assert_equal json, JSON.
|
|
292
|
+
assert_equal json, JSON.generate(data)
|
|
204
293
|
assert_equal data, JSON.parse(json)
|
|
205
294
|
#
|
|
206
|
-
json = '["
|
|
295
|
+
json = '["/"]'
|
|
207
296
|
data = JSON.parse(json)
|
|
208
297
|
assert_equal ['/'], data
|
|
209
|
-
assert_equal json, JSON.
|
|
298
|
+
assert_equal json, JSON.generate(data)
|
|
210
299
|
#
|
|
211
300
|
json = '["\""]'
|
|
212
301
|
data = JSON.parse(json)
|
|
213
302
|
assert_equal ['"'], data
|
|
214
|
-
assert_equal json, JSON.
|
|
303
|
+
assert_equal json, JSON.generate(data)
|
|
304
|
+
json = '["\\\'"]'
|
|
305
|
+
data = JSON.parse(json)
|
|
306
|
+
assert_equal ["'"], data
|
|
307
|
+
assert_equal '["\'"]', JSON.generate(data)
|
|
215
308
|
end
|
|
216
309
|
|
|
217
310
|
def test_wrong_inputs
|
|
@@ -231,5 +324,71 @@ EOT
|
|
|
231
324
|
assert_raises(ParserError) { JSON.parse('[1.]') }
|
|
232
325
|
assert_raises(ParserError) { JSON.parse(' ') }
|
|
233
326
|
end
|
|
327
|
+
|
|
328
|
+
def test_nesting
|
|
329
|
+
assert_raises(JSON::NestingError) { JSON.parse '[[]]', :max_nesting => 1 }
|
|
330
|
+
assert_raises(JSON::NestingError) { JSON.parser.new('[[]]', :max_nesting => 1).parse }
|
|
331
|
+
assert_equal [[]], JSON.parse('[[]]', :max_nesting => 2)
|
|
332
|
+
too_deep = '[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]'
|
|
333
|
+
too_deep_ary = eval too_deep
|
|
334
|
+
assert_raises(JSON::NestingError) { JSON.parse too_deep }
|
|
335
|
+
assert_raises(JSON::NestingError) { JSON.parser.new(too_deep).parse }
|
|
336
|
+
assert_raises(JSON::NestingError) { JSON.parse too_deep, :max_nesting => 19 }
|
|
337
|
+
ok = JSON.parse too_deep, :max_nesting => 20
|
|
338
|
+
assert_equal too_deep_ary, ok
|
|
339
|
+
ok = JSON.parse too_deep, :max_nesting => nil
|
|
340
|
+
assert_equal too_deep_ary, ok
|
|
341
|
+
ok = JSON.parse too_deep, :max_nesting => false
|
|
342
|
+
assert_equal too_deep_ary, ok
|
|
343
|
+
ok = JSON.parse too_deep, :max_nesting => 0
|
|
344
|
+
assert_equal too_deep_ary, ok
|
|
345
|
+
assert_raises(JSON::NestingError) { JSON.generate [[]], :max_nesting => 1 }
|
|
346
|
+
assert_equal '[[]]', JSON.generate([[]], :max_nesting => 2)
|
|
347
|
+
assert_raises(JSON::NestingError) { JSON.generate too_deep_ary }
|
|
348
|
+
assert_raises(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 19 }
|
|
349
|
+
ok = JSON.generate too_deep_ary, :max_nesting => 20
|
|
350
|
+
assert_equal too_deep, ok
|
|
351
|
+
ok = JSON.generate too_deep_ary, :max_nesting => nil
|
|
352
|
+
assert_equal too_deep, ok
|
|
353
|
+
ok = JSON.generate too_deep_ary, :max_nesting => false
|
|
354
|
+
assert_equal too_deep, ok
|
|
355
|
+
ok = JSON.generate too_deep_ary, :max_nesting => 0
|
|
356
|
+
assert_equal too_deep, ok
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def test_symbolize_names
|
|
360
|
+
assert_equal({ "foo" => "bar", "baz" => "quux" },
|
|
361
|
+
JSON.parse('{"foo":"bar", "baz":"quux"}'))
|
|
362
|
+
assert_equal({ :foo => "bar", :baz => "quux" },
|
|
363
|
+
JSON.parse('{"foo":"bar", "baz":"quux"}', :symbolize_names => true))
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def test_load_dump
|
|
367
|
+
too_deep = '[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]'
|
|
368
|
+
assert_equal too_deep, JSON.dump(eval(too_deep))
|
|
369
|
+
assert_kind_of String, Marshal.dump(eval(too_deep))
|
|
370
|
+
assert_raises(ArgumentError) { JSON.dump(eval(too_deep), 19) }
|
|
371
|
+
assert_raises(ArgumentError) { Marshal.dump(eval(too_deep), 19) }
|
|
372
|
+
assert_equal too_deep, JSON.dump(eval(too_deep), 20)
|
|
373
|
+
assert_kind_of String, Marshal.dump(eval(too_deep), 20)
|
|
374
|
+
output = StringIO.new
|
|
375
|
+
JSON.dump(eval(too_deep), output)
|
|
376
|
+
assert_equal too_deep, output.string
|
|
377
|
+
output = StringIO.new
|
|
378
|
+
JSON.dump(eval(too_deep), output, 20)
|
|
379
|
+
assert_equal too_deep, output.string
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def test_big_integers
|
|
383
|
+
json1 = JSON([orig = (1 << 31) - 1])
|
|
384
|
+
assert_equal orig, JSON[json1][0]
|
|
385
|
+
json2 = JSON([orig = 1 << 31])
|
|
386
|
+
assert_equal orig, JSON[json2][0]
|
|
387
|
+
json3 = JSON([orig = (1 << 62) - 1])
|
|
388
|
+
assert_equal orig, JSON[json3][0]
|
|
389
|
+
json4 = JSON([orig = 1 << 62])
|
|
390
|
+
assert_equal orig, JSON[json4][0]
|
|
391
|
+
json5 = JSON([orig = 1 << 64])
|
|
392
|
+
assert_equal orig, JSON[json5][0]
|
|
393
|
+
end
|
|
234
394
|
end
|
|
235
|
-
# vim: set et sw=2 ts=2:
|
data/tests/test_json_addition.rb
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# -*- coding:utf-8 -*-
|
|
2
3
|
|
|
3
4
|
require 'test/unit'
|
|
4
|
-
|
|
5
|
+
case ENV['JSON']
|
|
6
|
+
when 'pure' then require 'json/pure'
|
|
7
|
+
when 'ext' then require 'json/ext'
|
|
8
|
+
else require 'json'
|
|
9
|
+
end
|
|
10
|
+
require 'json/add/core'
|
|
11
|
+
require 'date'
|
|
5
12
|
|
|
6
13
|
class TC_JSONAddition < Test::Unit::TestCase
|
|
7
14
|
include JSON
|
|
@@ -23,21 +30,29 @@ class TC_JSONAddition < Test::Unit::TestCase
|
|
|
23
30
|
|
|
24
31
|
def to_json(*args)
|
|
25
32
|
{
|
|
26
|
-
'json_class' => self.class,
|
|
33
|
+
'json_class' => self.class.name,
|
|
27
34
|
'args' => [ @a ],
|
|
28
35
|
}.to_json(*args)
|
|
29
36
|
end
|
|
30
37
|
end
|
|
31
38
|
|
|
32
39
|
class B
|
|
40
|
+
def self.json_creatable?
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
|
|
33
44
|
def to_json(*args)
|
|
34
45
|
{
|
|
35
|
-
'json_class' => self.class,
|
|
46
|
+
'json_class' => self.class.name,
|
|
36
47
|
}.to_json(*args)
|
|
37
48
|
end
|
|
38
49
|
end
|
|
39
50
|
|
|
40
51
|
class C
|
|
52
|
+
def self.json_creatable?
|
|
53
|
+
false
|
|
54
|
+
end
|
|
55
|
+
|
|
41
56
|
def to_json(*args)
|
|
42
57
|
{
|
|
43
58
|
'json_class' => 'TC_JSONAddition::Nix',
|
|
@@ -45,10 +60,6 @@ class TC_JSONAddition < Test::Unit::TestCase
|
|
|
45
60
|
end
|
|
46
61
|
end
|
|
47
62
|
|
|
48
|
-
def setup
|
|
49
|
-
$KCODE = 'UTF8'
|
|
50
|
-
end
|
|
51
|
-
|
|
52
63
|
def test_extended_json
|
|
53
64
|
a = A.new(666)
|
|
54
65
|
assert A.json_creatable?
|
|
@@ -58,22 +69,38 @@ class TC_JSONAddition < Test::Unit::TestCase
|
|
|
58
69
|
assert_equal a, a_again
|
|
59
70
|
end
|
|
60
71
|
|
|
61
|
-
def
|
|
72
|
+
def test_extended_json_disabled
|
|
73
|
+
a = A.new(666)
|
|
74
|
+
assert A.json_creatable?
|
|
75
|
+
json = generate(a)
|
|
76
|
+
a_again = JSON.parse(json, :create_additions => true)
|
|
77
|
+
assert_kind_of a.class, a_again
|
|
78
|
+
assert_equal a, a_again
|
|
79
|
+
a_hash = JSON.parse(json, :create_additions => false)
|
|
80
|
+
assert_kind_of Hash, a_hash
|
|
81
|
+
assert_equal(
|
|
82
|
+
{"args"=>[666], "json_class"=>"TC_JSONAddition::A"}.sort_by { |k,| k },
|
|
83
|
+
a_hash.sort_by { |k,| k }
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_extended_json_fail1
|
|
62
88
|
b = B.new
|
|
63
89
|
assert !B.json_creatable?
|
|
64
90
|
json = generate(b)
|
|
65
|
-
assert_equal({
|
|
91
|
+
assert_equal({ "json_class"=>"TC_JSONAddition::B" }, JSON.parse(json))
|
|
66
92
|
end
|
|
67
93
|
|
|
68
|
-
def
|
|
94
|
+
def test_extended_json_fail2
|
|
69
95
|
c = C.new
|
|
70
96
|
assert !C.json_creatable?
|
|
71
97
|
json = generate(c)
|
|
72
|
-
assert_raises(ArgumentError) { JSON.parse(json) }
|
|
98
|
+
assert_raises(ArgumentError, NameError) { JSON.parse(json) }
|
|
73
99
|
end
|
|
74
100
|
|
|
75
101
|
def test_raw_strings
|
|
76
102
|
raw = ''
|
|
103
|
+
raw.respond_to?(:encode!) and raw.encode!(Encoding::ASCII_8BIT)
|
|
77
104
|
raw_array = []
|
|
78
105
|
for i in 0..255
|
|
79
106
|
raw << i
|
|
@@ -83,12 +110,53 @@ class TC_JSONAddition < Test::Unit::TestCase
|
|
|
83
110
|
json_raw_object = raw.to_json_raw_object
|
|
84
111
|
hash = { 'json_class' => 'String', 'raw'=> raw_array }
|
|
85
112
|
assert_equal hash, json_raw_object
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# "
|
|
90
|
-
assert_equal json_raw, json
|
|
113
|
+
assert_match /\A\{.*\}\Z/, json
|
|
114
|
+
assert_match /"json_class":"String"/, json
|
|
115
|
+
assert_match /"raw":\[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255\]/, json
|
|
91
116
|
raw_again = JSON.parse(json)
|
|
92
117
|
assert_equal raw, raw_again
|
|
93
118
|
end
|
|
119
|
+
|
|
120
|
+
MyJsonStruct = Struct.new 'MyJsonStruct', :foo, :bar
|
|
121
|
+
|
|
122
|
+
def test_core
|
|
123
|
+
t = Time.now
|
|
124
|
+
assert_equal t.inspect, JSON(JSON(t)).inspect
|
|
125
|
+
d = Date.today
|
|
126
|
+
assert_equal d, JSON(JSON(d))
|
|
127
|
+
d = DateTime.civil(2007, 6, 14, 14, 57, 10, Rational(1, 12), 2299161)
|
|
128
|
+
assert_equal d, JSON(JSON(d))
|
|
129
|
+
assert_equal 1..10, JSON(JSON(1..10))
|
|
130
|
+
assert_equal 1...10, JSON(JSON(1...10))
|
|
131
|
+
assert_equal "a".."c", JSON(JSON("a".."c"))
|
|
132
|
+
assert_equal "a"..."c", JSON(JSON("a"..."c"))
|
|
133
|
+
s = MyJsonStruct.new 4711, 'foot'
|
|
134
|
+
assert_equal s, JSON(JSON(s))
|
|
135
|
+
struct = Struct.new :foo, :bar
|
|
136
|
+
s = struct.new 4711, 'foot'
|
|
137
|
+
assert_raises(JSONError) { JSON(s) }
|
|
138
|
+
begin
|
|
139
|
+
raise TypeError, "test me"
|
|
140
|
+
rescue TypeError => e
|
|
141
|
+
e_json = JSON.generate e
|
|
142
|
+
e_again = JSON e_json
|
|
143
|
+
assert_kind_of TypeError, e_again
|
|
144
|
+
assert_equal e.message, e_again.message
|
|
145
|
+
assert_equal e.backtrace, e_again.backtrace
|
|
146
|
+
end
|
|
147
|
+
assert_equal(/foo/, JSON(JSON(/foo/)))
|
|
148
|
+
assert_equal(/foo/i, JSON(JSON(/foo/i)))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def test_utc_datetime
|
|
152
|
+
now = Time.now
|
|
153
|
+
d = DateTime.parse(now.to_s) # usual case
|
|
154
|
+
assert_equal d, JSON.parse(d.to_json)
|
|
155
|
+
d = DateTime.parse(now.utc.to_s) # of = 0
|
|
156
|
+
assert_equal d, JSON.parse(d.to_json)
|
|
157
|
+
d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(1,24))
|
|
158
|
+
assert_equal d, JSON.parse(d.to_json)
|
|
159
|
+
d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(12,24))
|
|
160
|
+
assert_equal d, JSON.parse(d.to_json)
|
|
161
|
+
end
|
|
94
162
|
end
|