diff options
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | ext/json/ext/generator/generator.c | 143 | ||||
-rw-r--r-- | ext/json/ext/generator/generator.h | 33 | ||||
-rw-r--r-- | lib/json/common.rb | 28 | ||||
-rw-r--r-- | lib/json/pure/generator.rb | 7 | ||||
-rw-r--r-- | lib/json/pure/parser.rb | 55 | ||||
-rw-r--r-- | lib/json/version.rb | 2 | ||||
-rwxr-xr-x | tests/test_json_generate.rb | 45 |
9 files changed, 214 insertions, 106 deletions
@@ -1,3 +1,8 @@ +2010-08-07 (1.4.5) + * Manage data structure nesting depth in state object during generation. This + should reduce problems with to_json method definŃ–tions that only have one + argument. + * Some fixes in the state objects and additional tests. 2010-08-06 (1.4.4) * Fixes build problem for rubinius under OS X, http://github.com/flori/json/issues/closed#issue/25 * Fixes crashes described in http://github.com/flori/json/issues/closed#issue/21 and @@ -1 +1 @@ -1.4.4 +1.4.5 diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 5414cd8..b4cb740 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -14,7 +14,7 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, 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_respond_to_p, i_match, i_keys, i_depth; /* * Copyright 2001-2004 Unicode, Inc. @@ -417,13 +417,12 @@ static FBuffer *fbuffer_dup(FBuffer *fb) */ /* - * call-seq: to_json(state = nil, depth = 0) + * call-seq: to_json(state = nil) * * Returns a JSON string containing a JSON object, that is generated from * this Hash instance. * _state_ is a JSON::State object, that can also be used to configure the * produced JSON string output further. - * _depth_ is used to find out nesting depth, to indent accordingly. */ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) { @@ -431,13 +430,12 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) } /* - * call-seq: to_json(state = nil, depth = 0) + * call-seq: to_json(state = nil) * * Returns a JSON string containing a JSON array, that is generated from * this Array instance. * _state_ is a JSON::State object, that can also be used to configure the * produced JSON string output further. - * _depth_ is used to find out nesting depth, to indent accordingly. */ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { GENERATE_JSON(array); @@ -578,12 +576,12 @@ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) */ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self) { - VALUE state, depth; + VALUE state; VALUE string = rb_funcall(self, i_to_s, 0); - rb_scan_args(argc, argv, "02", &state, &depth); + rb_scan_args(argc, argv, "01", &state); Check_Type(string, T_STRING); state = cState_from_state_s(cState, state); - return cState_partial_generate(state, string, depth); + return cState_partial_generate(state, string); } static void State_free(JSON_Generator_State *state) @@ -679,6 +677,17 @@ static VALUE cState_configure(VALUE self, VALUE opts) state->max_nesting = 0; } } + tmp = ID2SYM(i_depth); + state->depth = 0; + if (option_given_p(opts, tmp)) { + VALUE depth = rb_hash_aref(opts, tmp); + if (RTEST(depth)) { + Check_Type(depth, T_FIXNUM); + state->depth = FIX2LONG(depth); + } else { + state->depth = 0; + } + } tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan)); state->allow_nan = RTEST(tmp); tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only)); @@ -704,6 +713,7 @@ static VALUE cState_to_h(VALUE self) rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse); 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_depth), LONG2FIX(state->depth)); return result; } @@ -722,7 +732,7 @@ static VALUE cState_aref(VALUE self, VALUE name) } } -static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { char *object_nl = state->object_nl; long object_nl_len = state->object_nl_len; @@ -733,9 +743,9 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S long delim_len = FBUFFER_LEN(state->object_delim); char *delim2 = FBUFFER_PTR(state->object_delim2); long delim2_len = FBUFFER_LEN(state->object_delim2); + long depth = ++state->depth; int i, j; VALUE key, key_to_s, keys; - depth++; if (max_nesting != 0 && depth > max_nesting) { fbuffer_free(buffer); rb_raise(eNestingError, "nesting of %ld is too deep", depth); @@ -755,11 +765,11 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S key = rb_ary_entry(keys, i); key_to_s = rb_funcall(key, i_to_s, 0); Check_Type(key_to_s, T_STRING); - generate_json(buffer, Vstate, state, key_to_s, depth); + generate_json(buffer, Vstate, state, key_to_s); fbuffer_append(buffer, delim2, delim2_len); - generate_json(buffer, Vstate, state, rb_hash_aref(obj, key), depth); + generate_json(buffer, Vstate, state, rb_hash_aref(obj, key)); } - depth--; + depth = --state->depth; if (object_nl) { fbuffer_append(buffer, object_nl, object_nl_len); if (indent) { @@ -771,7 +781,7 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S fbuffer_append_char(buffer, '}'); } -static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { char *array_nl = state->array_nl; long array_nl_len = state->array_nl_len; @@ -780,8 +790,8 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St long max_nesting = state->max_nesting; char *delim = FBUFFER_PTR(state->array_delim); long delim_len = FBUFFER_LEN(state->array_delim); + long depth = ++state->depth; int i, j; - depth++; if (max_nesting != 0 && depth > max_nesting) { fbuffer_free(buffer); rb_raise(eNestingError, "nesting of %ld is too deep", depth); @@ -795,9 +805,9 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St fbuffer_append(buffer, indent, indent_len); } } - generate_json(buffer, Vstate, state, rb_ary_entry(obj, i), depth); + generate_json(buffer, Vstate, state, rb_ary_entry(obj, i)); } - depth--; + state->depth = --depth; if (array_nl) { fbuffer_append(buffer, array_nl, array_nl_len); if (indent) { @@ -809,7 +819,7 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St fbuffer_append_char(buffer, ']'); } -static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { fbuffer_append_char(buffer, '"'); #ifdef HAVE_RUBY_ENCODING_H @@ -823,33 +833,33 @@ static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_S fbuffer_append_char(buffer, '"'); } -static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { fbuffer_append(buffer, "null", 4); } -static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { fbuffer_append(buffer, "false", 5); } -static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { fbuffer_append(buffer, "true", 4); } -static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { fbuffer_append_long(buffer, FIX2LONG(obj)); } -static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { VALUE tmp = rb_funcall(obj, i_to_s, 0); fbuffer_append(buffer, RSTRING_PAIR(tmp)); } -static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { double value = RFLOAT_VALUE(obj); char allow_nan = state->allow_nan; @@ -866,36 +876,36 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St fbuffer_append(buffer, RSTRING_PAIR(tmp)); } -static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth) +static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { VALUE tmp; VALUE klass = CLASS_OF(obj); if (klass == rb_cHash) { - generate_json_object(buffer, Vstate, state, obj, depth); + generate_json_object(buffer, Vstate, state, obj); } else if (klass == rb_cArray) { - generate_json_array(buffer, Vstate, state, obj, depth); + generate_json_array(buffer, Vstate, state, obj); } else if (klass == rb_cString) { - generate_json_string(buffer, Vstate, state, obj, depth); + generate_json_string(buffer, Vstate, state, obj); } else if (obj == Qnil) { - generate_json_null(buffer, Vstate, state, obj, depth); + generate_json_null(buffer, Vstate, state, obj); } else if (obj == Qfalse) { - generate_json_false(buffer, Vstate, state, obj, depth); + generate_json_false(buffer, Vstate, state, obj); } else if (obj == Qtrue) { - generate_json_true(buffer, Vstate, state, obj, depth); + generate_json_true(buffer, Vstate, state, obj); } else if (klass == rb_cFixnum) { - generate_json_fixnum(buffer, Vstate, state, obj, depth); + generate_json_fixnum(buffer, Vstate, state, obj); } else if (klass == rb_cBignum) { - generate_json_bignum(buffer, Vstate, state, obj, depth); + generate_json_bignum(buffer, Vstate, state, obj); } else if (klass == rb_cFloat) { - generate_json_float(buffer, Vstate, state, obj, depth); + generate_json_float(buffer, Vstate, state, obj); } else if (rb_respond_to(obj, i_to_json)) { - tmp = rb_funcall(obj, i_to_json, 2, Vstate, INT2FIX(depth + 1)); + tmp = rb_funcall(obj, i_to_json, 1, Vstate); Check_Type(tmp, T_STRING); fbuffer_append(buffer, RSTRING_PAIR(tmp)); } else { tmp = rb_funcall(obj, i_to_s, 0); Check_Type(tmp, T_STRING); - generate_json(buffer, Vstate, state, tmp, depth + 1); + generate_json(buffer, Vstate, state, tmp); } } @@ -936,11 +946,11 @@ static VALUE fbuffer_to_s(FBuffer *fb) return result; } -static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth) +static VALUE cState_partial_generate(VALUE self, VALUE obj) { FBuffer *buffer = cState_prepare_buffer(self); GET_STATE(self); - generate_json(buffer, self, state, obj, NIL_P(depth) ? 0 : FIX2INT(depth)); + generate_json(buffer, self, state, obj); return fbuffer_to_s(buffer); } @@ -953,7 +963,7 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth) */ static VALUE cState_generate(VALUE self, VALUE obj) { - VALUE result = cState_partial_generate(self, obj, Qnil); + VALUE result = cState_partial_generate(self, obj); VALUE re, args[2]; args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z"); args[1] = CRegexp_MULTILINE; @@ -1055,16 +1065,20 @@ static VALUE cState_indent(VALUE self) */ static VALUE cState_indent_set(VALUE self, VALUE indent) { + int len; GET_STATE(self); Check_Type(indent, T_STRING); - if (RSTRING_LEN(indent) == 0) { + len = RSTRING_LEN(indent); + if (len == 0) { if (state->indent) { ruby_xfree(state->indent); state->indent = NULL; + state->indent_len = 0; } } else { if (state->indent) ruby_xfree(state->indent); state->indent = strdup(RSTRING_PTR(indent)); + state->indent_len = len; } return Qnil; } @@ -1089,16 +1103,20 @@ static VALUE cState_space(VALUE self) */ static VALUE cState_space_set(VALUE self, VALUE space) { + int len; GET_STATE(self); Check_Type(space, T_STRING); - if (RSTRING_LEN(space) == 0) { + len = RSTRING_LEN(space); + if (len == 0) { if (state->space) { ruby_xfree(state->space); state->space = NULL; + state->space_len = 0; } } else { if (state->space) ruby_xfree(state->space); state->space = strdup(RSTRING_PTR(space)); + state->space_len = len; } return Qnil; } @@ -1121,16 +1139,20 @@ static VALUE cState_space_before(VALUE self) */ static VALUE cState_space_before_set(VALUE self, VALUE space_before) { + int len; GET_STATE(self); Check_Type(space_before, T_STRING); - if (RSTRING_LEN(space_before) == 0) { + len = RSTRING_LEN(space_before); + if (len == 0) { if (state->space_before) { ruby_xfree(state->space_before); state->space_before = NULL; + state->space_before_len = 0; } } else { if (state->space_before) ruby_xfree(state->space_before); state->space_before = strdup(RSTRING_PTR(space_before)); + state->space_before_len = len; } return Qnil; } @@ -1155,9 +1177,11 @@ static VALUE cState_object_nl(VALUE self) */ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl) { + int len; GET_STATE(self); Check_Type(object_nl, T_STRING); - if (RSTRING_LEN(object_nl) == 0) { + len = RSTRING_LEN(object_nl); + if (len == 0) { if (state->object_nl) { ruby_xfree(state->object_nl); state->object_nl = NULL; @@ -1165,6 +1189,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl) } else { if (state->object_nl) ruby_xfree(state->object_nl); state->object_nl = strdup(RSTRING_PTR(object_nl)); + state->object_nl_len = len; } return Qnil; } @@ -1187,9 +1212,11 @@ static VALUE cState_array_nl(VALUE self) */ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl) { + int len; GET_STATE(self); Check_Type(array_nl, T_STRING); - if (RSTRING_LEN(array_nl) == 0) { + len = RSTRING_LEN(array_nl); + if (len == 0) { if (state->array_nl) { ruby_xfree(state->array_nl); state->array_nl = NULL; @@ -1197,6 +1224,7 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl) } else { if (state->array_nl) ruby_xfree(state->array_nl); state->array_nl = strdup(RSTRING_PTR(array_nl)); + state->array_nl_len = len; } return Qnil; } @@ -1264,6 +1292,30 @@ static VALUE cState_ascii_only_p(VALUE self) } /* + * call-seq: depth + * + * This integer returns the current depth of data structure nesting. + */ +static VALUE cState_depth(VALUE self) +{ + GET_STATE(self); + return LONG2FIX(state->depth); +} + +/* + * call-seq: depth=(depth) + * + * This sets the maximum level of data structure nesting in the generated JSON + * to the integer depth, max_nesting = 0 if no maximum should be checked. + */ +static VALUE cState_depth_set(VALUE self, VALUE depth) +{ + GET_STATE(self); + Check_Type(depth, T_FIXNUM); + return state->depth = FIX2LONG(depth); +} + +/* * */ void Init_generator() @@ -1297,6 +1349,8 @@ void Init_generator() 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); + rb_define_method(cState, "depth", cState_depth, 0); + rb_define_method(cState, "depth=", cState_depth_set, 1); rb_define_method(cState, "configure", cState_configure, 1); rb_define_method(cState, "to_h", cState_to_h, 0); rb_define_method(cState, "[]", cState_aref, 1); @@ -1341,6 +1395,7 @@ void Init_generator() i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_ascii_only = rb_intern("ascii_only"); + i_depth = rb_intern("depth"); i_pack = rb_intern("pack"); i_unpack = rb_intern("unpack"); i_create_id = rb_intern("create_id"); diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h index dbfc4d0..4863fd2 100644 --- a/ext/json/ext/generator/generator.h +++ b/ext/json/ext/generator/generator.h @@ -124,22 +124,23 @@ typedef struct JSON_Generator_StateStruct { long max_nesting; char allow_nan; char ascii_only; + long depth; } JSON_Generator_State; #define GET_STATE(self) \ JSON_Generator_State *state; \ Data_Get_Struct(self, JSON_Generator_State, state) -#define GENERATE_JSON(type) \ +#define GENERATE_JSON(type) \ FBuffer *buffer; \ - VALUE Vstate, depth; \ + VALUE Vstate; \ JSON_Generator_State *state; \ \ - rb_scan_args(argc, argv, "02", &Vstate, &depth); \ + rb_scan_args(argc, argv, "01", &Vstate); \ Vstate = cState_from_state_s(cState, Vstate); \ Data_Get_Struct(Vstate, JSON_Generator_State, state); \ buffer = cState_prepare_buffer(Vstate); \ - generate_json_##type(buffer, Vstate, state, self, NIL_P(depth) ? 0 : FIX2INT(depth)); \ + generate_json_##type(buffer, Vstate, state, self); \ return fbuffer_to_s(buffer) static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self); @@ -161,17 +162,17 @@ static JSON_Generator_State *State_allocate(); static VALUE cState_s_allocate(VALUE klass); static VALUE cState_configure(VALUE self, VALUE opts); static VALUE cState_to_h(VALUE self); -static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth); -static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth); +static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj); +static VALUE cState_partial_generate(VALUE self, VALUE obj); static VALUE cState_generate(VALUE self, VALUE obj); static VALUE cState_initialize(int argc, VALUE *argv, VALUE self); static VALUE cState_from_state_s(VALUE self, VALUE opts); @@ -189,6 +190,8 @@ static VALUE cState_max_nesting(VALUE self); static VALUE cState_max_nesting_set(VALUE self, VALUE depth); static VALUE cState_allow_nan_p(VALUE self); static VALUE cState_ascii_only_p(VALUE self); +static VALUE cState_depth(VALUE self); +static VALUE cState_depth_set(VALUE self, VALUE depth); static FBuffer *cState_prepare_buffer(VALUE self); #endif diff --git a/lib/json/common.rb b/lib/json/common.rb index 244634b..b530670 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -11,9 +11,9 @@ module JSON # generate and parse for their documentation. def [](object, opts = {}) if object.respond_to? :to_str - JSON.parse(object.to_str, opts => {}) + JSON.parse(object.to_str, opts) else - JSON.generate(object, opts => {}) + JSON.generate(object, opts) end end @@ -63,20 +63,20 @@ module JSON end self.state = generator::State const_set :State, self.state - const_set :SAFE_STATE_PROTOTYPE, State.new.freeze + const_set :SAFE_STATE_PROTOTYPE, State.new const_set :FAST_STATE_PROTOTYPE, State.new( :indent => '', :space => '', :object_nl => "", :array_nl => "", :max_nesting => false - ).freeze + ) const_set :PRETTY_STATE_PROTOTYPE, State.new( :indent => ' ', :space => ' ', :object_nl => "\n", :array_nl => "\n" - ).freeze + ) end # Returns the JSON generator modul, that is used by JSON. This might be @@ -196,6 +196,7 @@ module JSON # amount of sanity checks, and the pretty_generate method for some # defaults for a pretty output. def generate(obj, opts = nil) + state = SAFE_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -204,10 +205,7 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end - state = SAFE_STATE_PROTOTYPE.dup state = state.configure(opts) - else - state = SAFE_STATE_PROTOTYPE end state.generate(obj) end @@ -225,6 +223,7 @@ module JSON # *WARNING*: Be careful not to pass any Ruby data structures with circles as # _obj_ argument, because this will cause JSON to go into an infinite loop. def fast_generate(obj, opts = nil) + state = FAST_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -233,10 +232,7 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end - state = FAST_STATE_PROTOTYPE.dup state.configure(opts) - else - state = FAST_STATE_PROTOTYPE end state.generate(obj) end @@ -254,6 +250,7 @@ module JSON # The _opts_ argument can be used to configure the generator, see the # generate method for a more detailed explanation. def pretty_generate(obj, opts = nil) + state = PRETTY_STATE_PROTOTYPE.dup if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -262,10 +259,7 @@ module JSON else raise TypeError, "can't convert #{opts.class} into Hash" end - state = PRETTY_STATE_PROTOTYPE.dup state.configure(opts) - else - state = PRETTY_STATE_PROTOTYPE end state.generate(obj) end @@ -377,11 +371,11 @@ module ::Kernel # # The _opts_ argument is passed through to generate/parse respectively, see # generate and parse for their documentation. - def JSON(object, opts = {}) + def JSON(object, *args) if object.respond_to? :to_str - JSON.parse(object.to_str, opts) + JSON.parse(object.to_str, args.first) else - JSON.generate(object, opts) + JSON.generate(object, args.first) end end end diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 0584108..12959bf 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -163,6 +163,10 @@ module JSON # the generated JSON, max_nesting = 0 if no maximum is checked. attr_accessor :max_nesting + # This integer returns the current depth data structure nesting in the + # generated JSON. + attr_accessor :depth + def check_max_nesting(depth) # :nodoc: return if @max_nesting.zero? current_nesting = depth + 1 @@ -196,6 +200,7 @@ module JSON @array_nl = opts[:array_nl] if opts.key?(:array_nl) @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan) @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only) + @depth = opts[:depth] || 0 if !opts.key?(:max_nesting) # defaults to 19 @max_nesting = 19 elsif opts[:max_nesting] @@ -210,7 +215,7 @@ module JSON # passed to the configure method. def to_h result = {} - for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting] + for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only depth] result[iv.intern] = instance_variable_get("@#{iv}") end result diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index 41b8ac7..844931b 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -69,41 +69,42 @@ module JSON # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array def initialize(source, opts = {}) - if defined?(::Encoding) - if source.encoding == ::Encoding::ASCII_8BIT - b = source[0, 4].bytes.to_a - source = case - when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 - source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) - when b.size >= 4 && b[0] == 0 && b[2] == 0 - source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) - when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 - source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) - - when b.size >= 4 && b[1] == 0 && b[3] == 0 - source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) - else - source.dup - end - else - source = source.encode(::Encoding::UTF_8) - end - source.force_encoding(::Encoding::ASCII_8BIT) - else - b = source + opts ||= {} + if defined?(::Encoding) + if source.encoding == ::Encoding::ASCII_8BIT + b = source[0, 4].bytes.to_a source = case when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 - JSON.iconv('utf-8', 'utf-32be', b) + source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) when b.size >= 4 && b[0] == 0 && b[2] == 0 - JSON.iconv('utf-8', 'utf-16be', b) + source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 - JSON.iconv('utf-8', 'utf-32le', b) + source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) + when b.size >= 4 && b[1] == 0 && b[3] == 0 - JSON.iconv('utf-8', 'utf-16le', b) + source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) else - b + source.dup end + else + source = source.encode(::Encoding::UTF_8) end + source.force_encoding(::Encoding::ASCII_8BIT) + else + b = source + source = case + when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 + JSON.iconv('utf-8', 'utf-32be', b) + when b.size >= 4 && b[0] == 0 && b[2] == 0 + JSON.iconv('utf-8', 'utf-16be', b) + when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 + JSON.iconv('utf-8', 'utf-32le', b) + when b.size >= 4 && b[1] == 0 && b[3] == 0 + JSON.iconv('utf-8', 'utf-16le', b) + else + b + end + end super source if !opts.key?(:max_nesting) # defaults to 19 @max_nesting = 19 diff --git a/lib/json/version.rb b/lib/json/version.rb index ab4972c..ef44e2a 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,6 +1,6 @@ module JSON # JSON version - VERSION = '1.4.4' + VERSION = '1.4.5' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: diff --git a/tests/test_json_generate.rb b/tests/test_json_generate.rb index 26ca1ad..8dc804b 100755 --- a/tests/test_json_generate.rb +++ b/tests/test_json_generate.rb @@ -102,6 +102,51 @@ EOT assert s[:check_circular?] end + def test_pretty_state + state = PRETTY_STATE_PROTOTYPE.dup + assert_equal({ + :allow_nan => false, + :array_nl => "\n", + :ascii_only => false, + :depth => 0, + :indent => " ", + :max_nesting => 19, + :object_nl => "\n", + :space => " ", + :space_before => "", + }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s }) + end + + def test_safe_state + state = SAFE_STATE_PROTOTYPE.dup + assert_equal({ + :allow_nan => false, + :array_nl => "", + :ascii_only => false, + :depth => 0, + :indent => "", + :max_nesting => 19, + :object_nl => "", + :space => "", + :space_before => "", + }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s }) + end + + def test_fast_state + state = FAST_STATE_PROTOTYPE.dup + assert_equal({ + :allow_nan => false, + :array_nl => "", + :ascii_only => false, + :depth => 0, + :indent => "", + :max_nesting => 0, + :object_nl => "", + :space => "", + :space_before => "", + }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s }) + end + def test_allow_nan assert_raises(GeneratorError) { generate([JSON::NaN]) } assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true) |