json 2.15.2-java → 2.17.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: 48d22171870875f79dc860de151f8ad1ca9b3658d70ba4b12b8eb303221108fa
4
- data.tar.gz: f2a4754a3852a4791e3a62fce1966f613fe0167f0cb8748491922e4ddeab170f
3
+ metadata.gz: d56d853c903b3ec52d57a6bfdf5561e8d2201b9154c6bc01b9b6d19975c89fa1
4
+ data.tar.gz: a881ffcd7f7ce84310437d8d962a306ac211027bd445aa00447a0d431c8c63d3
5
5
  SHA512:
6
- metadata.gz: efe4e79202ce017a55d516b2c7f473e63d58c536986130cf68c3f473c9ae8ca819e13d590b79f04b7e845c83425a29b22062bfdeaecd227577b9c56ed94f4e3b
7
- data.tar.gz: 96da2d6068270a844bd6015ac0e67196c19b6c208f49e7adb71ad610662cce066d5d0dc294582a01153853f2186a03e06d20abbd16d7b6faf83dab05c097d84b
6
+ metadata.gz: 63541eade76e9363ed4e5b995a3828ad2b7b0d2f8ef5a7e9a9682496a2e864af05870258b08eda59218a9a928747e274ec93d653b6e4f34c1009fc581bd584e4
7
+ data.tar.gz: ac8045765538bbd965aacfba95cd282331f0b1c874a9fc96475841c336e6b3a2ad183d940b32e7821dbb0cbee960c5b691ac14a3dba1c7eef2ca9295aedc727a
data/CHANGES.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ### Unreleased
4
4
 
5
+ ### 2025-12-03 (2.17.0)
6
+
7
+ * Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument.
8
+ * Fix the parser to no longer ignore invalid escapes in strings.
9
+ Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes.
10
+ * Fixed `JSON::Coder` to use the depth it was initialized with.
11
+ * On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`.
12
+ * Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset
13
+ automatically to its initial value.
14
+ In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to
15
+ generate, and is reset to its initial value. Similarly when `to_json` raises an exception.
16
+
17
+ ### 2025-11-07 (2.16.0)
18
+
19
+ * Deprecate `JSON::State#[]` and `JSON::State#[]=`. Consider using `JSON::Coder` instead.
20
+ * `JSON::Coder` now also yields to the block when encountering strings with invalid encoding.
21
+ * Fix GeneratorError messages to be UTF-8 encoded.
22
+ * Fix memory leak when `Exception` is raised, or `throw` is used during JSON generation.
23
+ * Optimized floating point number parsing by integrating the ryu algorithm (thanks to Josef Šimánek).
24
+ * Optimized numbers parsing using SWAR (thanks to Scott Myron).
25
+ * Optimized parsing of pretty printed documents using SWAR (thanks to Scott Myron).
26
+
5
27
  ### 2025-10-25 (2.15.2)
6
28
 
7
29
  * Fix `JSON::Coder` to have one dedicated depth counter per invocation.
data/LEGAL CHANGED
@@ -6,3 +6,15 @@
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
+ ext/json/ext/vendor/fpconv.h::
11
+ This file is adapted from https://github.com/night-shift/fpconv
12
+ It is licensed under Boost Software License 1.0.
13
+
14
+ ext/json/ext/vendor/jeaiii-ltoa.h::
15
+ This file is adapted from https://github.com/jeaiii/itoa
16
+ It is licensed under the MIT License
17
+
18
+ ext/json/ext/vendor/ryu.h::
19
+ This file is adapted from the Ryu algorithm by Ulf Adams https://github.com/ulfjack/ryu.
20
+ It is dual-licensed under Apache License 2.0 OR Boost Software License 1.0.
data/README.md CHANGED
@@ -113,7 +113,23 @@ puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
113
113
  The provided block is called for all objects that don't have a native JSON equivalent, and
114
114
  must return a Ruby object that has a native JSON equivalent.
115
115
 
116
- It is also called for objects that do have a JSON equivalent, but are used as Hash keys, for instance `{ 1 => 2}`.
116
+ It is also called for objects that do have a JSON equivalent, but are used as Hash keys, for instance `{ 1 => 2}`,
117
+ as well as for strings that aren't valid UTF-8:
118
+
119
+ ```ruby
120
+ coder = JSON::Combining.new do |object, is_object_key|
121
+ case object
122
+ when String
123
+ if !string.valid_encoding? || string.encoding != Encoding::UTF_8
124
+ Base64.encode64(string)
125
+ else
126
+ string
127
+ end
128
+ else
129
+ object
130
+ end
131
+ end
132
+ ```
117
133
 
118
134
  ## Combining JSON fragments
119
135
 
data/lib/json/common.rb CHANGED
@@ -71,8 +71,13 @@ module JSON
71
71
  end
72
72
  when object_class
73
73
  if opts[:create_additions] != false
74
- if class_name = object[JSON.create_id]
75
- klass = JSON.deep_const_get(class_name)
74
+ if class_path = object[JSON.create_id]
75
+ klass = begin
76
+ Object.const_get(class_path)
77
+ rescue NameError => e
78
+ raise ArgumentError, "can't get const #{class_path}: #{e}"
79
+ end
80
+
76
81
  if klass.respond_to?(:json_creatable?) ? klass.json_creatable? : klass.respond_to?(:json_create)
77
82
  create_additions_warning if create_additions.nil?
78
83
  object = klass.json_create(object)
@@ -147,16 +152,6 @@ module JSON
147
152
  const_set :Parser, parser
148
153
  end
149
154
 
150
- # Return the constant located at _path_. The format of _path_ has to be
151
- # either ::A::B::C or A::B::C. In any case, A has to be located at the top
152
- # level (absolute namespace path?). If there doesn't exist a constant at
153
- # the given path, an ArgumentError is raised.
154
- def deep_const_get(path) # :nodoc:
155
- Object.const_get(path)
156
- rescue NameError => e
157
- raise ArgumentError, "can't get const #{path}: #{e}"
158
- end
159
-
160
155
  # Set the module _generator_ to be used by JSON.
161
156
  def generator=(generator) # :nodoc:
162
157
  old, $VERBOSE = $VERBOSE, nil
@@ -555,6 +550,7 @@ module JSON
555
550
  :create_additions => nil,
556
551
  }
557
552
  # :call-seq:
553
+ # JSON.unsafe_load(source, options = {}) -> object
558
554
  # JSON.unsafe_load(source, proc = nil, options = {}) -> object
559
555
  #
560
556
  # Returns the Ruby objects created by parsing the given +source+.
@@ -686,7 +682,12 @@ module JSON
686
682
  #
687
683
  def unsafe_load(source, proc = nil, options = nil)
688
684
  opts = if options.nil?
689
- _unsafe_load_default_options
685
+ if proc && proc.is_a?(Hash)
686
+ options, proc = proc, nil
687
+ options
688
+ else
689
+ _unsafe_load_default_options
690
+ end
690
691
  else
691
692
  _unsafe_load_default_options.merge(options)
692
693
  end
@@ -714,6 +715,7 @@ module JSON
714
715
  end
715
716
 
716
717
  # :call-seq:
718
+ # JSON.load(source, options = {}) -> object
717
719
  # JSON.load(source, proc = nil, options = {}) -> object
718
720
  #
719
721
  # Returns the Ruby objects created by parsing the given +source+.
@@ -850,8 +852,18 @@ module JSON
850
852
  # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
851
853
  #
852
854
  def load(source, proc = nil, options = nil)
855
+ if proc && options.nil? && proc.is_a?(Hash)
856
+ options = proc
857
+ proc = nil
858
+ end
859
+
853
860
  opts = if options.nil?
854
- _load_default_options
861
+ if proc && proc.is_a?(Hash)
862
+ options, proc = proc, nil
863
+ options
864
+ else
865
+ _load_default_options
866
+ end
855
867
  else
856
868
  _load_default_options.merge(options)
857
869
  end
@@ -1053,7 +1065,7 @@ module JSON
1053
1065
  options[:as_json] = as_json if as_json
1054
1066
 
1055
1067
  @state = State.new(options).freeze
1056
- @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options))
1068
+ @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options)).freeze
1057
1069
  end
1058
1070
 
1059
1071
  # call-seq:
@@ -1062,7 +1074,7 @@ module JSON
1062
1074
  #
1063
1075
  # Serialize the given object into a \JSON document.
1064
1076
  def dump(object, io = nil)
1065
- @state.generate_new(object, io)
1077
+ @state.generate(object, io)
1066
1078
  end
1067
1079
  alias_method :generate, :dump
1068
1080
 
@@ -75,6 +75,8 @@ module JSON
75
75
  #
76
76
  # Returns the value returned by method +name+.
77
77
  def [](name)
78
+ ::JSON.deprecation_warning("JSON::State#[] is deprecated and will be removed in json 3.0.0")
79
+
78
80
  if respond_to?(name)
79
81
  __send__(name)
80
82
  else
@@ -87,6 +89,8 @@ module JSON
87
89
  #
88
90
  # Sets the attribute name to value.
89
91
  def []=(name, value)
92
+ ::JSON.deprecation_warning("JSON::State#[]= is deprecated and will be removed in json 3.0.0")
93
+
90
94
  if respond_to?(name_writer = "#{name}=")
91
95
  __send__ name_writer, value
92
96
  else
Binary file
Binary file
@@ -55,6 +55,11 @@ module JSON
55
55
  (Symbol === key || String === key)
56
56
  end
57
57
 
58
+ def self.valid_encoding?(string) # :nodoc:
59
+ return false unless string.encoding == ::Encoding::UTF_8 || string.encoding == ::Encoding::US_ASCII
60
+ string.is_a?(Symbol) || string.valid_encoding?
61
+ end
62
+
58
63
  # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
59
64
  # UTF16 big endian characters as \u????, and return it.
60
65
  def self.utf8_to_json(string, script_safe = false) # :nodoc:
@@ -307,8 +312,8 @@ module JSON
307
312
  def to_h
308
313
  result = {}
309
314
  instance_variables.each do |iv|
310
- iv = iv.to_s[1..-1]
311
- result[iv.to_sym] = self[iv]
315
+ key = iv.to_s[1..-1]
316
+ result[key.to_sym] = instance_variable_get(iv)
312
317
  end
313
318
 
314
319
  if result[:allow_duplicate_key].nil?
@@ -325,6 +330,9 @@ module JSON
325
330
  # created this method raises a
326
331
  # GeneratorError exception.
327
332
  def generate(obj, anIO = nil)
333
+ return dup.generate(obj, anIO) if frozen?
334
+
335
+ depth = @depth
328
336
  if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
329
337
  !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj)
330
338
  result = generate_json(obj, ''.dup)
@@ -341,14 +349,8 @@ module JSON
341
349
  else
342
350
  result
343
351
  end
344
- end
345
-
346
- def generate_new(obj, anIO = nil) # :nodoc:
347
- dup.generate(obj, anIO)
348
- end
349
-
350
- private def initialize_copy(_orig)
351
- @depth = 0
352
+ ensure
353
+ @depth = depth unless frozen?
352
354
  end
353
355
 
354
356
  # Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
@@ -434,6 +436,8 @@ module JSON
434
436
 
435
437
  # Return the value returned by method +name+.
436
438
  def [](name)
439
+ ::JSON.deprecation_warning("JSON::State#[] is deprecated and will be removed in json 3.0.0")
440
+
437
441
  if respond_to?(name)
438
442
  __send__(name)
439
443
  else
@@ -443,6 +447,8 @@ module JSON
443
447
  end
444
448
 
445
449
  def []=(name, value)
450
+ ::JSON.deprecation_warning("JSON::State#[]= is deprecated and will be removed in json 3.0.0")
451
+
446
452
  if respond_to?(name_writer = "#{name}=")
447
453
  __send__ name_writer, value
448
454
  else
@@ -485,8 +491,11 @@ module JSON
485
491
  # _depth_ is used to find out nesting depth, to indent accordingly.
486
492
  def to_json(state = nil, *)
487
493
  state = State.from_state(state)
494
+ depth = state.depth
488
495
  state.check_max_nesting
489
496
  json_transform(state)
497
+ ensure
498
+ state.depth = depth
490
499
  end
491
500
 
492
501
  private
@@ -521,13 +530,17 @@ module JSON
521
530
  end
522
531
  result << state.indent * depth if indent
523
532
 
524
- if state.strict? && !Generator.native_key?(key)
525
- if state.as_json
533
+ if state.strict?
534
+ if state.as_json && (!Generator.native_key?(key) || !Generator.valid_encoding?(key))
526
535
  key = state.as_json.call(key, true)
527
536
  end
528
537
 
529
538
  unless Generator.native_key?(key)
530
- raise GeneratorError.new("#{key.class} not allowed as object key in JSON", value)
539
+ raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key)
540
+ end
541
+
542
+ unless Generator.valid_encoding?(key)
543
+ raise GeneratorError.new("source sequence is illegal/malformed utf-8", key)
531
544
  end
532
545
  end
533
546
 
@@ -546,17 +559,19 @@ module JSON
546
559
  raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
547
560
  end
548
561
  result << value.to_json(state)
562
+ state.depth = depth
549
563
  else
550
564
  raise GeneratorError.new("#{value.class} not allowed in JSON", value)
551
565
  end
552
566
  elsif value.respond_to?(:to_json)
553
567
  result << value.to_json(state)
568
+ state.depth = depth
554
569
  else
555
570
  result << %{"#{String(value)}"}
556
571
  end
557
572
  first = false
558
573
  }
559
- depth = state.depth -= 1
574
+ depth -= 1
560
575
  unless first
561
576
  result << state.object_nl
562
577
  result << state.indent * depth if indent
@@ -573,8 +588,11 @@ module JSON
573
588
  # produced JSON string output further.
574
589
  def to_json(state = nil, *)
575
590
  state = State.from_state(state)
591
+ depth = state.depth
576
592
  state.check_max_nesting
577
593
  json_transform(state)
594
+ ensure
595
+ state.depth = depth
578
596
  end
579
597
 
580
598
  private
@@ -612,12 +630,13 @@ module JSON
612
630
  end
613
631
  elsif value.respond_to?(:to_json)
614
632
  result << value.to_json(state)
633
+ state.depth = depth
615
634
  else
616
635
  result << %{"#{String(value)}"}
617
636
  end
618
637
  first = false
619
638
  }
620
- depth = state.depth -= 1
639
+ depth -= 1
621
640
  result << state.array_nl
622
641
  result << state.indent * depth if indent
623
642
  result << ']'
@@ -642,6 +661,9 @@ module JSON
642
661
  if casted_value.equal?(self)
643
662
  raise GeneratorError.new("#{self} not allowed in JSON", self)
644
663
  end
664
+ unless Generator.native_type?(casted_value)
665
+ raise GeneratorError.new("#{casted_value.class} returned by #{state.as_json} not allowed in JSON", casted_value)
666
+ end
645
667
 
646
668
  state.check_max_nesting
647
669
  state.depth += 1
@@ -674,14 +696,25 @@ module JSON
674
696
  # \u????.
675
697
  def to_json(state = nil, *args)
676
698
  state = State.from_state(state)
677
- if encoding == ::Encoding::UTF_8
678
- unless valid_encoding?
699
+ string = self
700
+
701
+ if state.strict? && state.as_json
702
+ unless Generator.valid_encoding?(string)
703
+ string = state.as_json.call(string, false)
704
+ unless string.is_a?(::String)
705
+ return string.to_json(state, *args)
706
+ end
707
+ end
708
+ end
709
+
710
+ if string.encoding == ::Encoding::UTF_8
711
+ unless string.valid_encoding?
679
712
  raise GeneratorError.new("source sequence is illegal/malformed utf-8", self)
680
713
  end
681
- string = self
682
714
  else
683
- string = encode(::Encoding::UTF_8)
715
+ string = string.encode(::Encoding::UTF_8)
684
716
  end
717
+
685
718
  if state.ascii_only?
686
719
  %("#{JSON::TruffleRuby::Generator.utf8_to_json_ascii(string, state.script_safe)}")
687
720
  else
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.15.2'
4
+ VERSION = '2.17.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.15.2
4
+ version: 2.17.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: 2025-10-25 00:00:00.000000000 Z
11
+ date: 2025-12-03 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