mirror of
https://github.com/ruby/ruby.git
synced 2026-10-29 17:06:21 +08:00
[ruby/json] Reject incompatible RFC 8785 generation options
https://github.com/ruby/json/commit/49e8df0e16
This commit is contained in:
1 parent
523192880c
commit
95db6abe45
4 files changed
+80
-1
No files matched your search
@@ -1307,10 +1307,28 @@ static void generate_json_no_fallback(FBuffer *buffer, struct generate_json_data
|
||||
generate_json_general(buffer, data, obj, false);
|
||||
}
|
||||
|
||||
static void validate_rfc8785(JSON_Generator_State *state)
|
||||
{
|
||||
if (!state->rfc8785) return;
|
||||
|
||||
const char *option = state->indent ? "indent" :
|
||||
state->space ? "space" :
|
||||
state->space_before ? "space_before" :
|
||||
state->object_nl ? "object_nl" :
|
||||
state->array_nl ? "array_nl" :
|
||||
state->ascii_only ? "ascii_only" :
|
||||
state->script_safe ? "script_safe" :
|
||||
state->allow_nan ? "allow_nan" : NULL;
|
||||
if (option) {
|
||||
rb_raise(rb_eArgError, "%s cannot be used with rfc8785", option);
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE generate_json_try(VALUE d)
|
||||
{
|
||||
struct generate_json_data *data = (struct generate_json_data *)d;
|
||||
|
||||
validate_rfc8785(data->state);
|
||||
data->func(data->buffer, data, data->obj);
|
||||
|
||||
return fbuffer_finalize(data->buffer);
|
||||
@@ -1990,6 +2008,7 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con
|
||||
rb_hash_foreach(config, configure_state_i, (VALUE)&data);
|
||||
|
||||
raise_argument_error_on_unknown_keywords(data.unknown_keywords);
|
||||
validate_rfc8785(state);
|
||||
}
|
||||
|
||||
static VALUE cState_configure(VALUE self, VALUE opts)
|
||||
|
||||
@@ -418,6 +418,10 @@ require 'json/common'
|
||||
# as defined in RFC8785.
|
||||
# Integers are converted to Float before formatting and may be rounded.
|
||||
# Integers outside the finite Float range raise JSON::GeneratorError.
|
||||
# Nonempty +indent+, +space+, +space_before+, +object_nl+, or +array_nl+,
|
||||
# and enabled +ascii_only+, +script_safe+, or +allow_nan+ raise ArgumentError.
|
||||
# JSON.dump defaults +allow_nan+ to +false+ when +rfc8785+ is enabled.
|
||||
# JSON::Fragment contents are inserted as is; the caller must ensure they are canonical.
|
||||
#
|
||||
# In this example, +obj+ is used first to generate the shortest
|
||||
# \JSON data (no whitespace), then again with all formatting options
|
||||
|
||||
@@ -763,7 +763,7 @@ module JSON
|
||||
end
|
||||
|
||||
opts = {
|
||||
allow_nan: true,
|
||||
allow_nan: !(kwargs && kwargs[:rfc8785]),
|
||||
}
|
||||
opts[:max_nesting] = _deprecated_limit if _deprecated_limit
|
||||
opts.merge!(kwargs) if kwargs
|
||||
|
||||
@@ -1325,6 +1325,62 @@ class JSONGeneratorTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
{
|
||||
indent: ' ', space: ' ', space_before: ' ', object_nl: "\n", array_nl: "\n",
|
||||
ascii_only: true, script_safe: true, allow_nan: true,
|
||||
}.each do |option, value|
|
||||
define_method("test_rfc8785_rejects_#{option}") do
|
||||
options = { rfc8785: true, option => value }
|
||||
error = assert_raise(ArgumentError) { JSON.generate({ 'a' => [1] }, options) }
|
||||
assert_equal "#{option} cannot be used with rfc8785", error.message
|
||||
assert_raise(ArgumentError) { JSON.generate(nil, options.to_a.reverse.to_h) }
|
||||
assert_raise(ArgumentError) { [1].to_json(options) }
|
||||
assert_raise(ArgumentError) { { 'a' => 1 }.to_json(options) }
|
||||
assert_raise(ArgumentError) { JSON::Coder.new(**options).dump([1]) }
|
||||
|
||||
state = JSON::State.new(option => value)
|
||||
state.rfc8785 = true
|
||||
io = StringIO.new
|
||||
assert_raise(ArgumentError) { state.generate([1], io) }
|
||||
assert_equal '', io.string
|
||||
assert_raise(ArgumentError) { [1].to_json(state) }
|
||||
assert_equal 0, state.depth
|
||||
assert_raise(ArgumentError) { state.freeze.generate([1]) }
|
||||
end
|
||||
end
|
||||
|
||||
def test_rfc8785_accepts_disabled_options
|
||||
options = { rfc8785: true, indent: '', space: '', space_before: '',
|
||||
object_nl: '', array_nl: '', ascii_only: false,
|
||||
script_safe: false, allow_nan: false }
|
||||
assert_equal '["é/",1]', JSON.generate(['é/', 1], options)
|
||||
end
|
||||
|
||||
def test_rfc8785_rejects_pretty_generation
|
||||
assert_raise(ArgumentError) { JSON.pretty_generate({ 'a' => 1 }, rfc8785: true) }
|
||||
end
|
||||
|
||||
def test_rfc8785_dump_disables_allow_nan_by_default
|
||||
assert_equal '[1]', JSON.dump([1], rfc8785: true)
|
||||
[Float::NAN, Float::INFINITY, -Float::INFINITY].each do |number|
|
||||
assert_raise(JSON::GeneratorError) { JSON.dump([number], rfc8785: true) }
|
||||
end
|
||||
assert_raise(ArgumentError) { JSON.dump([1], rfc8785: true, allow_nan: true) }
|
||||
end
|
||||
|
||||
def test_rfc8785_fragments
|
||||
fragment = JSON::Fragment.new('[1,2]')
|
||||
assert_rfc8785 '[1,2]', fragment
|
||||
assert_rfc8785 '[[1,2]]', [fragment]
|
||||
assert_rfc8785 '{"a":[1,2]}', { 'a' => fragment }
|
||||
assert_equal '[[1,2]]', JSON.generate([fragment], rfc8785: true, max_nesting: false)
|
||||
assert_equal '[1,2]', JSON::Coder.new(rfc8785: true).dump(fragment)
|
||||
assert_equal '[1,2]', fragment.to_json(rfc8785: true)
|
||||
|
||||
# Fragment contents are the caller's responsibility and are inserted as is.
|
||||
assert_rfc8785 '[1, 2]', JSON::Fragment.new('[1, 2]')
|
||||
end
|
||||
|
||||
def test_rfc8785_numbers
|
||||
assert_rfc8785 '-9007199254740992', -9007199254740992
|
||||
assert_rfc8785 '0', 0
|
||||
|
||||
Reference in new issue
Block a user