JSON.generate: call to_json on String subclasses
Fix: https://github.com/ruby/json/issues/667 This is yet another behavior on which the various implementations differed, but the C implementation used to call `to_json` on String subclasses used as keys. This was optimized out in e125072130229e54a651f7b11d7d5a782ae7fb65 but there is an Active Support test case for it, so it's best to make all 3 implementation respect this behavior.
This commit is contained in:
parent
b8b33efd4d
commit
ef5565f5d1
@ -42,6 +42,10 @@ static VALUE fbuffer_to_s(FBuffer *fb);
|
|||||||
#define RB_UNLIKELY(expr) expr
|
#define RB_UNLIKELY(expr) expr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef RB_LIKELY
|
||||||
|
#define RB_LIKELY(expr) expr
|
||||||
|
#endif
|
||||||
|
|
||||||
static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
|
static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
|
||||||
{
|
{
|
||||||
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
|
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
|
||||||
|
@ -737,7 +737,11 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
|
||||||
generate_json_string(buffer, data, state, key_to_s);
|
generate_json_string(buffer, data, state, key_to_s);
|
||||||
|
} else {
|
||||||
|
generate_json(buffer, data, state, key_to_s);
|
||||||
|
}
|
||||||
if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, state->space_before);
|
if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, state->space_before);
|
||||||
fbuffer_append_char(buffer, ':');
|
fbuffer_append_char(buffer, ':');
|
||||||
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, state->space);
|
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, state->space);
|
||||||
|
@ -486,6 +486,41 @@ class JSONGeneratorTest < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class MyCustomString < String
|
||||||
|
def to_json(_state = nil)
|
||||||
|
'"my_custom_key"'
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_subclass_as_keys
|
||||||
|
# Ref: https://github.com/ruby/json/issues/667
|
||||||
|
# if key.to_s doesn't return a bare string, we call `to_json` on it.
|
||||||
|
key = MyCustomString.new("won't be used")
|
||||||
|
assert_equal '{"my_custom_key":1}', JSON.generate(key => 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
class FakeString
|
||||||
|
def to_json(_state = nil)
|
||||||
|
raise "Shouldn't be called"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_custom_object_as_keys
|
||||||
|
key = FakeString.new
|
||||||
|
error = assert_raise(TypeError) do
|
||||||
|
JSON.generate(key => 1)
|
||||||
|
end
|
||||||
|
assert_match "FakeString", error.message
|
||||||
|
end
|
||||||
|
|
||||||
def test_to_json_called_with_state_object
|
def test_to_json_called_with_state_object
|
||||||
object = Object.new
|
object = Object.new
|
||||||
called = false
|
called = false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user