summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <jean.boussier@gmail.com>2025-08-23 16:58:54 +0200
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2025-08-27 10:01:52 +0900
commit0e0f0dfd070fc156ec74c58f44d86a884a0580e0 (patch)
tree35fd9b889a7759ab8e87bdd9ba1ee64400dd528d
parentc3a80ca58226f588ef393ab5ae1de304eabf9a9d (diff)
Fix `JSON::Coder` to cast non-string keys.
-rw-r--r--ext/json/generator/generator.c11
-rwxr-xr-xtest/json/json_coder_test.rb12
2 files changed, 22 insertions, 1 deletions
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
index c71e2f28a7..52dcd24f0e 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -1026,6 +1026,9 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
}
VALUE key_to_s;
+ bool as_json_called = false;
+
+ start:
switch (rb_type(key)) {
case T_STRING:
if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
@@ -1039,7 +1042,13 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
break;
default:
if (data->state->strict) {
- raise_generator_error(key, "%"PRIsVALUE" not allowed in JSON", rb_funcall(key, i_to_s, 0));
+ if (RTEST(data->state->as_json) && !as_json_called) {
+ key = rb_proc_call_with_block(data->state->as_json, 1, &key, Qnil);
+ as_json_called = true;
+ goto start;
+ } else {
+ raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key));
+ }
}
key_to_s = rb_convert_type(key, T_STRING, "String", "to_s");
break;
diff --git a/test/json/json_coder_test.rb b/test/json/json_coder_test.rb
index 9861181910..fc4aba2968 100755
--- a/test/json/json_coder_test.rb
+++ b/test/json/json_coder_test.rb
@@ -18,6 +18,18 @@ class JSONCoderTest < Test::Unit::TestCase
assert_raise(JSON::GeneratorError) { coder.dump([Object.new]) }
end
+ def test_json_coder_hash_key
+ obj = Object.new
+ coder = JSON::Coder.new(&:to_s)
+ assert_equal %({#{obj.to_s.inspect}:1}), coder.dump({ obj => 1 })
+
+ coder = JSON::Coder.new { 42 }
+ error = assert_raise JSON::GeneratorError do
+ coder.dump({ obj => 1 })
+ end
+ assert_equal "Integer not allowed as object key in JSON", error.message
+ end
+
def test_json_coder_options
coder = JSON::Coder.new(array_nl: "\n") do |object|
42