Merge JSON 2.7.2 for Ruby 3.3 (#11541)

Merge JSON 2.7.2
This commit is contained in:
Hiroshi SHIBATA 2024-09-05 00:47:06 +09:00 committed by GitHub
parent ef084cc8f4
commit 4eb51dfc9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 90 additions and 47 deletions

View file

@ -867,7 +867,7 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
if (klass == rb_cString) { if (klass == rb_cString) {
key_to_s = key; key_to_s = key;
} else if (klass == rb_cSymbol) { } else if (klass == rb_cSymbol) {
key_to_s = rb_id2str(SYM2ID(key)); key_to_s = rb_sym2str(key);
} else { } else {
key_to_s = rb_funcall(key, i_to_s, 0); key_to_s = rb_funcall(key, i_to_s, 0);
} }
@ -892,7 +892,6 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
struct hash_foreach_arg arg; struct hash_foreach_arg arg;
if (max_nesting != 0 && depth > max_nesting) { if (max_nesting != 0 && depth > max_nesting) {
fbuffer_free(buffer);
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth); rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
} }
fbuffer_append_char(buffer, '{'); fbuffer_append_char(buffer, '{');
@ -927,7 +926,6 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
long depth = ++state->depth; long depth = ++state->depth;
int i, j; int i, j;
if (max_nesting != 0 && depth > max_nesting) { if (max_nesting != 0 && depth > max_nesting) {
fbuffer_free(buffer);
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth); rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
} }
fbuffer_append_char(buffer, '['); fbuffer_append_char(buffer, '[');
@ -1020,10 +1018,8 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
VALUE tmp = rb_funcall(obj, i_to_s, 0); VALUE tmp = rb_funcall(obj, i_to_s, 0);
if (!allow_nan) { if (!allow_nan) {
if (isinf(value)) { if (isinf(value)) {
fbuffer_free(buffer);
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp)); rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
} else if (isnan(value)) { } else if (isnan(value)) {
fbuffer_free(buffer);
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp)); rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
} }
} }
@ -1096,11 +1092,45 @@ static FBuffer *cState_prepare_buffer(VALUE self)
return buffer; return buffer;
} }
struct generate_json_data {
FBuffer *buffer;
VALUE vstate;
JSON_Generator_State *state;
VALUE obj;
};
static VALUE generate_json_try(VALUE d)
{
struct generate_json_data *data = (struct generate_json_data *)d;
generate_json(data->buffer, data->vstate, data->state, data->obj);
return Qnil;
}
static VALUE generate_json_rescue(VALUE d, VALUE exc)
{
struct generate_json_data *data = (struct generate_json_data *)d;
fbuffer_free(data->buffer);
rb_exc_raise(exc);
return Qundef;
}
static VALUE cState_partial_generate(VALUE self, VALUE obj) static VALUE cState_partial_generate(VALUE self, VALUE obj)
{ {
FBuffer *buffer = cState_prepare_buffer(self); FBuffer *buffer = cState_prepare_buffer(self);
GET_STATE(self); GET_STATE(self);
generate_json(buffer, self, state, obj);
struct generate_json_data data = {
.buffer = buffer,
.vstate = self,
.state = state,
.obj = obj
};
rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
return fbuffer_to_s(buffer); return fbuffer_to_s(buffer);
} }

View file

@ -2,7 +2,10 @@
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json' require 'json'
end end
require 'ostruct' begin
require 'ostruct'
rescue LoadError
end
class OpenStruct class OpenStruct
@ -48,4 +51,4 @@ class OpenStruct
def to_json(*args) def to_json(*args)
as_json.to_json(*args) as_json.to_json(*args)
end end
end end if defined?(::OpenStruct)

View file

@ -1,8 +1,9 @@
#frozen_string_literal: false #frozen_string_literal: false
require 'json/version' require 'json/version'
require 'json/generic_object'
module JSON module JSON
autoload :GenericObject, 'json/generic_object'
NOT_SET = Object.new.freeze NOT_SET = Object.new.freeze
private_constant :NOT_SET private_constant :NOT_SET

View file

@ -1,5 +1,9 @@
#frozen_string_literal: false #frozen_string_literal: false
require 'ostruct' begin
require 'ostruct'
rescue LoadError
warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`."
end
module JSON module JSON
class GenericObject < OpenStruct class GenericObject < OpenStruct
@ -67,5 +71,5 @@ module JSON
def to_json(*a) def to_json(*a)
as_json.to_json(*a) as_json.to_json(*a)
end end
end end if defined?(::OpenStruct)
end end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: false # frozen_string_literal: false
module JSON module JSON
# JSON version # JSON version
VERSION = '2.7.1' VERSION = '2.7.2'
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:

View file

@ -190,7 +190,7 @@ class JSONAdditionTest < Test::Unit::TestCase
# XXX this won't work; o.foo = { :bar => true } # XXX this won't work; o.foo = { :bar => true }
o.foo = { 'bar' => true } o.foo = { 'bar' => true }
assert_equal o, parse(JSON(o), :create_additions => true) assert_equal o, parse(JSON(o), :create_additions => true)
end end if defined?(::OpenStruct)
def test_set def test_set
s = Set.new([:a, :b, :c, :a]) s = Set.new([:a, :b, :c, :a])

View file

@ -79,4 +79,4 @@ class JSONGenericObjectTest < Test::Unit::TestCase
ensure ensure
JSON::GenericObject.json_creatable = false JSON::GenericObject.json_creatable = false
end end
end end if defined?(JSON::GenericObject)

View file

@ -3,7 +3,10 @@
require_relative 'test_helper' require_relative 'test_helper'
require 'stringio' require 'stringio'
require 'tempfile' require 'tempfile'
require 'ostruct' begin
require 'ostruct'
rescue LoadError
end
begin begin
require 'bigdecimal' require 'bigdecimal'
rescue LoadError rescue LoadError
@ -412,21 +415,6 @@ EOT
end end
end end
class SubOpenStruct < OpenStruct
def [](k)
__send__(k)
end
def []=(k, v)
@item_set = true
__send__("#{k}=", v)
end
def item_set?
@item_set
end
end
def test_parse_object_custom_hash_derived_class def test_parse_object_custom_hash_derived_class
res = parse('{"foo":"bar"}', :object_class => SubHash) res = parse('{"foo":"bar"}', :object_class => SubHash)
assert_equal({"foo" => "bar"}, res) assert_equal({"foo" => "bar"}, res)
@ -434,24 +422,41 @@ EOT
assert res.item_set? assert res.item_set?
end end
def test_parse_object_custom_non_hash_derived_class if defined?(::OpenStruct)
res = parse('{"foo":"bar"}', :object_class => SubOpenStruct) class SubOpenStruct < OpenStruct
assert_equal "bar", res.foo def [](k)
assert_equal(SubOpenStruct, res.class) __send__(k)
assert res.item_set? end
end
def test_parse_generic_object def []=(k, v)
res = parse( @item_set = true
'{"foo":"bar", "baz":{}}', __send__("#{k}=", v)
:object_class => JSON::GenericObject end
)
assert_equal(JSON::GenericObject, res.class) def item_set?
assert_equal "bar", res.foo @item_set
assert_equal "bar", res["foo"] end
assert_equal "bar", res[:foo] end
assert_equal "bar", res.to_hash[:foo]
assert_equal(JSON::GenericObject, res.baz.class) def test_parse_object_custom_non_hash_derived_class
res = parse('{"foo":"bar"}', :object_class => SubOpenStruct)
assert_equal "bar", res.foo
assert_equal(SubOpenStruct, res.class)
assert res.item_set?
end
def test_parse_generic_object
res = parse(
'{"foo":"bar", "baz":{}}',
:object_class => JSON::GenericObject
)
assert_equal(JSON::GenericObject, res.class)
assert_equal "bar", res.foo
assert_equal "bar", res["foo"]
assert_equal "bar", res[:foo]
assert_equal "bar", res.to_hash[:foo]
assert_equal(JSON::GenericObject, res.baz.class)
end
end end
def test_generate_core_subclasses_with_new_to_json def test_generate_core_subclasses_with_new_to_json