diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index efb6b4a0a2..a71acfbb76 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -16,7 +16,7 @@ static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth, - i_buffer_initial_length, i_dup, i_script_safe, i_escape_slash; + i_buffer_initial_length, i_dup, i_script_safe, i_escape_slash, i_strict; /* * Copyright 2001-2004 Unicode, Inc. @@ -749,6 +749,8 @@ static VALUE cState_configure(VALUE self, VALUE opts) tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash)); state->script_safe = RTEST(tmp); } + tmp = rb_hash_aref(opts, ID2SYM(i_strict)); + state->strict = RTEST(tmp); return self; } @@ -784,6 +786,7 @@ static VALUE cState_to_h(VALUE self) rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting)); rb_hash_aset(result, ID2SYM(i_script_safe), state->script_safe ? Qtrue : Qfalse); + rb_hash_aset(result, ID2SYM(i_strict), state->strict ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth)); rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length)); return result; @@ -1049,6 +1052,8 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s generate_json_bignum(buffer, Vstate, state, obj); } else if (klass == rb_cFloat) { generate_json_float(buffer, Vstate, state, obj); + } else if (state->strict) { + rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(CLASS_OF(obj))); } else if (rb_respond_to(obj, i_to_json)) { tmp = rb_funcall(obj, i_to_json, 1, Vstate); Check_Type(tmp, T_STRING); @@ -1423,7 +1428,7 @@ static VALUE cState_script_safe(VALUE self) } /* - * call-seq: script_safe=(depth) + * call-seq: script_safe=(enable) * * This sets whether or not the forward slashes will be escaped in * the json output. @@ -1435,6 +1440,37 @@ static VALUE cState_script_safe_set(VALUE self, VALUE enable) return Qnil; } +/* + * call-seq: strict + * + * If this boolean is false, types unsupported by the JSON format will + * be serialized as strings. + * If this boolean is true, types unsupported by the JSON format will + * raise a JSON::GeneratorError. + */ +static VALUE cState_strict(VALUE self) +{ + GET_STATE(self); + return state->strict ? Qtrue : Qfalse; +} + +/* + * call-seq: strict=(enable) + * + * This sets whether or not to serialize types unsupported by the + * JSON format as strings. + * If this boolean is false, types unsupported by the JSON format will + * be serialized as strings. + * If this boolean is true, types unsupported by the JSON format will + * raise a JSON::GeneratorError. + */ +static VALUE cState_strict_set(VALUE self, VALUE enable) +{ + GET_STATE(self); + state->strict = RTEST(enable); + return Qnil; +} + /* * call-seq: allow_nan? * @@ -1557,6 +1593,9 @@ void Init_generator(void) rb_define_alias(cState, "escape_slash", "script_safe"); rb_define_alias(cState, "escape_slash?", "script_safe?"); rb_define_alias(cState, "escape_slash=", "script_safe="); + rb_define_method(cState, "strict", cState_strict, 0); + rb_define_method(cState, "strict?", cState_strict, 0); + rb_define_method(cState, "strict=", cState_strict_set, 1); rb_define_method(cState, "check_circular?", cState_check_circular_p, 0); rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0); rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0); @@ -1615,6 +1654,7 @@ void Init_generator(void) i_max_nesting = rb_intern("max_nesting"); i_script_safe = rb_intern("script_safe"); i_escape_slash = rb_intern("escape_slash"); + i_strict = rb_intern("strict"); i_allow_nan = rb_intern("allow_nan"); i_ascii_only = rb_intern("ascii_only"); i_depth = rb_intern("depth"); diff --git a/ext/json/generator/generator.h b/ext/json/generator/generator.h index 5e6a228040..1a736b84dd 100644 --- a/ext/json/generator/generator.h +++ b/ext/json/generator/generator.h @@ -73,6 +73,7 @@ typedef struct JSON_Generator_StateStruct { char allow_nan; char ascii_only; char script_safe; + char strict; long depth; long buffer_initial_length; } JSON_Generator_State; @@ -153,6 +154,8 @@ static VALUE cState_depth(VALUE self); static VALUE cState_depth_set(VALUE self, VALUE depth); static VALUE cState_script_safe(VALUE self); static VALUE cState_script_safe_set(VALUE self, VALUE depth); +static VALUE cState_strict(VALUE self); +static VALUE cState_strict_set(VALUE self, VALUE strict); static FBuffer *cState_prepare_buffer(VALUE self); #ifndef ZALLOC #define ZALLOC(type) ((type *)ruby_zalloc(sizeof(type))) diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb index 68ff0efcd0..53b66a9bf6 100644 --- a/ext/json/lib/json/common.rb +++ b/ext/json/lib/json/common.rb @@ -3,6 +3,9 @@ require 'json/version' require 'json/generic_object' module JSON + NOT_SET = Object.new.freeze + private_constant :NOT_SET + class << self # :call-seq: # JSON[object] -> new_array or new_string @@ -608,7 +611,7 @@ module JSON # puts File.read(path) # Output: # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"} - def dump(obj, anIO = nil, limit = nil) + def dump(obj, anIO = nil, limit = nil, strict: NOT_SET) if anIO and limit.nil? anIO = anIO.to_io if anIO.respond_to?(:to_io) unless anIO.respond_to?(:write) @@ -618,6 +621,7 @@ module JSON end opts = JSON.dump_default_options opts = opts.merge(:max_nesting => limit) if limit + opts[:strict] = strict if NOT_SET != strict result = generate(obj, opts) if anIO anIO.write result diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index bcd0816f90..57f87432b6 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -1902,7 +1902,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->max_nesting = 100; json->allow_nan = 0; json->create_additions = 0; - json->create_id = rb_funcall(mJSON, i_create_id, 0); + json->create_id = Qnil; json->object_class = Qnil; json->array_class = Qnil; json->decimal_class = Qnil; diff --git a/ext/json/parser/parser.rl b/ext/json/parser/parser.rl index e6d5d3c152..af190e7500 100644 --- a/ext/json/parser/parser.rl +++ b/ext/json/parser/parser.rl @@ -797,7 +797,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->max_nesting = 100; json->allow_nan = 0; json->create_additions = 0; - json->create_id = rb_funcall(mJSON, i_create_id, 0); + json->create_id = Qnil; json->object_class = Qnil; json->array_class = Qnil; json->decimal_class = Qnil; diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 1bf4912ec7..46cbf9c7f7 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -150,6 +150,7 @@ EOT :buffer_initial_length => 1024, :depth => 0, :script_safe => false, + :strict => false, :indent => " ", :max_nesting => 100, :object_nl => "\n", @@ -167,6 +168,7 @@ EOT :buffer_initial_length => 1024, :depth => 0, :script_safe => false, + :strict => false, :indent => "", :max_nesting => 100, :object_nl => "", @@ -184,6 +186,7 @@ EOT :buffer_initial_length => 1024, :depth => 0, :script_safe => false, + :strict => false, :indent => "", :max_nesting => 0, :object_nl => "", @@ -336,7 +339,13 @@ EOT def test_json_generate assert_raise JSON::GeneratorError do - assert_equal true, generate(["\xea"]) + generate(["\xea"]) + end + end + + def test_json_generate_unsupported_types + assert_raise JSON::GeneratorError do + generate(Object.new, strict: true) end end