json 2.9.0-java → 2.10.0-java

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74fb4885db76820e52ae4f7bbd590e4adfeef0209e313b1383361ed4160d5fad
4
- data.tar.gz: 9800ac4ee5384282c67ed29ffbc82b91cc0f4ef1caf63e1a9dd573c4ef9cc040
3
+ metadata.gz: 7c1c1f77de7b4d11ae341c57e478018a4e4099df04a747de675a96d04c9b34a8
4
+ data.tar.gz: 3371f68faaf2fc0e463f8832dfec85a159abff5006b339d2012c5cb901cdbeda
5
5
  SHA512:
6
- metadata.gz: 6492a64b044bd77298366f70ed86e8afa4500faa21c083b68389fd76219ea1fce927b743cf0640f4ba7a5793436ac64f64dc4652eb1b31422782f510f636bea7
7
- data.tar.gz: 7625eb024f4756644e84ebfffd14aa371a9e0d1d9be36942e2df1f3c44dd383c444fa4535670f2758430a1c2a3e847b7e48a09c806b32b506f24876145512ae2
6
+ metadata.gz: ac182ab8e520d417bfc69535d640f928aebae3c9c3481c650074445b5f41b721a753e9aa34b32099e2d67e219cb9b5c979dd34873ce77902d776e8929472069f
7
+ data.tar.gz: 26c1acf79a62ad24214dffbd47804b42a4a6e4952b26aab27247607c23fe09d20cfb43e53b909d709fd89288bf70e8797f2a53db35b87691d44c2adbdea5dbd2
data/CHANGES.md CHANGED
@@ -1,5 +1,16 @@
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
+
10
+ ### 2024-12-18 (2.9.1)
11
+
12
+ * Fix support for Solaris 10.
13
+
3
14
  ### 2024-12-03 (2.9.0)
4
15
 
5
16
  * Fix C implementation of `script_safe` escaping to not confuse some other 3 wide characters with `\u2028` and `\u2029`.
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.0'
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.0
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-03 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"