json 2.6.2 → 2.10.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/BSDL +22 -0
  3. data/CHANGES.md +144 -17
  4. data/LEGAL +8 -0
  5. data/README.md +67 -224
  6. data/ext/json/ext/fbuffer/fbuffer.h +110 -92
  7. data/ext/json/ext/generator/extconf.rb +8 -2
  8. data/ext/json/ext/generator/generator.c +1020 -806
  9. data/ext/json/ext/parser/extconf.rb +7 -27
  10. data/ext/json/ext/parser/parser.c +1343 -3212
  11. data/json.gemspec +48 -52
  12. data/lib/json/add/bigdecimal.rb +39 -10
  13. data/lib/json/add/complex.rb +29 -6
  14. data/lib/json/add/core.rb +1 -1
  15. data/lib/json/add/date.rb +27 -7
  16. data/lib/json/add/date_time.rb +26 -9
  17. data/lib/json/add/exception.rb +25 -7
  18. data/lib/json/add/ostruct.rb +32 -9
  19. data/lib/json/add/range.rb +33 -8
  20. data/lib/json/add/rational.rb +28 -6
  21. data/lib/json/add/regexp.rb +26 -8
  22. data/lib/json/add/set.rb +25 -6
  23. data/lib/json/add/struct.rb +29 -7
  24. data/lib/json/add/symbol.rb +34 -7
  25. data/lib/json/add/time.rb +29 -15
  26. data/lib/json/common.rb +418 -128
  27. data/lib/json/ext/generator/state.rb +106 -0
  28. data/lib/json/ext.rb +34 -4
  29. data/lib/json/generic_object.rb +7 -3
  30. data/lib/json/truffle_ruby/generator.rb +690 -0
  31. data/lib/json/version.rb +3 -7
  32. data/lib/json.rb +25 -21
  33. metadata +15 -26
  34. data/VERSION +0 -1
  35. data/ext/json/ext/generator/depend +0 -1
  36. data/ext/json/ext/generator/generator.h +0 -174
  37. data/ext/json/ext/parser/depend +0 -1
  38. data/ext/json/ext/parser/parser.h +0 -96
  39. data/ext/json/ext/parser/parser.rl +0 -986
  40. data/ext/json/extconf.rb +0 -3
  41. data/lib/json/pure/generator.rb +0 -479
  42. data/lib/json/pure/parser.rb +0 -337
  43. data/lib/json/pure.rb +0 -15
  44. /data/{LICENSE → COPYING} +0 -0
@@ -0,0 +1,690 @@
1
+ # frozen_string_literal: true
2
+ module JSON
3
+ module TruffleRuby
4
+ module Generator
5
+ MAP = {
6
+ "\x0" => '\u0000',
7
+ "\x1" => '\u0001',
8
+ "\x2" => '\u0002',
9
+ "\x3" => '\u0003',
10
+ "\x4" => '\u0004',
11
+ "\x5" => '\u0005',
12
+ "\x6" => '\u0006',
13
+ "\x7" => '\u0007',
14
+ "\b" => '\b',
15
+ "\t" => '\t',
16
+ "\n" => '\n',
17
+ "\xb" => '\u000b',
18
+ "\f" => '\f',
19
+ "\r" => '\r',
20
+ "\xe" => '\u000e',
21
+ "\xf" => '\u000f',
22
+ "\x10" => '\u0010',
23
+ "\x11" => '\u0011',
24
+ "\x12" => '\u0012',
25
+ "\x13" => '\u0013',
26
+ "\x14" => '\u0014',
27
+ "\x15" => '\u0015',
28
+ "\x16" => '\u0016',
29
+ "\x17" => '\u0017',
30
+ "\x18" => '\u0018',
31
+ "\x19" => '\u0019',
32
+ "\x1a" => '\u001a',
33
+ "\x1b" => '\u001b',
34
+ "\x1c" => '\u001c',
35
+ "\x1d" => '\u001d',
36
+ "\x1e" => '\u001e',
37
+ "\x1f" => '\u001f',
38
+ '"' => '\"',
39
+ '\\' => '\\\\',
40
+ }.freeze # :nodoc:
41
+
42
+ SCRIPT_SAFE_MAP = MAP.merge(
43
+ '/' => '\\/',
44
+ "\u2028" => '\u2028',
45
+ "\u2029" => '\u2029',
46
+ ).freeze
47
+
48
+ SCRIPT_SAFE_ESCAPE_PATTERN = /[\/"\\\x0-\x1f\u2028-\u2029]/
49
+
50
+ # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
51
+ # UTF16 big endian characters as \u????, and return it.
52
+ def self.utf8_to_json(string, script_safe = false) # :nodoc:
53
+ if script_safe
54
+ if SCRIPT_SAFE_ESCAPE_PATTERN.match?(string)
55
+ string.gsub(SCRIPT_SAFE_ESCAPE_PATTERN, SCRIPT_SAFE_MAP)
56
+ else
57
+ string
58
+ end
59
+ else
60
+ if /["\\\x0-\x1f]/.match?(string)
61
+ string.gsub(/["\\\x0-\x1f]/, MAP)
62
+ else
63
+ string
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.utf8_to_json_ascii(original_string, script_safe = false) # :nodoc:
69
+ string = original_string.b
70
+ map = script_safe ? SCRIPT_SAFE_MAP : MAP
71
+ string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& }
72
+ string.gsub!(/(
73
+ (?:
74
+ [\xc2-\xdf][\x80-\xbf] |
75
+ [\xe0-\xef][\x80-\xbf]{2} |
76
+ [\xf0-\xf4][\x80-\xbf]{3}
77
+ )+ |
78
+ [\x80-\xc1\xf5-\xff] # invalid
79
+ )/nx) { |c|
80
+ c.size == 1 and raise GeneratorError.new("invalid utf8 byte: '#{c}'", original_string)
81
+ s = c.encode(::Encoding::UTF_16BE, ::Encoding::UTF_8).unpack('H*')[0]
82
+ s.force_encoding(::Encoding::BINARY)
83
+ s.gsub!(/.{4}/n, '\\\\u\&')
84
+ s.force_encoding(::Encoding::UTF_8)
85
+ }
86
+ string.force_encoding(::Encoding::UTF_8)
87
+ string
88
+ rescue => e
89
+ raise GeneratorError.new(e.message, original_string)
90
+ end
91
+
92
+ def self.valid_utf8?(string)
93
+ encoding = string.encoding
94
+ (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
95
+ string.valid_encoding?
96
+ end
97
+
98
+ # This class is used to create State instances, that are use to hold data
99
+ # while generating a JSON text from a Ruby data structure.
100
+ class State
101
+ def self.generate(obj, opts = nil, io = nil)
102
+ new(opts).generate(obj, io)
103
+ end
104
+
105
+ # Creates a State object from _opts_, which ought to be Hash to create
106
+ # a new State instance configured by _opts_, something else to create
107
+ # an unconfigured instance. If _opts_ is a State object, it is just
108
+ # returned.
109
+ def self.from_state(opts)
110
+ if opts
111
+ case
112
+ when self === opts
113
+ return opts
114
+ when opts.respond_to?(:to_hash)
115
+ return new(opts.to_hash)
116
+ when opts.respond_to?(:to_h)
117
+ return new(opts.to_h)
118
+ end
119
+ end
120
+ SAFE_STATE_PROTOTYPE.dup
121
+ end
122
+
123
+ # Instantiates a new State object, configured by _opts_.
124
+ #
125
+ # _opts_ can have the following keys:
126
+ #
127
+ # * *indent*: a string used to indent levels (default: ''),
128
+ # * *space*: a string that is put after, a : or , delimiter (default: ''),
129
+ # * *space_before*: a string that is put before a : pair delimiter (default: ''),
130
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
131
+ # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
132
+ # * *script_safe*: true if U+2028, U+2029 and forward slash (/) should be escaped
133
+ # as to make the JSON object safe to interpolate in a script tag (default: false).
134
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
135
+ # * *max_nesting*: sets the maximum level of data structure nesting in
136
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
137
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
138
+ # generated, otherwise an exception is thrown, if these values are
139
+ # encountered. This options defaults to false.
140
+ def initialize(opts = nil)
141
+ @indent = ''
142
+ @space = ''
143
+ @space_before = ''
144
+ @object_nl = ''
145
+ @array_nl = ''
146
+ @allow_nan = false
147
+ @ascii_only = false
148
+ @as_json = false
149
+ @depth = 0
150
+ @buffer_initial_length = 1024
151
+ @script_safe = false
152
+ @strict = false
153
+ @max_nesting = 100
154
+ configure(opts) if opts
155
+ end
156
+
157
+ # This string is used to indent levels in the JSON text.
158
+ attr_accessor :indent
159
+
160
+ # This string is used to insert a space between the tokens in a JSON
161
+ # string.
162
+ attr_accessor :space
163
+
164
+ # This string is used to insert a space before the ':' in JSON objects.
165
+ attr_accessor :space_before
166
+
167
+ # This string is put at the end of a line that holds a JSON object (or
168
+ # Hash).
169
+ attr_accessor :object_nl
170
+
171
+ # This string is put at the end of a line that holds a JSON array.
172
+ attr_accessor :array_nl
173
+
174
+ # This proc converts unsupported types into native JSON types.
175
+ attr_accessor :as_json
176
+
177
+ # This integer returns the maximum level of data structure nesting in
178
+ # the generated JSON, max_nesting = 0 if no maximum is checked.
179
+ attr_accessor :max_nesting
180
+
181
+ # If this attribute is set to true, forward slashes will be escaped in
182
+ # all json strings.
183
+ attr_accessor :script_safe
184
+
185
+ # If this attribute is set to true, attempting to serialize types not
186
+ # supported by the JSON spec will raise a JSON::GeneratorError
187
+ attr_accessor :strict
188
+
189
+ # :stopdoc:
190
+ attr_reader :buffer_initial_length
191
+
192
+ def buffer_initial_length=(length)
193
+ if length > 0
194
+ @buffer_initial_length = length
195
+ end
196
+ end
197
+ # :startdoc:
198
+
199
+ # This integer returns the current depth data structure nesting in the
200
+ # generated JSON.
201
+ attr_accessor :depth
202
+
203
+ def check_max_nesting # :nodoc:
204
+ return if @max_nesting.zero?
205
+ current_nesting = depth + 1
206
+ current_nesting > @max_nesting and
207
+ raise NestingError, "nesting of #{current_nesting} is too deep"
208
+ end
209
+
210
+ # Returns true, if circular data structures are checked,
211
+ # otherwise returns false.
212
+ def check_circular?
213
+ !@max_nesting.zero?
214
+ end
215
+
216
+ # Returns true if NaN, Infinity, and -Infinity should be considered as
217
+ # valid JSON and output.
218
+ def allow_nan?
219
+ @allow_nan
220
+ end
221
+
222
+ # Returns true, if only ASCII characters should be generated. Otherwise
223
+ # returns false.
224
+ def ascii_only?
225
+ @ascii_only
226
+ end
227
+
228
+ # Returns true, if forward slashes are escaped. Otherwise returns false.
229
+ def script_safe?
230
+ @script_safe
231
+ end
232
+
233
+ # Returns true, if strict mode is enabled. Otherwise returns false.
234
+ # Strict mode only allow serializing JSON native types: Hash, Array,
235
+ # String, Integer, Float, true, false and nil.
236
+ def strict?
237
+ @strict
238
+ end
239
+
240
+ # Configure this State instance with the Hash _opts_, and return
241
+ # itself.
242
+ def configure(opts)
243
+ if opts.respond_to?(:to_hash)
244
+ opts = opts.to_hash
245
+ elsif opts.respond_to?(:to_h)
246
+ opts = opts.to_h
247
+ else
248
+ raise TypeError, "can't convert #{opts.class} into Hash"
249
+ end
250
+ opts.each do |key, value|
251
+ instance_variable_set "@#{key}", value
252
+ end
253
+
254
+ # NOTE: If adding new instance variables here, check whether #generate should check them for #generate_json
255
+ @indent = opts[:indent] || '' if opts.key?(:indent)
256
+ @space = opts[:space] || '' if opts.key?(:space)
257
+ @space_before = opts[:space_before] || '' if opts.key?(:space_before)
258
+ @object_nl = opts[:object_nl] || '' if opts.key?(:object_nl)
259
+ @array_nl = opts[:array_nl] || '' if opts.key?(:array_nl)
260
+ @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
261
+ @as_json = opts[:as_json].to_proc if opts[:as_json]
262
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
263
+ @depth = opts[:depth] || 0
264
+ @buffer_initial_length ||= opts[:buffer_initial_length]
265
+
266
+ @script_safe = if opts.key?(:script_safe)
267
+ !!opts[:script_safe]
268
+ elsif opts.key?(:escape_slash)
269
+ !!opts[:escape_slash]
270
+ else
271
+ false
272
+ end
273
+
274
+ @strict = !!opts[:strict] if opts.key?(:strict)
275
+
276
+ if !opts.key?(:max_nesting) # defaults to 100
277
+ @max_nesting = 100
278
+ elsif opts[:max_nesting]
279
+ @max_nesting = opts[:max_nesting]
280
+ else
281
+ @max_nesting = 0
282
+ end
283
+ self
284
+ end
285
+ alias merge configure
286
+
287
+ # Returns the configuration instance variables as a hash, that can be
288
+ # passed to the configure method.
289
+ def to_h
290
+ result = {}
291
+ instance_variables.each do |iv|
292
+ iv = iv.to_s[1..-1]
293
+ result[iv.to_sym] = self[iv]
294
+ end
295
+ result
296
+ end
297
+
298
+ alias to_hash to_h
299
+
300
+ # Generates a valid JSON document from object +obj+ and
301
+ # returns the result. If no valid JSON document can be
302
+ # created this method raises a
303
+ # GeneratorError exception.
304
+ def generate(obj, anIO = nil)
305
+ if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
306
+ !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj)
307
+ result = generate_json(obj, ''.dup)
308
+ else
309
+ result = obj.to_json(self)
310
+ end
311
+ JSON::TruffleRuby::Generator.valid_utf8?(result) or raise GeneratorError.new(
312
+ "source sequence #{result.inspect} is illegal/malformed utf-8",
313
+ obj
314
+ )
315
+ if anIO
316
+ anIO.write(result)
317
+ anIO
318
+ else
319
+ result
320
+ end
321
+ end
322
+
323
+ def generate_new(obj, anIO = nil) # :nodoc:
324
+ dup.generate(obj, anIO)
325
+ end
326
+
327
+ # Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
328
+ private def generate_json(obj, buf)
329
+ case obj
330
+ when Hash
331
+ buf << '{'
332
+ first = true
333
+ obj.each_pair do |k,v|
334
+ buf << ',' unless first
335
+
336
+ key_str = k.to_s
337
+ if key_str.class == String
338
+ fast_serialize_string(key_str, buf)
339
+ elsif key_str.is_a?(String)
340
+ generate_json(key_str, buf)
341
+ else
342
+ raise TypeError, "#{k.class}#to_s returns an instance of #{key_str.class}, expected a String"
343
+ end
344
+
345
+ buf << ':'
346
+ generate_json(v, buf)
347
+ first = false
348
+ end
349
+ buf << '}'
350
+ when Array
351
+ buf << '['
352
+ first = true
353
+ obj.each do |e|
354
+ buf << ',' unless first
355
+ generate_json(e, buf)
356
+ first = false
357
+ end
358
+ buf << ']'
359
+ when String
360
+ if obj.class == String
361
+ fast_serialize_string(obj, buf)
362
+ else
363
+ buf << obj.to_json(self)
364
+ end
365
+ when Integer
366
+ buf << obj.to_s
367
+ when Symbol
368
+ if @strict
369
+ fast_serialize_string(obj.name, buf)
370
+ else
371
+ buf << obj.to_json(self)
372
+ end
373
+ else
374
+ # Note: Float is handled this way since Float#to_s is slow anyway
375
+ buf << obj.to_json(self)
376
+ end
377
+ end
378
+
379
+ # Assumes !@ascii_only, !@script_safe
380
+ private def fast_serialize_string(string, buf) # :nodoc:
381
+ buf << '"'
382
+ unless string.encoding == ::Encoding::UTF_8
383
+ begin
384
+ string = string.encode(::Encoding::UTF_8)
385
+ rescue Encoding::UndefinedConversionError => error
386
+ raise GeneratorError.new(error.message, string)
387
+ end
388
+ end
389
+ raise GeneratorError.new("source sequence is illegal/malformed utf-8", string) unless string.valid_encoding?
390
+
391
+ if /["\\\x0-\x1f]/.match?(string)
392
+ buf << string.gsub(/["\\\x0-\x1f]/, MAP)
393
+ else
394
+ buf << string
395
+ end
396
+ buf << '"'
397
+ end
398
+
399
+ # Return the value returned by method +name+.
400
+ def [](name)
401
+ if respond_to?(name)
402
+ __send__(name)
403
+ else
404
+ instance_variable_get("@#{name}") if
405
+ instance_variables.include?("@#{name}".to_sym) # avoid warning
406
+ end
407
+ end
408
+
409
+ def []=(name, value)
410
+ if respond_to?(name_writer = "#{name}=")
411
+ __send__ name_writer, value
412
+ else
413
+ instance_variable_set "@#{name}", value
414
+ end
415
+ end
416
+ end
417
+
418
+ module GeneratorMethods
419
+ module Object
420
+ # Converts this object to a string (calling #to_s), converts
421
+ # it to a JSON string, and returns the result. This is a fallback, if no
422
+ # special method #to_json was defined for some object.
423
+ def to_json(state = nil, *)
424
+ state = State.from_state(state) if state
425
+ if state&.strict?
426
+ value = self
427
+ if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
428
+ if state.as_json
429
+ value = state.as_json.call(value)
430
+ unless false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value
431
+ raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
432
+ end
433
+ value.to_json(state)
434
+ else
435
+ raise GeneratorError.new("#{value.class} not allowed in JSON", value)
436
+ end
437
+ end
438
+ else
439
+ to_s.to_json
440
+ end
441
+ end
442
+ end
443
+
444
+ module Hash
445
+ # Returns a JSON string containing a JSON object, that is unparsed from
446
+ # this Hash instance.
447
+ # _state_ is a JSON::State object, that can also be used to configure the
448
+ # produced JSON string output further.
449
+ # _depth_ is used to find out nesting depth, to indent accordingly.
450
+ def to_json(state = nil, *)
451
+ state = State.from_state(state)
452
+ state.check_max_nesting
453
+ json_transform(state)
454
+ end
455
+
456
+ private
457
+
458
+ def json_shift(state)
459
+ state.object_nl.empty? or return ''
460
+ state.indent * state.depth
461
+ end
462
+
463
+ def json_transform(state)
464
+ depth = state.depth += 1
465
+
466
+ if empty?
467
+ state.depth -= 1
468
+ return '{}'
469
+ end
470
+
471
+ delim = ",#{state.object_nl}"
472
+ result = +"{#{state.object_nl}"
473
+ first = true
474
+ indent = !state.object_nl.empty?
475
+ each { |key, value|
476
+ result << delim unless first
477
+ result << state.indent * depth if indent
478
+
479
+ key_str = key.to_s
480
+ if key_str.is_a?(String)
481
+ key_json = key_str.to_json(state)
482
+ else
483
+ raise TypeError, "#{key.class}#to_s returns an instance of #{key_str.class}, expected a String"
484
+ end
485
+
486
+ result = +"#{result}#{key_json}#{state.space_before}:#{state.space}"
487
+ if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
488
+ if state.as_json
489
+ value = state.as_json.call(value)
490
+ unless false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value
491
+ raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
492
+ end
493
+ result << value.to_json(state)
494
+ else
495
+ raise GeneratorError.new("#{value.class} not allowed in JSON", value)
496
+ end
497
+ elsif value.respond_to?(:to_json)
498
+ result << value.to_json(state)
499
+ else
500
+ result << %{"#{String(value)}"}
501
+ end
502
+ first = false
503
+ }
504
+ depth = state.depth -= 1
505
+ unless first
506
+ result << state.object_nl
507
+ result << state.indent * depth if indent
508
+ end
509
+ result << '}'
510
+ result
511
+ end
512
+ end
513
+
514
+ module Array
515
+ # Returns a JSON string containing a JSON array, that is unparsed from
516
+ # this Array instance.
517
+ # _state_ is a JSON::State object, that can also be used to configure the
518
+ # produced JSON string output further.
519
+ def to_json(state = nil, *)
520
+ state = State.from_state(state)
521
+ state.check_max_nesting
522
+ json_transform(state)
523
+ end
524
+
525
+ private
526
+
527
+ def json_transform(state)
528
+ depth = state.depth += 1
529
+
530
+ if empty?
531
+ state.depth -= 1
532
+ return '[]'
533
+ end
534
+
535
+ result = '['.dup
536
+ if state.array_nl.empty?
537
+ delim = ","
538
+ else
539
+ result << state.array_nl
540
+ delim = ",#{state.array_nl}"
541
+ end
542
+
543
+ first = true
544
+ indent = !state.array_nl.empty?
545
+ each { |value|
546
+ result << delim unless first
547
+ result << state.indent * depth if indent
548
+ if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value || Symbol == value)
549
+ if state.as_json
550
+ value = state.as_json.call(value)
551
+ unless false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value || Symbol === value
552
+ raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
553
+ end
554
+ result << value.to_json(state)
555
+ else
556
+ raise GeneratorError.new("#{value.class} not allowed in JSON", value)
557
+ end
558
+ elsif value.respond_to?(:to_json)
559
+ result << value.to_json(state)
560
+ else
561
+ result << %{"#{String(value)}"}
562
+ end
563
+ first = false
564
+ }
565
+ depth = state.depth -= 1
566
+ result << state.array_nl
567
+ result << state.indent * depth if indent
568
+ result << ']'
569
+ end
570
+ end
571
+
572
+ module Integer
573
+ # Returns a JSON string representation for this Integer number.
574
+ def to_json(*) to_s end
575
+ end
576
+
577
+ module Float
578
+ # Returns a JSON string representation for this Float number.
579
+ def to_json(state = nil, *args)
580
+ state = State.from_state(state)
581
+ if infinite? || nan?
582
+ if state.allow_nan?
583
+ to_s
584
+ elsif state.strict? && state.as_json
585
+ casted_value = state.as_json.call(self)
586
+
587
+ if casted_value.equal?(self)
588
+ raise GeneratorError.new("#{self} not allowed in JSON", self)
589
+ end
590
+
591
+ state.check_max_nesting
592
+ state.depth += 1
593
+ result = casted_value.to_json(state, *args)
594
+ state.depth -= 1
595
+ result
596
+ else
597
+ raise GeneratorError.new("#{self} not allowed in JSON", self)
598
+ end
599
+ else
600
+ to_s
601
+ end
602
+ end
603
+ end
604
+
605
+ module Symbol
606
+ def to_json(state = nil, *args)
607
+ state = State.from_state(state)
608
+ if state.strict?
609
+ name.to_json(state, *args)
610
+ else
611
+ super
612
+ end
613
+ end
614
+ end
615
+
616
+ module String
617
+ # This string should be encoded with UTF-8 A call to this method
618
+ # returns a JSON string encoded with UTF16 big endian characters as
619
+ # \u????.
620
+ def to_json(state = nil, *args)
621
+ state = State.from_state(state)
622
+ if encoding == ::Encoding::UTF_8
623
+ unless valid_encoding?
624
+ raise GeneratorError.new("source sequence is illegal/malformed utf-8", self)
625
+ end
626
+ string = self
627
+ else
628
+ string = encode(::Encoding::UTF_8)
629
+ end
630
+ if state.ascii_only?
631
+ %("#{JSON::TruffleRuby::Generator.utf8_to_json_ascii(string, state.script_safe)}")
632
+ else
633
+ %("#{JSON::TruffleRuby::Generator.utf8_to_json(string, state.script_safe)}")
634
+ end
635
+ rescue Encoding::UndefinedConversionError => error
636
+ raise ::JSON::GeneratorError.new(error.message, self)
637
+ end
638
+
639
+ # Module that holds the extending methods if, the String module is
640
+ # included.
641
+ module Extend
642
+ # Raw Strings are JSON Objects (the raw bytes are stored in an
643
+ # array for the key "raw"). The Ruby String can be created by this
644
+ # module method.
645
+ def json_create(o)
646
+ o['raw'].pack('C*')
647
+ end
648
+ end
649
+
650
+ # Extends _modul_ with the String::Extend module.
651
+ def self.included(modul)
652
+ modul.extend Extend
653
+ end
654
+
655
+ # This method creates a raw object hash, that can be nested into
656
+ # other data structures and will be unparsed as a raw string. This
657
+ # method should be used, if you want to convert raw strings to JSON
658
+ # instead of UTF-8 strings, e. g. binary data.
659
+ def to_json_raw_object
660
+ {
661
+ JSON.create_id => self.class.name,
662
+ 'raw' => self.unpack('C*'),
663
+ }
664
+ end
665
+
666
+ # This method creates a JSON text from the result of
667
+ # a call to to_json_raw_object of this String.
668
+ def to_json_raw(*args)
669
+ to_json_raw_object.to_json(*args)
670
+ end
671
+ end
672
+
673
+ module TrueClass
674
+ # Returns a JSON string for true: 'true'.
675
+ def to_json(*) 'true' end
676
+ end
677
+
678
+ module FalseClass
679
+ # Returns a JSON string for false: 'false'.
680
+ def to_json(*) 'false' end
681
+ end
682
+
683
+ module NilClass
684
+ # Returns a JSON string for nil: 'null'.
685
+ def to_json(*) 'null' end
686
+ end
687
+ end
688
+ end
689
+ end
690
+ end
data/lib/json/version.rb CHANGED
@@ -1,9 +1,5 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
+
2
3
  module JSON
3
- # JSON version
4
- VERSION = '2.6.2'
5
- VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
6
- VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
7
- VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
8
- VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
4
+ VERSION = '2.10.1'
9
5
  end