diff options
-rwxr-xr-x | benchmarks/generator2_benchmark.rb | 8 | ||||
-rwxr-xr-x | benchmarks/generator_benchmark.rb | 8 | ||||
-rwxr-xr-x | benchmarks/parser_benchmark.rb | 12 | ||||
-rw-r--r-- | ext/json/ext/generator/generator.c | 54 | ||||
-rw-r--r-- | ext/json/ext/generator/unicode.c | 87 | ||||
-rw-r--r-- | ext/json/ext/generator/unicode.h | 1 | ||||
-rw-r--r-- | lib/json/pure/generator.rb | 46 | ||||
-rw-r--r-- | tests/test_json_encoding.rb | 7 | ||||
-rwxr-xr-x | tests/test_json_unicode.rb | 26 |
9 files changed, 197 insertions, 52 deletions
diff --git a/benchmarks/generator2_benchmark.rb b/benchmarks/generator2_benchmark.rb index 10cf711..38653cb 100755 --- a/benchmarks/generator2_benchmark.rb +++ b/benchmarks/generator2_benchmark.rb @@ -53,6 +53,12 @@ module JSONGeneratorCommon end alias reset_benchmark_generator_pretty generic_reset_method + + def benchmark_generator_ascii + @result = JSON.generate(@big, :ascii_only => true) + end + + alias reset_benchmark_generator_ascii generic_reset_method end class Generator2BenchmarkExt < Bullshit::RepeatCase @@ -193,9 +199,11 @@ if $0 == __FILE__ benchmark Generator2BenchmarkExt, :generator_fast, :load => yes benchmark Generator2BenchmarkExt, :generator_safe, :load => yes benchmark Generator2BenchmarkExt, :generator_pretty, :load => yes + benchmark Generator2BenchmarkExt, :generator_ascii, :load => yes benchmark Generator2BenchmarkPure, :generator_fast, :load => yes benchmark Generator2BenchmarkPure, :generator_safe, :load => yes benchmark Generator2BenchmarkPure, :generator_pretty, :load => yes + benchmark Generator2BenchmarkPure, :generator_ascii, :load => yes benchmark Generator2BenchmarkRails, :generator, :load => yes benchmark Generator2BenchmarkYajl, :generator, :load => yes end diff --git a/benchmarks/generator_benchmark.rb b/benchmarks/generator_benchmark.rb index 539fb91..79ac4d8 100755 --- a/benchmarks/generator_benchmark.rb +++ b/benchmarks/generator_benchmark.rb @@ -55,6 +55,12 @@ module JSONGeneratorCommon end alias reset_benchmark_generator_pretty generic_reset_method + + def benchmark_generator_ascii + @result = JSON.generate(@big, :ascii_only => true) + end + + alias reset_benchmark_generator_ascii generic_reset_method end class GeneratorBenchmarkExt < Bullshit::RepeatCase @@ -195,9 +201,11 @@ if $0 == __FILE__ benchmark GeneratorBenchmarkExt, :generator_fast, :load => yes benchmark GeneratorBenchmarkExt, :generator_safe, :load => yes benchmark GeneratorBenchmarkExt, :generator_pretty, :load => yes + benchmark GeneratorBenchmarkExt, :generator_ascii, :load => yes benchmark GeneratorBenchmarkPure, :generator_fast, :load => yes benchmark GeneratorBenchmarkPure, :generator_safe, :load => yes benchmark GeneratorBenchmarkPure, :generator_pretty, :load => yes + benchmark GeneratorBenchmarkPure, :generator_ascii, :load => yes benchmark GeneratorBenchmarkRails, :generator, :load => yes benchmark GeneratorBenchmarkYajl, :generator, :load => yes end diff --git a/benchmarks/parser_benchmark.rb b/benchmarks/parser_benchmark.rb index 7ac027e..2dbad2a 100755 --- a/benchmarks/parser_benchmark.rb +++ b/benchmarks/parser_benchmark.rb @@ -221,9 +221,9 @@ if $0 == __FILE__ ParserBenchmarkYajl.run else system "#{RAKE_PATH} clean" - #system "#{RUBY_PATH} #$0 yaml" - #system "#{RUBY_PATH} #$0 rails" - #system "#{RUBY_PATH} #$0 pure" + system "#{RUBY_PATH} #$0 yaml" + system "#{RUBY_PATH} #$0 rails" + system "#{RUBY_PATH} #$0 pure" system "#{RAKE_PATH} compile_ext" system "#{RUBY_PATH} #$0 ext" system "#{RUBY_PATH} #$0 yajl" @@ -231,9 +231,9 @@ if $0 == __FILE__ output_filename File.join(File.dirname(__FILE__), 'data', 'ParserBenchmarkComparison.log') benchmark ParserBenchmarkExt, :parser, :load => yes - #benchmark ParserBenchmarkPure, :parser, :load => yes - #benchmark ParserBenchmarkYAML, :parser, :load => yes - #benchmark ParserBenchmarkRails, :parser, :load => yes + benchmark ParserBenchmarkPure, :parser, :load => yes + benchmark ParserBenchmarkYAML, :parser, :load => yes + benchmark ParserBenchmarkRails, :parser, :load => yes benchmark ParserBenchmarkYajl, :parser, :load => yes end end diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 36580ad..dce6fef 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -16,6 +16,7 @@ #endif inline static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth); +inline static VALUE cState_from_state_s(VALUE self, VALUE opts); #ifndef RHASH_TBL #define RHASH_TBL(hsh) (RHASH(hsh)->tbl) @@ -45,7 +46,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_pack, i_unpack, i_create_id, i_extend; + i_allow_nan, i_ascii_only, i_pack, i_unpack, i_create_id, i_extend; typedef struct JSON_Generator_StateStruct { VALUE indent; @@ -56,8 +57,8 @@ typedef struct JSON_Generator_StateStruct { FBuffer *delim; FBuffer *delim2; long max_nesting; - int flag; - int allow_nan; + char allow_nan; + char ascii_only; } JSON_Generator_State; #define GET_STATE(self) \ @@ -92,7 +93,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) VALUE state, depth; rb_scan_args(argc, argv, "02", &state, &depth); - if (NIL_P(state)) state = rb_class_new_instance(0, NULL, cState); + state = cState_from_state_s(cState, state); return cState_partial_generate(state, self, depth); } @@ -108,7 +109,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self) static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) { VALUE state, depth; rb_scan_args(argc, argv, "02", &state, &depth); - if (NIL_P(state)) state = rb_class_new_instance(0, NULL, cState); + state = cState_from_state_s(cState, state); return cState_partial_generate(state, self, depth); } @@ -121,7 +122,7 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self) { VALUE state, depth; rb_scan_args(argc, argv, "02", &state, &depth); - if (NIL_P(state)) state = rb_class_new_instance(0, NULL, cState); + state = cState_from_state_s(cState, state); return cState_partial_generate(state, self, depth); } @@ -134,7 +135,7 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self) { VALUE state, depth; rb_scan_args(argc, argv, "02", &state, &depth); - if (NIL_P(state)) state = rb_class_new_instance(0, NULL, cState); + state = cState_from_state_s(cState, state); return cState_partial_generate(state, self, depth); } @@ -159,7 +160,7 @@ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self) { VALUE state, depth; rb_scan_args(argc, argv, "02", &state, &depth); - if (NIL_P(state)) state = rb_class_new_instance(0, NULL, cState); + state = cState_from_state_s(cState, state); return cState_partial_generate(state, self, depth); } @@ -214,7 +215,7 @@ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self) { VALUE state, depth; rb_scan_args(argc, argv, "02", &state, &depth); - if (NIL_P(state)) state = rb_class_new_instance(0, NULL, cState); + state = cState_from_state_s(cState, state); return cState_partial_generate(state, self, depth); } @@ -227,7 +228,7 @@ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self) { VALUE state, depth; rb_scan_args(argc, argv, "02", &state, &depth); - if (NIL_P(state)) state = rb_class_new_instance(0, NULL, cState); + state = cState_from_state_s(cState, state); return cState_partial_generate(state, self, depth); } @@ -239,7 +240,7 @@ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self) { VALUE state, depth; rb_scan_args(argc, argv, "02", &state, &depth); - if (NIL_P(state)) state = rb_class_new_instance(0, NULL, cState); + state = cState_from_state_s(cState, state); return cState_partial_generate(state, self, depth); } @@ -256,7 +257,7 @@ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self) VALUE string = rb_funcall(self, i_to_s, 0); rb_scan_args(argc, argv, "02", &state, &depth); Check_Type(string, T_STRING); - if (NIL_P(state)) state = rb_class_new_instance(0, NULL, cState); + state = cState_from_state_s(cState, state); return cState_partial_generate(state, string, depth); } @@ -348,6 +349,8 @@ static VALUE cState_configure(VALUE self, VALUE opts) } tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan)); state->allow_nan = RTEST(tmp); + tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only)); + state->ascii_only = RTEST(tmp); return self; } @@ -367,6 +370,7 @@ static VALUE cState_to_h(VALUE self) rb_hash_aset(result, ID2SYM(i_object_nl), state->object_nl); rb_hash_aset(result, ID2SYM(i_array_nl), state->array_nl); 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)); return result; } @@ -476,15 +480,13 @@ void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, V case T_STRING: fbuffer_append_char(buffer, '"'); #ifdef HAVE_RUBY_ENCODING_H - if (rb_funcall(obj, i_encoding, 0) == CEncoding_UTF_8) { + obj = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8); +#endif + if (state->ascii_only) { JSON_convert_UTF8_to_JSON_ASCII(buffer, obj); } else { - VALUE string = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8); - JSON_convert_UTF8_to_JSON_ASCII(buffer, string); + JSON_convert_UTF8_to_JSON(buffer, obj); } -#else - JSON_convert_UTF8_to_JSON_ASCII(buffer, obj); -#endif fbuffer_append_char(buffer, '"'); break; case T_NIL: @@ -598,7 +600,7 @@ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self) * new State instance configured by _opts_, something else to create an * unconfigured instance. If _opts_ is a State object, it is just returned. */ -static VALUE cState_from_state_s(VALUE self, VALUE opts) +inline static VALUE cState_from_state_s(VALUE self, VALUE opts) { if (rb_obj_is_kind_of(opts, self)) { return opts; @@ -786,6 +788,18 @@ static VALUE cState_allow_nan_p(VALUE self) } /* + * call-seq: ascii_only? + * + * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise + * returns false. + */ +static VALUE cState_ascii_only_p(VALUE self) +{ + GET_STATE(self); + return state->ascii_only ? Qtrue : Qfalse; +} + +/* * */ void Init_generator() @@ -816,6 +830,7 @@ void Init_generator() rb_define_method(cState, "max_nesting", cState_max_nesting, 0); rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1); 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, "configure", cState_configure, 1); rb_define_method(cState, "to_h", cState_to_h, 0); rb_define_method(cState, "generate", cState_generate, 1); @@ -857,6 +872,7 @@ void Init_generator() i_array_nl = rb_intern("array_nl"); i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); + i_ascii_only = rb_intern("ascii_only"); i_pack = rb_intern("pack"); i_unpack = rb_intern("unpack"); i_create_id = rb_intern("create_id"); diff --git a/ext/json/ext/generator/unicode.c b/ext/json/ext/generator/unicode.c index 2084c60..53a2ec1 100644 --- a/ext/json/ext/generator/unicode.c +++ b/ext/json/ext/generator/unicode.c @@ -86,23 +86,29 @@ inline static unsigned char isLegalUTF8(const UTF8 *source, int length) return 1; } -inline static void unicode_escape(FBuffer *buffer, UTF16 character) +inline static void unicode_escape(char *buf, UTF16 character) { const char *digits = "0123456789abcdef"; - char buf[7] = { '\\', 'u' }; - buf[6] = 0; buf[2] = digits[character >> 12]; buf[3] = digits[(character >> 8) & 0xf]; buf[4] = digits[(character >> 4) & 0xf]; buf[5] = digits[character & 0xf]; +} + + +inline static void unicode_escape_to_buffer(FBuffer *buffer, char buf[7], UTF16 character) +{ + unicode_escape(buf, character); fbuffer_append(buffer, buf, 6); } inline void JSON_convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string) { - const UTF8* source = (UTF8 *) RSTRING_PTR(string); - const UTF8* sourceEnd = source + RSTRING_LEN(string); + const UTF8 *source = (UTF8 *) RSTRING_PTR(string); + const UTF8 *sourceEnd = source + RSTRING_LEN(string); + char buf[7] = { '\\', 'u' }; + buf[6] = 0; while (source < sourceEnd) { UTF32 ch = 0; @@ -136,11 +142,11 @@ inline void JSON_convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string) rb_raise(rb_path2class("JSON::GeneratorError"), "source sequence is illegal/malformed utf-8"); #else - unicode_escape(buffer, UNI_REPLACEMENT_CHAR); + unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR); #endif } else { /* normal case */ - switch(ch) { + switch (ch) { case '\n': fbuffer_append(buffer, "\\n", 2); break; @@ -166,7 +172,7 @@ inline void JSON_convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string) if (ch >= 0x20 && ch <= 0x7f) { fbuffer_append_char(buffer, ch); } else { - unicode_escape(buffer, (UTF16) ch); + unicode_escape_to_buffer(buffer, buf, (UTF16) ch); } break; } @@ -177,13 +183,72 @@ inline void JSON_convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string) rb_raise(rb_path2class("JSON::GeneratorError"), "source sequence is illegal/malformed utf8"); #else - unicode_escape(buffer, UNI_REPLACEMENT_CHAR); + unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR); #endif } else { /* target is a character in range 0xFFFF - 0x10FFFF. */ ch -= halfBase; - unicode_escape(buffer, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START)); - unicode_escape(buffer, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START)); + unicode_escape_to_buffer(buffer, buf, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START)); + unicode_escape_to_buffer(buffer, buf, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START)); + } + } +} + +inline void JSON_convert_UTF8_to_JSON(FBuffer *buffer, VALUE string) +{ + const char *ptr = RSTRING_PTR(string), *p; + int len = RSTRING_LEN(string), start = 0, end = 0; + const char *escape = NULL; + int escape_len; + char buf[7] = { '\\', 'u' }; + buf[6] = 0; + + for (start = 0, end = 0; end < len;) { + p = ptr + end; + switch (*p) { + case '\n': + escape = "\\n"; + escape_len = 2; + break; + case '\r': + escape = "\\r"; + escape_len = 2; + break; + case '\\': + escape = "\\\\"; + escape_len = 2; + break; + case '"': + escape = "\\\""; + escape_len = 2; + break; + case '\t': + escape = "\\t"; + escape_len = 2; + break; + case '\f': + escape = "\\f"; + escape_len = 2; + break; + case '\b': + escape = "\\b"; + escape_len = 2; + break; + default: + if ((unsigned char) *p < 0x20) { + unicode_escape(buf, (UTF16) *p); + escape = buf; + escape_len = 6; + } else { + end++; + continue; + } + break; } + fbuffer_append(buffer, ptr + start, end - start); + fbuffer_append(buffer, escape, escape_len); + start = ++end; + escape = NULL; } + fbuffer_append(buffer, ptr + start, end - start); } diff --git a/ext/json/ext/generator/unicode.h b/ext/json/ext/generator/unicode.h index a9abf5b..b827119 100644 --- a/ext/json/ext/generator/unicode.h +++ b/ext/json/ext/generator/unicode.h @@ -27,6 +27,7 @@ static const UTF32 halfBase = 0x0010000UL; static const UTF32 halfMask = 0x3FFUL; inline void JSON_convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string); +inline void JSON_convert_UTF8_to_JSON(FBuffer *buffer, VALUE string); #ifndef RARRAY_PTR #define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 715acba..8358988 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -44,6 +44,15 @@ module JSON string << '' # XXX workaround: avoid buffer sharing string.force_encoding(::Encoding::ASCII_8BIT) string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] } + string.force_encoding(::Encoding::UTF_8) + string + end + + def utf8_to_json_ascii(string) # :nodoc: + string = string.dup + string << '' # XXX workaround: avoid buffer sharing + string.force_encoding(::Encoding::ASCII_8BIT) + string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] } string.gsub!(/( (?: [\xc2-\xdf][\x80-\xbf] | @@ -63,6 +72,10 @@ module JSON end else def utf8_to_json(string) # :nodoc: + string.gsub(/["\\\x0-\x1f]/) { MAP[$&] } + end + + def utf8_to_json_ascii(string) # :nodoc: string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] } string.gsub!(/( (?: @@ -81,7 +94,7 @@ module JSON raise GeneratorError, "Caught #{e.class}: #{e}" end end - module_function :utf8_to_json + module_function :utf8_to_json, :utf8_to_json_ascii module Pure module Generator @@ -125,6 +138,7 @@ module JSON @object_nl = '' @array_nl = '' @allow_nan = false + @ascii_only = false configure opts end @@ -168,6 +182,10 @@ module JSON @allow_nan end + def ascii_only? + @ascii_only + end + # Configure this State instance with the Hash _opts_, and return # itself. def configure(opts) @@ -177,6 +195,7 @@ module JSON @object_nl = opts[:object_nl] if opts.key?(:object_nl) @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) if !opts.key?(:max_nesting) # defaults to 19 @max_nesting = 19 elsif opts[:max_nesting] @@ -343,11 +362,17 @@ module JSON # This string should be encoded with UTF-8 A call to this method # returns a JSON string encoded with UTF16 big endian characters as # \u????. - def to_json(*) + def to_json(*args) + state, = *args + state ||= JSON.state.from_state(state) if encoding == ::Encoding::UTF_8 - '"' << JSON.utf8_to_json(self) << '"' + string = self else string = encode(::Encoding::UTF_8) + end + if state.ascii_only? + '"' << JSON.utf8_to_json_ascii(string) << '"' + else '"' << JSON.utf8_to_json(string) << '"' end end @@ -355,16 +380,23 @@ module JSON # This string should be encoded with UTF-8 A call to this method # returns a JSON string encoded with UTF16 big endian characters as # \u????. - def to_json(*) - '"' << JSON.utf8_to_json(self) << '"' + def to_json(*args) + state, = *args + state ||= JSON.state.from_state(state) + if state.ascii_only? + '"' << JSON.utf8_to_json_ascii(self) << '"' + else + '"' << JSON.utf8_to_json(self) << '"' + end end end # Module that holds the extinding methods if, the String module is # included. module Extend - # Raw Strings are JSON Objects (the raw bytes are stored in an array for the - # key "raw"). The Ruby String can be created by this module method. + # Raw Strings are JSON Objects (the raw bytes are stored in an + # array for the key "raw"). The Ruby String can be created by this + # module method. def json_create(o) o['raw'].pack('C*') end diff --git a/tests/test_json_encoding.rb b/tests/test_json_encoding.rb index bfb3e60..fdea329 100644 --- a/tests/test_json_encoding.rb +++ b/tests/test_json_encoding.rb @@ -57,11 +57,12 @@ class TC_JSONEncoding < Test::Unit::TestCase end def test_generate - assert_equal @generated, JSON.generate(@parsed) + assert_equal @generated, JSON.generate(@parsed, :ascii_only => true) if defined?(::Encoding) - assert_equal @generated, JSON.generate(@utf_16_data) + assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true) else - assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data) } + # XXX checking of correct utf8 data is not as strict (yet?) without :ascii_only + assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data, :ascii_only => true) } end end end diff --git a/tests/test_json_unicode.rb b/tests/test_json_unicode.rb index 1454fe1..505f5d5 100755 --- a/tests/test_json_unicode.rb +++ b/tests/test_json_unicode.rb @@ -19,22 +19,36 @@ class TC_JSONUnicode < Test::Unit::TestCase assert_equal '" "', ' '.to_json assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json utf8 = [ "© ≠ €! \01" ] + json = '["© ≠ €! \u0001"]' + assert_equal json, utf8.to_json(:ascii_only => false) + assert_equal utf8, parse(json) json = '["\u00a9 \u2260 \u20ac! \u0001"]' - assert_equal json, utf8.to_json + assert_equal json, utf8.to_json(:ascii_only => true) + assert_equal utf8, parse(json) + utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"] + json = "[\"\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212\"]" assert_equal utf8, parse(json) + assert_equal json, utf8.to_json(:ascii_only => false) utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"] + assert_equal utf8, parse(json) json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]" - assert_equal json, utf8.to_json + assert_equal json, utf8.to_json(:ascii_only => true) assert_equal utf8, parse(json) utf8 = ['საქართველო'] + json = '["საქართველო"]' + assert_equal json, utf8.to_json(:ascii_only => false) json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]" - assert_equal json, utf8.to_json + assert_equal json, utf8.to_json(:ascii_only => true) assert_equal utf8, parse(json) - assert_equal '["\\u00c3"]', JSON.generate(["Ã"]) + assert_equal '["Ã"]', JSON.generate(["Ã"], :ascii_only => false) + assert_equal '["\\u00c3"]', JSON.generate(["Ã"], :ascii_only => true) assert_equal ["€"], JSON.parse('["\u20ac"]') utf8 = ["\xf0\xa0\x80\x81"] + json = "[\"\xf0\xa0\x80\x81\"]" + assert_equal json, JSON.generate(utf8, :ascii_only => false) + assert_equal utf8, JSON.parse(json) json = '["\ud840\udc01"]' - assert_equal json, JSON.generate(utf8) + assert_equal json, JSON.generate(utf8, :ascii_only => true) assert_equal utf8, JSON.parse(json) end @@ -55,7 +69,7 @@ class TC_JSONUnicode < Test::Unit::TestCase end end assert_raise(JSON::GeneratorError) do - JSON.generate(["\x80"]) + JSON.generate(["\x80"], :ascii_only => true) end assert_equal "\302\200", JSON.parse('["\u0080"]').first end |