json 1.8.3 → 2.5.1
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.
- checksums.yaml +5 -5
- data/{CHANGES → CHANGES.md} +241 -90
- data/Gemfile +10 -6
- data/{COPYING-json-jruby → LICENSE} +5 -6
- data/{README.rdoc → README.md} +201 -134
- data/VERSION +1 -1
- data/ext/json/ext/fbuffer/fbuffer.h +0 -3
- data/ext/json/ext/generator/generator.c +264 -104
- data/ext/json/ext/generator/generator.h +12 -4
- data/ext/json/ext/parser/extconf.rb +28 -0
- data/ext/json/ext/parser/parser.c +425 -462
- data/ext/json/ext/parser/parser.h +5 -5
- data/ext/json/ext/parser/parser.rl +181 -181
- data/ext/json/extconf.rb +1 -1
- data/json.gemspec +0 -0
- data/lib/json.rb +550 -29
- data/lib/json/add/bigdecimal.rb +3 -2
- data/lib/json/add/complex.rb +4 -4
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/date.rb +1 -1
- data/lib/json/add/date_time.rb +1 -1
- data/lib/json/add/exception.rb +1 -1
- data/lib/json/add/ostruct.rb +3 -3
- data/lib/json/add/range.rb +1 -1
- data/lib/json/add/rational.rb +3 -3
- data/lib/json/add/regexp.rb +3 -3
- data/lib/json/add/set.rb +29 -0
- data/lib/json/add/struct.rb +1 -1
- data/lib/json/add/symbol.rb +1 -1
- data/lib/json/add/time.rb +1 -1
- data/lib/json/common.rb +381 -162
- data/lib/json/ext.rb +0 -6
- data/lib/json/generic_object.rb +5 -4
- data/lib/json/pure.rb +2 -8
- data/lib/json/pure/generator.rb +83 -126
- data/lib/json/pure/parser.rb +62 -84
- data/lib/json/version.rb +2 -1
- data/tests/fixtures/fail29.json +1 -0
- data/tests/fixtures/fail30.json +1 -0
- data/tests/fixtures/fail31.json +1 -0
- data/tests/fixtures/fail32.json +1 -0
- data/tests/fixtures/obsolete_fail1.json +1 -0
- data/tests/{test_json_addition.rb → json_addition_test.rb} +28 -25
- data/tests/json_common_interface_test.rb +169 -0
- data/tests/json_encoding_test.rb +107 -0
- data/tests/json_ext_parser_test.rb +15 -0
- data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +13 -8
- data/tests/{test_json_generate.rb → json_generator_test.rb} +109 -47
- data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
- data/tests/json_parser_test.rb +497 -0
- data/tests/json_string_matching_test.rb +38 -0
- data/tests/lib/core_assertions.rb +763 -0
- data/tests/lib/envutil.rb +365 -0
- data/tests/lib/find_executable.rb +22 -0
- data/tests/lib/helper.rb +4 -0
- data/tests/ractor_test.rb +30 -0
- data/tests/test_helper.rb +17 -0
- metadata +48 -76
- data/.gitignore +0 -16
- data/.travis.yml +0 -26
- data/COPYING +0 -58
- data/GPL +0 -340
- data/README-json-jruby.markdown +0 -33
- data/Rakefile +0 -412
- data/TODO +0 -1
- data/data/example.json +0 -1
- data/data/index.html +0 -38
- data/data/prototype.js +0 -4184
- data/diagrams/.keep +0 -0
- data/install.rb +0 -23
- data/java/src/json/ext/ByteListTranscoder.java +0 -167
- data/java/src/json/ext/Generator.java +0 -444
- data/java/src/json/ext/GeneratorMethods.java +0 -232
- data/java/src/json/ext/GeneratorService.java +0 -43
- data/java/src/json/ext/GeneratorState.java +0 -543
- data/java/src/json/ext/OptionsReader.java +0 -114
- data/java/src/json/ext/Parser.java +0 -2645
- data/java/src/json/ext/Parser.rl +0 -969
- data/java/src/json/ext/ParserService.java +0 -35
- data/java/src/json/ext/RuntimeInfo.java +0 -121
- data/java/src/json/ext/StringDecoder.java +0 -167
- data/java/src/json/ext/StringEncoder.java +0 -106
- data/java/src/json/ext/Utils.java +0 -89
- data/json-java.gemspec +0 -23
- data/json_pure.gemspec +0 -40
- data/tests/fixtures/fail1.json +0 -1
- data/tests/setup_variant.rb +0 -11
- data/tests/test_json.rb +0 -553
- data/tests/test_json_encoding.rb +0 -65
- data/tests/test_json_string_matching.rb +0 -39
- data/tests/test_json_unicode.rb +0 -72
- data/tools/fuzz.rb +0 -139
- data/tools/server.rb +0 -62
data/lib/json/ext.rb
CHANGED
data/lib/json/generic_object.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
require 'ostruct'
|
2
3
|
|
3
4
|
module JSON
|
@@ -48,12 +49,12 @@ module JSON
|
|
48
49
|
end
|
49
50
|
|
50
51
|
def [](name)
|
51
|
-
|
52
|
-
end
|
52
|
+
__send__(name)
|
53
|
+
end unless method_defined?(:[])
|
53
54
|
|
54
55
|
def []=(name, value)
|
55
|
-
__send__
|
56
|
-
end
|
56
|
+
__send__("#{name}=", value)
|
57
|
+
end unless method_defined?(:[]=)
|
57
58
|
|
58
59
|
def |(other)
|
59
60
|
self.class[other.to_hash.merge(to_hash)]
|
data/lib/json/pure.rb
CHANGED
@@ -1,17 +1,11 @@
|
|
1
|
-
if ENV['SIMPLECOV_COVERAGE'].to_i == 1
|
2
|
-
require 'simplecov'
|
3
|
-
SimpleCov.start do
|
4
|
-
add_filter "/tests/"
|
5
|
-
end
|
6
|
-
end
|
7
1
|
require 'json/common'
|
8
|
-
require 'json/pure/parser'
|
9
|
-
require 'json/pure/generator'
|
10
2
|
|
11
3
|
module JSON
|
12
4
|
# This module holds all the modules/classes that implement JSON's
|
13
5
|
# functionality in pure ruby.
|
14
6
|
module Pure
|
7
|
+
require 'json/pure/parser'
|
8
|
+
require 'json/pure/generator'
|
15
9
|
$DEBUG and warn "Using Pure library for JSON."
|
16
10
|
JSON.parser = Parser
|
17
11
|
JSON.generator = Generator
|
data/lib/json/pure/generator.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
module JSON
|
2
3
|
MAP = {
|
3
4
|
"\x0" => '\u0000',
|
@@ -36,87 +37,53 @@ module JSON
|
|
36
37
|
'\\' => '\\\\',
|
37
38
|
} # :nodoc:
|
38
39
|
|
40
|
+
ESCAPE_SLASH_MAP = MAP.merge(
|
41
|
+
'/' => '\\/',
|
42
|
+
)
|
43
|
+
|
39
44
|
# Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
|
40
45
|
# UTF16 big endian characters as \u????, and return it.
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
def utf8_to_json_ascii(string) # :nodoc:
|
51
|
-
string = string.dup
|
52
|
-
string.force_encoding(::Encoding::ASCII_8BIT)
|
53
|
-
string.gsub!(/["\\\x0-\x1f]/n) { MAP[$&] }
|
54
|
-
string.gsub!(/(
|
55
|
-
(?:
|
56
|
-
[\xc2-\xdf][\x80-\xbf] |
|
57
|
-
[\xe0-\xef][\x80-\xbf]{2} |
|
58
|
-
[\xf0-\xf4][\x80-\xbf]{3}
|
59
|
-
)+ |
|
60
|
-
[\x80-\xc1\xf5-\xff] # invalid
|
61
|
-
)/nx) { |c|
|
62
|
-
c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
|
63
|
-
s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
|
64
|
-
s.force_encoding(::Encoding::ASCII_8BIT)
|
65
|
-
s.gsub!(/.{4}/n, '\\\\u\&')
|
66
|
-
s.force_encoding(::Encoding::UTF_8)
|
67
|
-
}
|
68
|
-
string.force_encoding(::Encoding::UTF_8)
|
69
|
-
string
|
70
|
-
rescue => e
|
71
|
-
raise GeneratorError.wrap(e)
|
72
|
-
end
|
73
|
-
|
74
|
-
def valid_utf8?(string)
|
75
|
-
encoding = string.encoding
|
76
|
-
(encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
|
77
|
-
string.valid_encoding?
|
78
|
-
end
|
79
|
-
module_function :valid_utf8?
|
80
|
-
else
|
81
|
-
def utf8_to_json(string) # :nodoc:
|
82
|
-
string.gsub(/["\\\x0-\x1f]/n) { MAP[$&] }
|
83
|
-
end
|
46
|
+
def utf8_to_json(string, escape_slash = false) # :nodoc:
|
47
|
+
string = string.dup
|
48
|
+
string.force_encoding(::Encoding::ASCII_8BIT)
|
49
|
+
map = escape_slash ? ESCAPE_SLASH_MAP : MAP
|
50
|
+
string.gsub!(/[\/"\\\x0-\x1f]/) { map[$&] || $& }
|
51
|
+
string.force_encoding(::Encoding::UTF_8)
|
52
|
+
string
|
53
|
+
end
|
84
54
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
}
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
55
|
+
def utf8_to_json_ascii(string, escape_slash = false) # :nodoc:
|
56
|
+
string = string.dup
|
57
|
+
string.force_encoding(::Encoding::ASCII_8BIT)
|
58
|
+
map = escape_slash ? ESCAPE_SLASH_MAP : MAP
|
59
|
+
string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& }
|
60
|
+
string.gsub!(/(
|
61
|
+
(?:
|
62
|
+
[\xc2-\xdf][\x80-\xbf] |
|
63
|
+
[\xe0-\xef][\x80-\xbf]{2} |
|
64
|
+
[\xf0-\xf4][\x80-\xbf]{3}
|
65
|
+
)+ |
|
66
|
+
[\x80-\xc1\xf5-\xff] # invalid
|
67
|
+
)/nx) { |c|
|
68
|
+
c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
|
69
|
+
s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
|
70
|
+
s.force_encoding(::Encoding::ASCII_8BIT)
|
71
|
+
s.gsub!(/.{4}/n, '\\\\u\&')
|
72
|
+
s.force_encoding(::Encoding::UTF_8)
|
73
|
+
}
|
74
|
+
string.force_encoding(::Encoding::UTF_8)
|
75
|
+
string
|
76
|
+
rescue => e
|
77
|
+
raise GeneratorError.wrap(e)
|
78
|
+
end
|
103
79
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
| \xe0[\xa0-\xbf][\x80-\xbf] # excluding overlongs
|
109
|
-
| [\xe1-\xec\xee\xef][\x80-\xbf]{2} # straight 3-byte
|
110
|
-
| \xed[\x80-\x9f][\x80-\xbf] # excluding surrogates
|
111
|
-
| \xf0[\x90-\xbf][\x80-\xbf]{2} # planes 1-3
|
112
|
-
| [\xf1-\xf3][\x80-\xbf]{3} # planes 4-15
|
113
|
-
| \xf4[\x80-\x8f][\x80-\xbf]{2} # plane 16
|
114
|
-
)*\z/nx
|
115
|
-
end
|
80
|
+
def valid_utf8?(string)
|
81
|
+
encoding = string.encoding
|
82
|
+
(encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
|
83
|
+
string.valid_encoding?
|
116
84
|
end
|
117
85
|
module_function :utf8_to_json, :utf8_to_json_ascii, :valid_utf8?
|
118
86
|
|
119
|
-
|
120
87
|
module Pure
|
121
88
|
module Generator
|
122
89
|
# This class is used to create State instances, that are use to hold data
|
@@ -148,14 +115,13 @@ module JSON
|
|
148
115
|
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
|
149
116
|
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
|
150
117
|
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
|
118
|
+
# * *escape_slash*: true if forward slash (/) should be escaped (default: false)
|
151
119
|
# * *check_circular*: is deprecated now, use the :max_nesting option instead,
|
152
120
|
# * *max_nesting*: sets the maximum level of data structure nesting in
|
153
121
|
# the generated JSON, max_nesting = 0 if no maximum should be checked.
|
154
122
|
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
|
155
123
|
# generated, otherwise an exception is thrown, if these values are
|
156
124
|
# encountered. This options defaults to false.
|
157
|
-
# * *quirks_mode*: Enables quirks_mode for parser, that is for example
|
158
|
-
# generating single JSON values instead of documents is possible.
|
159
125
|
def initialize(opts = {})
|
160
126
|
@indent = ''
|
161
127
|
@space = ''
|
@@ -164,7 +130,7 @@ module JSON
|
|
164
130
|
@array_nl = ''
|
165
131
|
@allow_nan = false
|
166
132
|
@ascii_only = false
|
167
|
-
@
|
133
|
+
@escape_slash = false
|
168
134
|
@buffer_initial_length = 1024
|
169
135
|
configure opts
|
170
136
|
end
|
@@ -190,9 +156,9 @@ module JSON
|
|
190
156
|
# the generated JSON, max_nesting = 0 if no maximum is checked.
|
191
157
|
attr_accessor :max_nesting
|
192
158
|
|
193
|
-
# If this attribute is set to true,
|
194
|
-
#
|
195
|
-
attr_accessor :
|
159
|
+
# If this attribute is set to true, forward slashes will be escaped in
|
160
|
+
# all json strings.
|
161
|
+
attr_accessor :escape_slash
|
196
162
|
|
197
163
|
# :stopdoc:
|
198
164
|
attr_reader :buffer_initial_length
|
@@ -233,9 +199,9 @@ module JSON
|
|
233
199
|
@ascii_only
|
234
200
|
end
|
235
201
|
|
236
|
-
# Returns true, if
|
237
|
-
def
|
238
|
-
@
|
202
|
+
# Returns true, if forward slashes are escaped. Otherwise returns false.
|
203
|
+
def escape_slash?
|
204
|
+
@escape_slash
|
239
205
|
end
|
240
206
|
|
241
207
|
# Configure this State instance with the Hash _opts_, and return
|
@@ -259,8 +225,8 @@ module JSON
|
|
259
225
|
@allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
|
260
226
|
@ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
|
261
227
|
@depth = opts[:depth] || 0
|
262
|
-
@quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode)
|
263
228
|
@buffer_initial_length ||= opts[:buffer_initial_length]
|
229
|
+
@escape_slash = !!opts[:escape_slash] if opts.key?(:escape_slash)
|
264
230
|
|
265
231
|
if !opts.key?(:max_nesting) # defaults to 100
|
266
232
|
@max_nesting = 100
|
@@ -286,20 +252,14 @@ module JSON
|
|
286
252
|
|
287
253
|
alias to_hash to_h
|
288
254
|
|
289
|
-
# Generates a valid JSON document from object +obj+ and
|
290
|
-
# result. If no valid JSON document can be
|
255
|
+
# Generates a valid JSON document from object +obj+ and
|
256
|
+
# returns the result. If no valid JSON document can be
|
257
|
+
# created this method raises a
|
291
258
|
# GeneratorError exception.
|
292
259
|
def generate(obj)
|
293
260
|
result = obj.to_json(self)
|
294
261
|
JSON.valid_utf8?(result) or raise GeneratorError,
|
295
262
|
"source sequence #{result.inspect} is illegal/malformed utf-8"
|
296
|
-
unless @quirks_mode
|
297
|
-
unless result =~ /\A\s*\[/ && result =~ /\]\s*\Z/ ||
|
298
|
-
result =~ /\A\s*\{/ && result =~ /\}\s*\Z/
|
299
|
-
then
|
300
|
-
raise GeneratorError, "only generation of JSON objects or arrays allowed"
|
301
|
-
end
|
302
|
-
end
|
303
263
|
result
|
304
264
|
end
|
305
265
|
|
@@ -308,7 +268,8 @@ module JSON
|
|
308
268
|
if respond_to?(name)
|
309
269
|
__send__(name)
|
310
270
|
else
|
311
|
-
instance_variable_get("@#{name}")
|
271
|
+
instance_variable_get("@#{name}") if
|
272
|
+
instance_variables.include?("@#{name}".to_sym) # avoid warning
|
312
273
|
end
|
313
274
|
end
|
314
275
|
|
@@ -363,12 +324,18 @@ module JSON
|
|
363
324
|
result << state.space_before
|
364
325
|
result << ':'
|
365
326
|
result << state.space
|
366
|
-
|
327
|
+
if value.respond_to?(:to_json)
|
328
|
+
result << value.to_json(state)
|
329
|
+
else
|
330
|
+
result << %{"#{String(value)}"}
|
331
|
+
end
|
367
332
|
first = false
|
368
333
|
}
|
369
334
|
depth = state.depth -= 1
|
370
|
-
|
371
|
-
|
335
|
+
unless first
|
336
|
+
result << state.object_nl
|
337
|
+
result << state.indent * depth if indent
|
338
|
+
end
|
372
339
|
result << '}'
|
373
340
|
result
|
374
341
|
end
|
@@ -398,7 +365,11 @@ module JSON
|
|
398
365
|
each { |value|
|
399
366
|
result << delim unless first
|
400
367
|
result << state.indent * depth if indent
|
401
|
-
|
368
|
+
if value.respond_to?(:to_json)
|
369
|
+
result << value.to_json(state)
|
370
|
+
else
|
371
|
+
result << %{"#{String(value)}"}
|
372
|
+
end
|
402
373
|
first = false
|
403
374
|
}
|
404
375
|
depth = state.depth -= 1
|
@@ -437,38 +408,24 @@ module JSON
|
|
437
408
|
end
|
438
409
|
|
439
410
|
module String
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
string = encode(::Encoding::UTF_8)
|
450
|
-
end
|
451
|
-
if state.ascii_only?
|
452
|
-
'"' << JSON.utf8_to_json_ascii(string) << '"'
|
453
|
-
else
|
454
|
-
'"' << JSON.utf8_to_json(string) << '"'
|
455
|
-
end
|
411
|
+
# This string should be encoded with UTF-8 A call to this method
|
412
|
+
# returns a JSON string encoded with UTF16 big endian characters as
|
413
|
+
# \u????.
|
414
|
+
def to_json(state = nil, *args)
|
415
|
+
state = State.from_state(state)
|
416
|
+
if encoding == ::Encoding::UTF_8
|
417
|
+
string = self
|
418
|
+
else
|
419
|
+
string = encode(::Encoding::UTF_8)
|
456
420
|
end
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
def to_json(state = nil, *args)
|
462
|
-
state = State.from_state(state)
|
463
|
-
if state.ascii_only?
|
464
|
-
'"' << JSON.utf8_to_json_ascii(self) << '"'
|
465
|
-
else
|
466
|
-
'"' << JSON.utf8_to_json(self) << '"'
|
467
|
-
end
|
421
|
+
if state.ascii_only?
|
422
|
+
'"' << JSON.utf8_to_json_ascii(string, state.escape_slash) << '"'
|
423
|
+
else
|
424
|
+
'"' << JSON.utf8_to_json(string, state.escape_slash) << '"'
|
468
425
|
end
|
469
426
|
end
|
470
427
|
|
471
|
-
# Module that holds the
|
428
|
+
# Module that holds the extending methods if, the String module is
|
472
429
|
# included.
|
473
430
|
module Extend
|
474
431
|
# Raw Strings are JSON Objects (the raw bytes are stored in an
|
data/lib/json/pure/parser.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#frozen_string_literal: false
|
1
2
|
require 'strscan'
|
2
3
|
|
3
4
|
module JSON
|
@@ -48,7 +49,7 @@ module JSON
|
|
48
49
|
)+
|
49
50
|
)mx
|
50
51
|
|
51
|
-
UNPARSED = Object.new
|
52
|
+
UNPARSED = Object.new.freeze
|
52
53
|
|
53
54
|
# Creates a new JSON::Pure::Parser instance for the string _source_.
|
54
55
|
#
|
@@ -58,23 +59,25 @@ module JSON
|
|
58
59
|
# structures. Disable depth checking with :max_nesting => false|nil|0,
|
59
60
|
# it defaults to 100.
|
60
61
|
# * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
|
61
|
-
# defiance of RFC
|
62
|
+
# defiance of RFC 7159 to be parsed by the Parser. This option defaults
|
62
63
|
# to false.
|
64
|
+
# * *freeze*: If set to true, all parsed objects will be frozen. Parsed
|
65
|
+
# string will be deduplicated if possible.
|
63
66
|
# * *symbolize_names*: If set to true, returns symbols for the names
|
64
|
-
# (keys) in a JSON object. Otherwise strings are returned, which is
|
65
|
-
# the default.
|
67
|
+
# (keys) in a JSON object. Otherwise strings are returned, which is
|
68
|
+
# also the default. It's not possible to use this option in
|
69
|
+
# conjunction with the *create_additions* option.
|
66
70
|
# * *create_additions*: If set to true, the Parser creates
|
67
|
-
# additions when
|
71
|
+
# additions when a matching class and create_id are found. This
|
68
72
|
# option defaults to false.
|
69
73
|
# * *object_class*: Defaults to Hash
|
70
74
|
# * *array_class*: Defaults to Array
|
71
|
-
# * *
|
72
|
-
#
|
75
|
+
# * *decimal_class*: Specifies which class to use instead of the default
|
76
|
+
# (Float) when parsing decimal numbers. This class must accept a single
|
77
|
+
# string argument in its constructor.
|
73
78
|
def initialize(source, opts = {})
|
74
79
|
opts ||= {}
|
75
|
-
|
76
|
-
source = convert_encoding source
|
77
|
-
end
|
80
|
+
source = convert_encoding source
|
78
81
|
super source
|
79
82
|
if !opts.key?(:max_nesting) # defaults to 100
|
80
83
|
@max_nesting = 100
|
@@ -85,61 +88,45 @@ module JSON
|
|
85
88
|
end
|
86
89
|
@allow_nan = !!opts[:allow_nan]
|
87
90
|
@symbolize_names = !!opts[:symbolize_names]
|
91
|
+
@freeze = !!opts[:freeze]
|
88
92
|
if opts.key?(:create_additions)
|
89
93
|
@create_additions = !!opts[:create_additions]
|
90
94
|
else
|
91
95
|
@create_additions = false
|
92
96
|
end
|
97
|
+
@symbolize_names && @create_additions and raise ArgumentError,
|
98
|
+
'options :symbolize_names and :create_additions cannot be used '\
|
99
|
+
'in conjunction'
|
93
100
|
@create_id = @create_additions ? JSON.create_id : nil
|
94
101
|
@object_class = opts[:object_class] || Hash
|
95
102
|
@array_class = opts[:array_class] || Array
|
103
|
+
@decimal_class = opts[:decimal_class]
|
96
104
|
@match_string = opts[:match_string]
|
97
105
|
end
|
98
106
|
|
99
107
|
alias source string
|
100
108
|
|
101
|
-
def quirks_mode?
|
102
|
-
!!@quirks_mode
|
103
|
-
end
|
104
|
-
|
105
109
|
def reset
|
106
110
|
super
|
107
111
|
@current_nesting = 0
|
108
112
|
end
|
109
113
|
|
110
|
-
# Parses the current JSON string _source_ and returns the
|
111
|
-
# structure as a result.
|
114
|
+
# Parses the current JSON string _source_ and returns the
|
115
|
+
# complete data structure as a result.
|
112
116
|
def parse
|
113
117
|
reset
|
114
118
|
obj = nil
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
if eos?
|
119
|
-
raise ParserError, "source did not contain any JSON!"
|
120
|
-
else
|
121
|
-
obj = parse_value
|
122
|
-
obj == UNPARSED and raise ParserError, "source did not contain any JSON!"
|
123
|
-
end
|
119
|
+
while !eos? && skip(IGNORE) do end
|
120
|
+
if eos?
|
121
|
+
raise ParserError, "source is not valid JSON!"
|
124
122
|
else
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
@current_nesting = 1
|
130
|
-
obj = parse_object
|
131
|
-
when scan(ARRAY_OPEN)
|
132
|
-
obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
|
133
|
-
@current_nesting = 1
|
134
|
-
obj = parse_array
|
135
|
-
when skip(IGNORE)
|
136
|
-
;
|
137
|
-
else
|
138
|
-
raise ParserError, "source '#{peek(20)}' not in JSON!"
|
139
|
-
end
|
140
|
-
end
|
141
|
-
obj or raise ParserError, "source did not contain any JSON!"
|
123
|
+
obj = parse_value
|
124
|
+
UNPARSED.equal?(obj) and raise ParserError,
|
125
|
+
"source is not valid JSON!"
|
126
|
+
obj.freeze if @freeze
|
142
127
|
end
|
128
|
+
while !eos? && skip(IGNORE) do end
|
129
|
+
eos? or raise ParserError, "source is not valid JSON!"
|
143
130
|
obj
|
144
131
|
end
|
145
132
|
|
@@ -149,43 +136,12 @@ module JSON
|
|
149
136
|
if source.respond_to?(:to_str)
|
150
137
|
source = source.to_str
|
151
138
|
else
|
152
|
-
raise TypeError,
|
139
|
+
raise TypeError,
|
140
|
+
"#{source.inspect} is not like a string"
|
153
141
|
end
|
154
|
-
if
|
155
|
-
|
156
|
-
b = source[0, 4].bytes.to_a
|
157
|
-
source =
|
158
|
-
case
|
159
|
-
when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
|
160
|
-
source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
|
161
|
-
when b.size >= 4 && b[0] == 0 && b[2] == 0
|
162
|
-
source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
|
163
|
-
when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
|
164
|
-
source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
|
165
|
-
when b.size >= 4 && b[1] == 0 && b[3] == 0
|
166
|
-
source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
|
167
|
-
else
|
168
|
-
source.dup
|
169
|
-
end
|
170
|
-
else
|
171
|
-
source = source.encode(::Encoding::UTF_8)
|
172
|
-
end
|
142
|
+
if source.encoding != ::Encoding::ASCII_8BIT
|
143
|
+
source = source.encode(::Encoding::UTF_8)
|
173
144
|
source.force_encoding(::Encoding::ASCII_8BIT)
|
174
|
-
else
|
175
|
-
b = source
|
176
|
-
source =
|
177
|
-
case
|
178
|
-
when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
|
179
|
-
JSON.iconv('utf-8', 'utf-32be', b)
|
180
|
-
when b.size >= 4 && b[0] == 0 && b[2] == 0
|
181
|
-
JSON.iconv('utf-8', 'utf-16be', b)
|
182
|
-
when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
|
183
|
-
JSON.iconv('utf-8', 'utf-32le', b)
|
184
|
-
when b.size >= 4 && b[1] == 0 && b[3] == 0
|
185
|
-
JSON.iconv('utf-8', 'utf-16le', b)
|
186
|
-
else
|
187
|
-
b
|
188
|
-
end
|
189
145
|
end
|
190
146
|
source
|
191
147
|
end
|
@@ -209,6 +165,7 @@ module JSON
|
|
209
165
|
EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT
|
210
166
|
end
|
211
167
|
|
168
|
+
STR_UMINUS = ''.respond_to?(:-@)
|
212
169
|
def parse_string
|
213
170
|
if scan(STRING)
|
214
171
|
return '' if self[1].empty?
|
@@ -228,6 +185,15 @@ module JSON
|
|
228
185
|
if string.respond_to?(:force_encoding)
|
229
186
|
string.force_encoding(::Encoding::UTF_8)
|
230
187
|
end
|
188
|
+
|
189
|
+
if @freeze
|
190
|
+
if STR_UMINUS
|
191
|
+
string = -string
|
192
|
+
else
|
193
|
+
string.freeze
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
231
197
|
if @create_additions and @match_string
|
232
198
|
for (regexp, klass) in @match_string
|
233
199
|
klass.json_creatable? or next
|
@@ -245,7 +211,15 @@ module JSON
|
|
245
211
|
def parse_value
|
246
212
|
case
|
247
213
|
when scan(FLOAT)
|
248
|
-
|
214
|
+
if @decimal_class then
|
215
|
+
if @decimal_class == BigDecimal then
|
216
|
+
BigDecimal(self[1])
|
217
|
+
else
|
218
|
+
@decimal_class.new(self[1]) || Float(self[1])
|
219
|
+
end
|
220
|
+
else
|
221
|
+
Float(self[1])
|
222
|
+
end
|
249
223
|
when scan(INTEGER)
|
250
224
|
Integer(self[1])
|
251
225
|
when scan(TRUE)
|
@@ -254,7 +228,7 @@ module JSON
|
|
254
228
|
false
|
255
229
|
when scan(NULL)
|
256
230
|
nil
|
257
|
-
when (string = parse_string)
|
231
|
+
when !UNPARSED.equal?(string = parse_string)
|
258
232
|
string
|
259
233
|
when scan(ARRAY_OPEN)
|
260
234
|
@current_nesting += 1
|
@@ -282,9 +256,11 @@ module JSON
|
|
282
256
|
@max_nesting.nonzero? && @current_nesting > @max_nesting
|
283
257
|
result = @array_class.new
|
284
258
|
delim = false
|
285
|
-
|
259
|
+
loop do
|
286
260
|
case
|
287
|
-
when
|
261
|
+
when eos?
|
262
|
+
raise ParserError, "unexpected end of string while parsing array"
|
263
|
+
when !UNPARSED.equal?(value = parse_value)
|
288
264
|
delim = false
|
289
265
|
result << value
|
290
266
|
skip(IGNORE)
|
@@ -314,15 +290,17 @@ module JSON
|
|
314
290
|
@max_nesting.nonzero? && @current_nesting > @max_nesting
|
315
291
|
result = @object_class.new
|
316
292
|
delim = false
|
317
|
-
|
293
|
+
loop do
|
318
294
|
case
|
319
|
-
when
|
295
|
+
when eos?
|
296
|
+
raise ParserError, "unexpected end of string while parsing object"
|
297
|
+
when !UNPARSED.equal?(string = parse_string)
|
320
298
|
skip(IGNORE)
|
321
299
|
unless scan(PAIR_DELIMITER)
|
322
300
|
raise ParserError, "expected ':' in object at '#{peek(20)}'!"
|
323
301
|
end
|
324
302
|
skip(IGNORE)
|
325
|
-
unless (value = parse_value)
|
303
|
+
unless UNPARSED.equal?(value = parse_value)
|
326
304
|
result[@symbolize_names ? string.to_sym : string] = value
|
327
305
|
delim = false
|
328
306
|
skip(IGNORE)
|