json 2.9.1-java → 2.10.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fbe500aeaaee4ca7ecdd33333c6867ecaa7f4dabbfb7ee0dd951009650494ec
4
- data.tar.gz: 6faa1e8eb9a2cc5f98c01bfc26d2b0b84e814455e7f6829bfd3b617181078650
3
+ metadata.gz: 7c1c1f77de7b4d11ae341c57e478018a4e4099df04a747de675a96d04c9b34a8
4
+ data.tar.gz: 3371f68faaf2fc0e463f8832dfec85a159abff5006b339d2012c5cb901cdbeda
5
5
  SHA512:
6
- metadata.gz: 95285612fa42036d37652f18239c85ed51550d6fd87bd5eccc78a13cccd68f687d1ed915d30d46bff3b5b5fd9bf3f9fee5fe6cfd498a1fdbc89f8d657b62966a
7
- data.tar.gz: f08899fdf14ef2d8e5e8693176394763becce85f2d735f7961c0ed37862315bc67f421439c978a8785609dcb0790112122021af10f25efc428a2bba1637970bd
6
+ metadata.gz: ac182ab8e520d417bfc69535d640f928aebae3c9c3481c650074445b5f41b721a753e9aa34b32099e2d67e219cb9b5c979dd34873ce77902d776e8929472069f
7
+ data.tar.gz: 26c1acf79a62ad24214dffbd47804b42a4a6e4952b26aab27247607c23fe09d20cfb43e53b909d709fd89288bf70e8797f2a53db35b87691d44c2adbdea5dbd2
data/CHANGES.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changes
2
2
 
3
+ ### 2025-02-10 (2.10.0)
4
+
5
+ * `strict: true` now accept symbols as values. Previously they'd only be accepted as hash keys.
6
+ * The C extension Parser has been entirely reimplemented from scratch.
7
+ * Introduced `JSON::Coder` as a new API allowing to customize how non native types are serialized in a non-global way.
8
+ * The Java implementation of the generator received many optimizations.
9
+
3
10
  ### 2024-12-18 (2.9.1)
4
11
 
5
12
  * Fix support for Solaris 10.
data/LEGAL CHANGED
@@ -6,55 +6,3 @@
6
6
  All the files in this distribution are covered under either the Ruby's
7
7
  license (see the file COPYING) or public-domain except some files
8
8
  mentioned below.
9
-
10
- == MIT License
11
- >>>
12
- Permission is hereby granted, free of charge, to any person obtaining
13
- a copy of this software and associated documentation files (the
14
- "Software"), to deal in the Software without restriction, including
15
- without limitation the rights to use, copy, modify, merge, publish,
16
- distribute, sublicense, and/or sell copies of the Software, and to
17
- permit persons to whom the Software is furnished to do so, subject to
18
- the following conditions:
19
-
20
- The above copyright notice and this permission notice shall be
21
- included in all copies or substantial portions of the Software.
22
-
23
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
-
31
- == Old-style BSD license
32
- >>>
33
- Redistribution and use in source and binary forms, with or without
34
- modification, are permitted provided that the following conditions
35
- are met:
36
- 1. Redistributions of source code must retain the above copyright
37
- notice, this list of conditions and the following disclaimer.
38
- 2. Redistributions in binary form must reproduce the above copyright
39
- notice, this list of conditions and the following disclaimer in the
40
- documentation and/or other materials provided with the distribution.
41
- 3. Neither the name of the University nor the names of its contributors
42
- may be used to endorse or promote products derived from this software
43
- without specific prior written permission.
44
-
45
- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48
- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55
- SUCH DAMAGE.
56
-
57
- IMPORTANT NOTE::
58
-
59
- From ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
60
- paragraph 3 above is now null and void.
data/README.md CHANGED
@@ -29,7 +29,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
29
29
 
30
30
  $ gem install json
31
31
 
32
- ## Usage
32
+ ## Basic Usage
33
33
 
34
34
  To use JSON you can
35
35
 
@@ -52,7 +52,80 @@ You can also use the `pretty_generate` method (which formats the output more
52
52
  verbosely and nicely) or `fast_generate` (which doesn't do any of the security
53
53
  checks generate performs, e. g. nesting deepness checks).
54
54
 
55
- ## Handling arbitrary types
55
+ ## Casting non native types
56
+
57
+ JSON documents can only support Hashes, Arrays, Strings, Integers and Floats.
58
+
59
+ By default if you attempt to serialize something else, `JSON.generate` will
60
+ search for a `#to_json` method on that object:
61
+
62
+ ```ruby
63
+ Position = Struct.new(:latitude, :longitude) do
64
+ def to_json(state = nil, *)
65
+ JSON::State.from_state(state).generate({
66
+ latitude: latitude,
67
+ longitude: longitude,
68
+ })
69
+ end
70
+ end
71
+
72
+ JSON.generate([
73
+ Position.new(12323.234, 435345.233),
74
+ Position.new(23434.676, 159435.324),
75
+ ]) # => [{"latitude":12323.234,"longitude":435345.233},{"latitude":23434.676,"longitude":159435.324}]
76
+ ```
77
+
78
+ If a `#to_json` method isn't defined on the object, `JSON.generate` will fallback to call `#to_s`:
79
+
80
+ ```ruby
81
+ JSON.generate(Object.new) # => "#<Object:0x000000011e768b98>"
82
+ ```
83
+
84
+ Both of these behavior can be disabled using the `strict: true` option:
85
+
86
+ ```ruby
87
+ JSON.generate(Object.new, strict: true) # => Object not allowed in JSON (JSON::GeneratorError)
88
+ JSON.generate(Position.new(1, 2)) # => Position not allowed in JSON (JSON::GeneratorError)
89
+ ```
90
+
91
+ ## JSON::Coder
92
+
93
+ Since `#to_json` methods are global, it can sometimes be problematic if you need a given type to be
94
+ serialized in different ways in different locations.
95
+
96
+ Instead it is recommended to use the newer `JSON::Coder` API:
97
+
98
+ ```ruby
99
+ module MyApp
100
+ API_JSON_CODER = JSON::Coder.new do |object|
101
+ case object
102
+ when Time
103
+ object.iso8601(3)
104
+ else
105
+ object
106
+ end
107
+ end
108
+ end
109
+
110
+ puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
111
+ ```
112
+
113
+ The provided block is called for all objects that don't have a native JSON equivalent, and
114
+ must return a Ruby object that has a native JSON equivalent.
115
+
116
+ ## Combining JSON fragments
117
+
118
+ To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`:
119
+
120
+ ```ruby
121
+ posts_json = cache.fetch_multi(post_ids) do |post_id|
122
+ JSON.generate(Post.find(post_id))
123
+ end
124
+ posts_json.map! { |post_json| JSON::Fragment.new(post_json) }
125
+ JSON.generate({ posts: posts_json, count: posts_json.count })
126
+ ```
127
+
128
+ ## Round-tripping arbitrary types
56
129
 
57
130
  > [!CAUTION]
58
131
  > You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input,
data/json.gemspec CHANGED
@@ -11,14 +11,13 @@ spec = Gem::Specification.new do |s|
11
11
  s.version = version
12
12
 
13
13
  s.summary = "JSON Implementation for Ruby"
14
- s.homepage = "https://ruby.github.io/json"
14
+ s.homepage = "https://github.com/ruby/json"
15
15
  s.metadata = {
16
16
  'bug_tracker_uri' => 'https://github.com/ruby/json/issues',
17
17
  'changelog_uri' => 'https://github.com/ruby/json/blob/master/CHANGES.md',
18
- 'documentation_uri' => 'https://ruby.github.io/json/doc/index.html',
18
+ 'documentation_uri' => 'https://docs.ruby-lang.org/en/master/JSON.html',
19
19
  'homepage_uri' => s.homepage,
20
20
  'source_code_uri' => 'https://github.com/ruby/json',
21
- 'wiki_uri' => 'https://github.com/ruby/json/wiki'
22
21
  }
23
22
 
24
23
  s.required_ruby_version = Gem::Requirement.new(">= 2.7")
@@ -53,7 +52,7 @@ spec = Gem::Specification.new do |s|
53
52
  s.files += Dir["lib/json/ext/**/*.jar"]
54
53
  else
55
54
  s.extensions = Dir["ext/json/**/extconf.rb"]
56
- s.files += Dir["ext/json/**/*.{c,h,rl}"]
55
+ s.files += Dir["ext/json/**/*.{c,h}"]
57
56
  end
58
57
  end
59
58
 
@@ -36,8 +36,13 @@ class Symbol
36
36
  #
37
37
  # # {"json_class":"Symbol","s":"foo"}
38
38
  #
39
- def to_json(*a)
40
- as_json.to_json(*a)
39
+ def to_json(state = nil, *a)
40
+ state = ::JSON::State.from_state(state)
41
+ if state.strict?
42
+ super
43
+ else
44
+ as_json.to_json(state, *a)
45
+ end
41
46
  end
42
47
 
43
48
  # See #as_json.
data/lib/json/common.rb CHANGED
@@ -167,6 +167,30 @@ module JSON
167
167
  # system. Usually this means that the iconv library is not installed.
168
168
  class MissingUnicodeSupport < JSONError; end
169
169
 
170
+ # Fragment of JSON document that is to be included as is:
171
+ # fragment = JSON::Fragment.new("[1, 2, 3]")
172
+ # JSON.generate({ count: 3, items: fragments })
173
+ #
174
+ # This allows to easily assemble multiple JSON fragments that have
175
+ # been persisted somewhere without having to parse them nor resorting
176
+ # to string interpolation.
177
+ #
178
+ # Note: no validation is performed on the provided string. It is the
179
+ # responsability of the caller to ensure the string contains valid JSON.
180
+ Fragment = Struct.new(:json) do
181
+ def initialize(json)
182
+ unless string = String.try_convert(json)
183
+ raise TypeError, " no implicit conversion of #{json.class} into String"
184
+ end
185
+
186
+ super(string)
187
+ end
188
+
189
+ def to_json(state = nil, *)
190
+ json
191
+ end
192
+ end
193
+
170
194
  module_function
171
195
 
172
196
  # :call-seq:
@@ -232,12 +256,13 @@ module JSON
232
256
  # - Option +max_nesting+, if not provided, defaults to +false+,
233
257
  # which disables checking for nesting depth.
234
258
  # - Option +allow_nan+, if not provided, defaults to +true+.
235
- def parse!(source, opts = {})
236
- opts = {
259
+ def parse!(source, opts = nil)
260
+ options = {
237
261
  :max_nesting => false,
238
262
  :allow_nan => true
239
- }.merge(opts)
240
- Parser.new(source, **(opts||{})).parse
263
+ }
264
+ options.merge!(opts) if opts
265
+ Parser.new(source, options).parse
241
266
  end
242
267
 
243
268
  # :call-seq:
@@ -258,7 +283,7 @@ module JSON
258
283
  # JSON.parse!(File.read(path, opts))
259
284
  #
260
285
  # See method #parse!
261
- def load_file!(filespec, opts = {})
286
+ def load_file!(filespec, opts = nil)
262
287
  parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
263
288
  end
264
289
 
@@ -818,11 +843,7 @@ module JSON
818
843
  opts = merge_dump_options(opts, **kwargs) if kwargs
819
844
 
820
845
  begin
821
- if State === opts
822
- opts.generate(obj, anIO)
823
- else
824
- State.generate(obj, opts, anIO)
825
- end
846
+ State.generate(obj, opts, anIO)
826
847
  rescue JSON::NestingError
827
848
  raise ArgumentError, "exceed depth limit"
828
849
  end
@@ -841,6 +862,82 @@ module JSON
841
862
  class << self
842
863
  private :merge_dump_options
843
864
  end
865
+
866
+ # JSON::Coder holds a parser and generator configuration.
867
+ #
868
+ # module MyApp
869
+ # JSONC_CODER = JSON::Coder.new(
870
+ # allow_trailing_comma: true
871
+ # )
872
+ # end
873
+ #
874
+ # MyApp::JSONC_CODER.load(document)
875
+ #
876
+ class Coder
877
+ # :call-seq:
878
+ # JSON.new(options = nil, &block)
879
+ #
880
+ # Argument +options+, if given, contains a \Hash of options for both parsing and generating.
881
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options], and {Generating Options}[#module-JSON-label-Generating+Options].
882
+ #
883
+ # For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
884
+ # encoutered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
885
+ # \JSON counterpart:
886
+ #
887
+ # module MyApp
888
+ # API_JSON_CODER = JSON::Coder.new do |object|
889
+ # case object
890
+ # when Time
891
+ # object.iso8601(3)
892
+ # else
893
+ # object # Unknown type, will raise
894
+ # end
895
+ # end
896
+ # end
897
+ #
898
+ # puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
899
+ #
900
+ def initialize(options = nil, &as_json)
901
+ if options.nil?
902
+ options = { strict: true }
903
+ else
904
+ options = options.dup
905
+ options[:strict] = true
906
+ end
907
+ options[:as_json] = as_json if as_json
908
+ options[:create_additions] = false unless options.key?(:create_additions)
909
+
910
+ @state = State.new(options).freeze
911
+ @parser_config = Ext::Parser::Config.new(options)
912
+ end
913
+
914
+ # call-seq:
915
+ # dump(object) -> String
916
+ # dump(object, io) -> io
917
+ #
918
+ # Serialize the given object into a \JSON document.
919
+ def dump(object, io = nil)
920
+ @state.generate_new(object, io)
921
+ end
922
+ alias_method :generate, :dump
923
+
924
+ # call-seq:
925
+ # load(string) -> Object
926
+ #
927
+ # Parse the given \JSON document and return an equivalent Ruby object.
928
+ def load(source)
929
+ @parser_config.parse(source)
930
+ end
931
+ alias_method :parse, :load
932
+
933
+ # call-seq:
934
+ # load(path) -> Object
935
+ #
936
+ # Parse the given \JSON document and return an equivalent Ruby object.
937
+ def load_file(path)
938
+ load(File.read(path, encoding: Encoding::UTF_8))
939
+ end
940
+ end
844
941
  end
845
942
 
846
943
  module ::Kernel
@@ -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,
Binary file
Binary file
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
+ SAFE_STATE_PROTOTYPE.dup
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.key?(: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.1'
4
+ VERSION = '2.10.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.1
4
+ version: 2.10.0
5
5
  platform: java
6
6
  authors:
7
7
  - Daniel Luz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-18 00:00:00.000000000 Z
11
+ date: 2025-02-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A JSON implementation as a JRuby extension.
14
14
  email: dev+ruby@mernen.com
@@ -46,16 +46,15 @@ files:
46
46
  - lib/json/generic_object.rb
47
47
  - lib/json/truffle_ruby/generator.rb
48
48
  - lib/json/version.rb
49
- homepage: https://ruby.github.io/json
49
+ homepage: https://github.com/ruby/json
50
50
  licenses:
51
51
  - Ruby
52
52
  metadata:
53
53
  bug_tracker_uri: https://github.com/ruby/json/issues
54
54
  changelog_uri: https://github.com/ruby/json/blob/master/CHANGES.md
55
- documentation_uri: https://ruby.github.io/json/doc/index.html
56
- homepage_uri: https://ruby.github.io/json
55
+ documentation_uri: https://docs.ruby-lang.org/en/master/JSON.html
56
+ homepage_uri: https://github.com/ruby/json
57
57
  source_code_uri: https://github.com/ruby/json
58
- wiki_uri: https://github.com/ruby/json/wiki
59
58
  post_install_message:
60
59
  rdoc_options:
61
60
  - "--title"