json 2.9.0 → 2.11.3

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.
@@ -47,17 +47,6 @@ module JSON
47
47
 
48
48
  alias_method :merge, :configure
49
49
 
50
- # call-seq:
51
- # generate(obj) -> String
52
- # generate(obj, anIO) -> anIO
53
- #
54
- # Generates a valid JSON document from object +obj+ and returns the
55
- # result. If no valid JSON document can be created this method raises a
56
- # GeneratorError exception.
57
- def generate(obj, io = nil)
58
- _generate(obj, io)
59
- end
60
-
61
50
  # call-seq: to_h
62
51
  #
63
52
  # Returns the configuration instance variables as a hash, that can be
@@ -69,6 +58,7 @@ module JSON
69
58
  space_before: space_before,
70
59
  object_nl: object_nl,
71
60
  array_nl: array_nl,
61
+ as_json: as_json,
72
62
  allow_nan: allow_nan?,
73
63
  ascii_only: ascii_only?,
74
64
  max_nesting: max_nesting,
data/lib/json/ext.rb CHANGED
@@ -6,15 +6,37 @@ module JSON
6
6
  # This module holds all the modules/classes that implement JSON's
7
7
  # functionality as C extensions.
8
8
  module Ext
9
+ class Parser
10
+ class << self
11
+ def parse(...)
12
+ new(...).parse
13
+ end
14
+ alias_method :parse, :parse # Allow redefinition by extensions
15
+ end
16
+
17
+ def initialize(source, opts = nil)
18
+ @source = source
19
+ @config = Config.new(opts)
20
+ end
21
+
22
+ def source
23
+ @source.dup
24
+ end
25
+
26
+ def parse
27
+ @config.parse(@source)
28
+ end
29
+ end
30
+
31
+ require 'json/ext/parser'
32
+ Ext::Parser::Config = Ext::ParserConfig
33
+ JSON.parser = Ext::Parser
34
+
9
35
  if RUBY_ENGINE == 'truffleruby'
10
- require 'json/ext/parser'
11
36
  require 'json/truffle_ruby/generator'
12
- JSON.parser = Parser
13
37
  JSON.generator = ::JSON::TruffleRuby::Generator
14
38
  else
15
- require 'json/ext/parser'
16
39
  require 'json/ext/generator'
17
- JSON.parser = Parser
18
40
  JSON.generator = Generator
19
41
  end
20
42
  end
@@ -39,30 +39,33 @@ module JSON
39
39
  '\\' => '\\\\',
40
40
  }.freeze # :nodoc:
41
41
 
42
- ESCAPE_PATTERN = /[\/"\\\x0-\x1f]/n # :nodoc:
43
-
44
42
  SCRIPT_SAFE_MAP = MAP.merge(
45
43
  '/' => '\\/',
46
- "\u2028".b => '\u2028',
47
- "\u2029".b => '\u2029',
44
+ "\u2028" => '\u2028',
45
+ "\u2029" => '\u2029',
48
46
  ).freeze
49
47
 
50
- SCRIPT_SAFE_ESCAPE_PATTERN = Regexp.union(ESCAPE_PATTERN, "\u2028".b, "\u2029".b)
48
+ SCRIPT_SAFE_ESCAPE_PATTERN = /[\/"\\\x0-\x1f\u2028-\u2029]/
51
49
 
52
50
  # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
53
51
  # UTF16 big endian characters as \u????, and return it.
54
- def utf8_to_json(string, script_safe = false) # :nodoc:
55
- string = string.b
52
+ def self.utf8_to_json(string, script_safe = false) # :nodoc:
56
53
  if script_safe
57
- string.gsub!(SCRIPT_SAFE_ESCAPE_PATTERN) { SCRIPT_SAFE_MAP[$&] || $& }
54
+ if SCRIPT_SAFE_ESCAPE_PATTERN.match?(string)
55
+ string.gsub(SCRIPT_SAFE_ESCAPE_PATTERN, SCRIPT_SAFE_MAP)
56
+ else
57
+ string
58
+ end
58
59
  else
59
- string.gsub!(ESCAPE_PATTERN) { MAP[$&] || $& }
60
+ if /["\\\x0-\x1f]/.match?(string)
61
+ string.gsub(/["\\\x0-\x1f]/, MAP)
62
+ else
63
+ string
64
+ end
60
65
  end
61
- string.force_encoding(::Encoding::UTF_8)
62
- string
63
66
  end
64
67
 
65
- def utf8_to_json_ascii(original_string, script_safe = false) # :nodoc:
68
+ def self.utf8_to_json_ascii(original_string, script_safe = false) # :nodoc:
66
69
  string = original_string.b
67
70
  map = script_safe ? SCRIPT_SAFE_MAP : MAP
68
71
  string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& }
@@ -86,24 +89,17 @@ module JSON
86
89
  raise GeneratorError.new(e.message, original_string)
87
90
  end
88
91
 
89
- def valid_utf8?(string)
92
+ def self.valid_utf8?(string)
90
93
  encoding = string.encoding
91
94
  (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
92
95
  string.valid_encoding?
93
96
  end
94
- module_function :utf8_to_json, :utf8_to_json_ascii, :valid_utf8?
95
97
 
96
98
  # This class is used to create State instances, that are use to hold data
97
99
  # while generating a JSON text from a Ruby data structure.
98
100
  class State
99
101
  def self.generate(obj, opts = nil, io = nil)
100
- string = new(opts).generate(obj)
101
- if io
102
- io.write(string)
103
- io
104
- else
105
- string
106
- end
102
+ new(opts).generate(obj, io)
107
103
  end
108
104
 
109
105
  # Creates a State object from _opts_, which ought to be Hash to create
@@ -111,16 +107,17 @@ module JSON
111
107
  # an unconfigured instance. If _opts_ is a State object, it is just
112
108
  # returned.
113
109
  def self.from_state(opts)
114
- case
115
- when self === opts
116
- opts
117
- when opts.respond_to?(:to_hash)
118
- new(opts.to_hash)
119
- when opts.respond_to?(:to_h)
120
- new(opts.to_h)
121
- else
122
- SAFE_STATE_PROTOTYPE.dup
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
123
119
  end
120
+ new
124
121
  end
125
122
 
126
123
  # Instantiates a new State object, configured by _opts_.
@@ -148,6 +145,7 @@ module JSON
148
145
  @array_nl = ''
149
146
  @allow_nan = false
150
147
  @ascii_only = false
148
+ @as_json = false
151
149
  @depth = 0
152
150
  @buffer_initial_length = 1024
153
151
  @script_safe = false
@@ -173,6 +171,9 @@ module JSON
173
171
  # This string is put at the end of a line that holds a JSON array.
174
172
  attr_accessor :array_nl
175
173
 
174
+ # This proc converts unsupported types into native JSON types.
175
+ attr_accessor :as_json
176
+
176
177
  # This integer returns the maximum level of data structure nesting in
177
178
  # the generated JSON, max_nesting = 0 if no maximum is checked.
178
179
  attr_accessor :max_nesting
@@ -257,6 +258,7 @@ module JSON
257
258
  @object_nl = opts[:object_nl] || '' if opts.key?(:object_nl)
258
259
  @array_nl = opts[:array_nl] || '' if opts.key?(:array_nl)
259
260
  @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
261
+ @as_json = opts[:as_json].to_proc if opts[:as_json]
260
262
  @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
261
263
  @depth = opts[:depth] || 0
262
264
  @buffer_initial_length ||= opts[:buffer_initial_length]
@@ -299,9 +301,9 @@ module JSON
299
301
  # returns the result. If no valid JSON document can be
300
302
  # created this method raises a
301
303
  # GeneratorError exception.
302
- def generate(obj)
304
+ def generate(obj, anIO = nil)
303
305
  if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
304
- !@ascii_only and !@script_safe and @max_nesting == 0 and !@strict
306
+ !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj)
305
307
  result = generate_json(obj, ''.dup)
306
308
  else
307
309
  result = obj.to_json(self)
@@ -310,7 +312,16 @@ module JSON
310
312
  "source sequence #{result.inspect} is illegal/malformed utf-8",
311
313
  obj
312
314
  )
313
- result
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)
314
325
  end
315
326
 
316
327
  # Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
@@ -353,6 +364,12 @@ module JSON
353
364
  end
354
365
  when Integer
355
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
356
373
  else
357
374
  # Note: Float is handled this way since Float#to_s is slow anyway
358
375
  buf << obj.to_json(self)
@@ -371,8 +388,8 @@ module JSON
371
388
  end
372
389
  raise GeneratorError.new("source sequence is illegal/malformed utf-8", string) unless string.valid_encoding?
373
390
 
374
- if /["\\\x0-\x1f]/n.match?(string)
375
- buf << string.gsub(/["\\\x0-\x1f]/n, MAP)
391
+ if /["\\\x0-\x1f]/.match?(string)
392
+ buf << string.gsub(/["\\\x0-\x1f]/, MAP)
376
393
  else
377
394
  buf << string
378
395
  end
@@ -404,8 +421,20 @@ module JSON
404
421
  # it to a JSON string, and returns the result. This is a fallback, if no
405
422
  # special method #to_json was defined for some object.
406
423
  def to_json(state = nil, *)
407
- if state && State.from_state(state).strict?
408
- raise GeneratorError.new("#{self.class} not allowed in JSON", self)
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
409
438
  else
410
439
  to_s.to_json
411
440
  end
@@ -455,8 +484,16 @@ module JSON
455
484
  end
456
485
 
457
486
  result = +"#{result}#{key_json}#{state.space_before}:#{state.space}"
458
- if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value)
459
- raise GeneratorError.new("#{value.class} not allowed in JSON", value)
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
460
497
  elsif value.respond_to?(:to_json)
461
498
  result << value.to_json(state)
462
499
  else
@@ -508,8 +545,16 @@ module JSON
508
545
  each { |value|
509
546
  result << delim unless first
510
547
  result << state.indent * depth if indent
511
- if state.strict? && !(false == value || true == value || nil == value || String === value || Array === value || Hash === value || Integer === value || Float === value)
512
- raise GeneratorError.new("#{value.class} not allowed in JSON", value)
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
513
558
  elsif value.respond_to?(:to_json)
514
559
  result << value.to_json(state)
515
560
  else
@@ -531,18 +576,23 @@ module JSON
531
576
 
532
577
  module Float
533
578
  # Returns a JSON string representation for this Float number.
534
- def to_json(state = nil, *)
579
+ def to_json(state = nil, *args)
535
580
  state = State.from_state(state)
536
- case
537
- when infinite?
538
- if state.allow_nan?
539
- to_s
540
- else
541
- raise GeneratorError.new("#{self} not allowed in JSON", self)
542
- end
543
- when nan?
581
+ if infinite? || nan?
544
582
  if state.allow_nan?
545
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
546
596
  else
547
597
  raise GeneratorError.new("#{self} not allowed in JSON", self)
548
598
  end
@@ -552,6 +602,17 @@ module JSON
552
602
  end
553
603
  end
554
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
+
555
616
  module String
556
617
  # This string should be encoded with UTF-8 A call to this method
557
618
  # returns a JSON string encoded with UTF16 big endian characters as
data/lib/json/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSON
4
- VERSION = '2.9.0'
4
+ VERSION = '2.11.3'
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.0
4
+ version: 2.11.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-03 00:00:00.000000000 Z
10
+ date: 2025-04-25 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: This is a JSON implementation as a Ruby extension in C.
14
13
  email: flori@ping.de
@@ -29,7 +28,8 @@ files:
29
28
  - ext/json/ext/generator/generator.c
30
29
  - ext/json/ext/parser/extconf.rb
31
30
  - ext/json/ext/parser/parser.c
32
- - ext/json/ext/parser/parser.rl
31
+ - ext/json/ext/vendor/fpconv.c
32
+ - ext/json/ext/vendor/jeaiii-ltoa.h
33
33
  - json.gemspec
34
34
  - lib/json.rb
35
35
  - lib/json/add/bigdecimal.rb
@@ -52,17 +52,15 @@ files:
52
52
  - lib/json/generic_object.rb
53
53
  - lib/json/truffle_ruby/generator.rb
54
54
  - lib/json/version.rb
55
- homepage: https://ruby.github.io/json
55
+ homepage: https://github.com/ruby/json
56
56
  licenses:
57
57
  - Ruby
58
58
  metadata:
59
59
  bug_tracker_uri: https://github.com/ruby/json/issues
60
60
  changelog_uri: https://github.com/ruby/json/blob/master/CHANGES.md
61
- documentation_uri: https://ruby.github.io/json/doc/index.html
62
- homepage_uri: https://ruby.github.io/json
61
+ documentation_uri: https://docs.ruby-lang.org/en/master/JSON.html
62
+ homepage_uri: https://github.com/ruby/json
63
63
  source_code_uri: https://github.com/ruby/json
64
- wiki_uri: https://github.com/ruby/json/wiki
65
- post_install_message:
66
64
  rdoc_options:
67
65
  - "--title"
68
66
  - JSON implementation for Ruby
@@ -81,8 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
79
  - !ruby/object:Gem::Version
82
80
  version: '0'
83
81
  requirements: []
84
- rubygems_version: 3.5.11
85
- signing_key:
82
+ rubygems_version: 3.6.2
86
83
  specification_version: 4
87
84
  summary: JSON Implementation for Ruby
88
85
  test_files: []