json 2.2.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +51 -0
  3. data/LICENSE +56 -0
  4. data/README.md +17 -1
  5. data/VERSION +1 -1
  6. data/ext/json/ext/generator/generator.c +222 -48
  7. data/ext/json/ext/generator/generator.h +5 -2
  8. data/ext/json/ext/parser/extconf.rb +25 -0
  9. data/ext/json/ext/parser/parser.c +150 -102
  10. data/ext/json/ext/parser/parser.h +1 -0
  11. data/ext/json/ext/parser/parser.rl +80 -32
  12. data/ext/json/extconf.rb +1 -0
  13. data/json.gemspec +0 -0
  14. data/lib/json.rb +549 -29
  15. data/lib/json/add/bigdecimal.rb +2 -2
  16. data/lib/json/add/complex.rb +2 -3
  17. data/lib/json/add/rational.rb +2 -3
  18. data/lib/json/add/regexp.rb +2 -2
  19. data/lib/json/common.rb +370 -125
  20. data/lib/json/pure/generator.rb +31 -10
  21. data/lib/json/pure/parser.rb +31 -5
  22. data/lib/json/version.rb +1 -1
  23. data/tests/fixtures/fail29.json +1 -0
  24. data/tests/fixtures/fail30.json +1 -0
  25. data/tests/fixtures/fail31.json +1 -0
  26. data/tests/fixtures/fail32.json +1 -0
  27. data/tests/json_addition_test.rb +0 -4
  28. data/tests/json_common_interface_test.rb +47 -4
  29. data/tests/json_fixtures_test.rb +9 -1
  30. data/tests/json_generator_test.rb +30 -8
  31. data/tests/json_parser_test.rb +39 -14
  32. data/tests/lib/core_assertions.rb +763 -0
  33. data/tests/lib/envutil.rb +365 -0
  34. data/tests/lib/find_executable.rb +22 -0
  35. data/tests/lib/helper.rb +4 -0
  36. data/tests/ractor_test.rb +30 -0
  37. data/tests/test_helper.rb +3 -3
  38. metadata +30 -40
  39. data/.gitignore +0 -17
  40. data/.travis.yml +0 -23
  41. data/README-json-jruby.md +0 -33
  42. data/Rakefile +0 -408
  43. data/diagrams/.keep +0 -0
  44. data/install.rb +0 -23
  45. data/java/src/json/ext/ByteListTranscoder.java +0 -166
  46. data/java/src/json/ext/Generator.java +0 -443
  47. data/java/src/json/ext/GeneratorMethods.java +0 -231
  48. data/java/src/json/ext/GeneratorService.java +0 -42
  49. data/java/src/json/ext/GeneratorState.java +0 -490
  50. data/java/src/json/ext/OptionsReader.java +0 -113
  51. data/java/src/json/ext/Parser.java +0 -2362
  52. data/java/src/json/ext/Parser.rl +0 -893
  53. data/java/src/json/ext/ParserService.java +0 -34
  54. data/java/src/json/ext/RuntimeInfo.java +0 -116
  55. data/java/src/json/ext/StringDecoder.java +0 -166
  56. data/java/src/json/ext/StringEncoder.java +0 -111
  57. data/java/src/json/ext/Utils.java +0 -88
  58. data/json-java.gemspec +0 -38
  59. data/json_pure.gemspec +0 -38
  60. data/references/rfc7159.txt +0 -899
  61. data/tools/diff.sh +0 -18
  62. data/tools/fuzz.rb +0 -131
  63. data/tools/server.rb +0 -62
@@ -37,6 +37,7 @@ typedef struct JSON_ParserStruct {
37
37
  int allow_nan;
38
38
  int parsing_name;
39
39
  int symbolize_names;
40
+ int freeze;
40
41
  VALUE object_class;
41
42
  VALUE array_class;
42
43
  VALUE decimal_class;
@@ -25,7 +25,7 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
25
25
 
26
26
  /* unicode */
27
27
 
28
- static const char digit_values[256] = {
28
+ static const signed char digit_values[256] = {
29
29
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
30
30
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31
31
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
@@ -44,7 +44,7 @@ static const char digit_values[256] = {
44
44
 
45
45
  static UTF32 unescape_unicode(const unsigned char *p)
46
46
  {
47
- char b;
47
+ signed char b;
48
48
  UTF32 result = 0;
49
49
  b = digit_values[p[0]];
50
50
  if (b < 0) return UNI_REPLACEMENT_CHAR;
@@ -89,13 +89,12 @@ static int convert_UTF32_to_UTF8(char *buf, UTF32 ch)
89
89
 
90
90
  static VALUE mJSON, mExt, cParser, eParserError, eNestingError;
91
91
  static VALUE CNaN, CInfinity, CMinusInfinity;
92
- static VALUE cBigDecimal = Qundef;
93
92
 
94
93
  static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
95
94
  i_chr, i_max_nesting, i_allow_nan, i_symbolize_names,
96
95
  i_object_class, i_array_class, i_decimal_class, i_key_p,
97
96
  i_deep_const_get, i_match, i_match_string, i_aset, i_aref,
98
- i_leftshift, i_new, i_BigDecimal;
97
+ i_leftshift, i_new, i_try_convert, i_freeze, i_uminus;
99
98
 
100
99
  %%{
101
100
  machine JSON_common;
@@ -138,6 +137,7 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions,
138
137
  fhold; fbreak;
139
138
  } else {
140
139
  if (NIL_P(json->object_class)) {
140
+ OBJ_FREEZE(last_name);
141
141
  rb_hash_aset(*result, last_name, v);
142
142
  } else {
143
143
  rb_funcall(*result, i_aset, 2, last_name, v);
@@ -289,6 +289,10 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
289
289
  %% write init;
290
290
  %% write exec;
291
291
 
292
+ if (json->freeze) {
293
+ OBJ_FREEZE(*result);
294
+ }
295
+
292
296
  if (cs >= JSON_value_first_final) {
293
297
  return p;
294
298
  } else {
@@ -340,19 +344,6 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
340
344
  ) (^[0-9Ee.\-]? @exit );
341
345
  }%%
342
346
 
343
- static int is_bigdecimal_class(VALUE obj)
344
- {
345
- if (cBigDecimal == Qundef) {
346
- if (rb_const_defined(rb_cObject, i_BigDecimal)) {
347
- cBigDecimal = rb_const_get_at(rb_cObject, i_BigDecimal);
348
- }
349
- else {
350
- return 0;
351
- }
352
- }
353
- return obj == cBigDecimal;
354
- }
355
-
356
347
  static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
357
348
  {
358
349
  int cs = EVIL;
@@ -362,21 +353,46 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
362
353
  %% write exec;
363
354
 
364
355
  if (cs >= JSON_float_first_final) {
356
+ VALUE mod = Qnil;
357
+ ID method_id = 0;
358
+ if (rb_respond_to(json->decimal_class, i_try_convert)) {
359
+ mod = json->decimal_class;
360
+ method_id = i_try_convert;
361
+ } else if (rb_respond_to(json->decimal_class, i_new)) {
362
+ mod = json->decimal_class;
363
+ method_id = i_new;
364
+ } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) {
365
+ VALUE name = rb_class_name(json->decimal_class);
366
+ const char *name_cstr = RSTRING_PTR(name);
367
+ const char *last_colon = strrchr(name_cstr, ':');
368
+ if (last_colon) {
369
+ const char *mod_path_end = last_colon - 1;
370
+ VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
371
+ mod = rb_path_to_class(mod_path);
372
+
373
+ const char *method_name_beg = last_colon + 1;
374
+ long before_len = method_name_beg - name_cstr;
375
+ long len = RSTRING_LEN(name) - before_len;
376
+ VALUE method_name = rb_str_substr(name, before_len, len);
377
+ method_id = SYM2ID(rb_str_intern(method_name));
378
+ } else {
379
+ mod = rb_mKernel;
380
+ method_id = SYM2ID(rb_str_intern(name));
381
+ }
382
+ }
383
+
365
384
  long len = p - json->memo;
366
385
  fbuffer_clear(json->fbuffer);
367
386
  fbuffer_append(json->fbuffer, json->memo, len);
368
387
  fbuffer_append_char(json->fbuffer, '\0');
369
- if (NIL_P(json->decimal_class)) {
370
- *result = rb_float_new(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
388
+
389
+ if (method_id) {
390
+ VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
391
+ *result = rb_funcallv(mod, method_id, 1, &text);
371
392
  } else {
372
- VALUE text;
373
- text = rb_str_new2(FBUFFER_PTR(json->fbuffer));
374
- if (is_bigdecimal_class(json->decimal_class)) {
375
- *result = rb_funcall(Qnil, i_BigDecimal, 1, text);
376
- } else {
377
- *result = rb_funcall(json->decimal_class, i_new, 1, text);
378
- }
393
+ *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1));
379
394
  }
395
+
380
396
  return p + 1;
381
397
  } else {
382
398
  return NULL;
@@ -571,10 +587,23 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
571
587
 
572
588
  if (json->symbolize_names && json->parsing_name) {
573
589
  *result = rb_str_intern(*result);
574
- } else {
575
- if (RB_TYPE_P(*result, T_STRING)) {
576
- rb_str_resize(*result, RSTRING_LEN(*result));
577
- }
590
+ } else if (RB_TYPE_P(*result, T_STRING)) {
591
+ # if STR_UMINUS_DEDUPE_FROZEN
592
+ if (json->freeze) {
593
+ // Starting from MRI 2.8 it is preferable to freeze the string
594
+ // before deduplication so that it can be interned directly
595
+ // otherwise it would be duplicated first which is wasteful.
596
+ *result = rb_funcall(rb_str_freeze(*result), i_uminus, 0);
597
+ }
598
+ # elif STR_UMINUS_DEDUPE
599
+ if (json->freeze) {
600
+ // MRI 2.5 and older do not deduplicate strings that are already
601
+ // frozen.
602
+ *result = rb_funcall(*result, i_uminus, 0);
603
+ }
604
+ # else
605
+ rb_str_resize(*result, RSTRING_LEN(*result));
606
+ # endif
578
607
  }
579
608
  if (cs >= JSON_string_first_final) {
580
609
  return p + 1;
@@ -682,6 +711,12 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
682
711
  } else {
683
712
  json->symbolize_names = 0;
684
713
  }
714
+ tmp = ID2SYM(i_freeze);
715
+ if (option_given_p(opts, tmp)) {
716
+ json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0;
717
+ } else {
718
+ json->freeze = 0;
719
+ }
685
720
  tmp = ID2SYM(i_create_additions);
686
721
  if (option_given_p(opts, tmp)) {
687
722
  json->create_additions = RTEST(rb_hash_aref(opts, tmp));
@@ -730,7 +765,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
730
765
  } else {
731
766
  json->max_nesting = 100;
732
767
  json->allow_nan = 0;
733
- json->create_additions = 1;
768
+ json->create_additions = 0;
734
769
  json->create_id = rb_funcall(mJSON, i_create_id, 0);
735
770
  json->object_class = Qnil;
736
771
  json->array_class = Qnil;
@@ -844,6 +879,10 @@ static VALUE cParser_source(VALUE self)
844
879
 
845
880
  void Init_parser(void)
846
881
  {
882
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
883
+ rb_ext_ractor_safe(true);
884
+ #endif
885
+
847
886
  #undef rb_intern
848
887
  rb_require("json/common");
849
888
  mJSON = rb_define_module("JSON");
@@ -851,14 +890,21 @@ void Init_parser(void)
851
890
  cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
852
891
  eParserError = rb_path2class("JSON::ParserError");
853
892
  eNestingError = rb_path2class("JSON::NestingError");
893
+ rb_gc_register_mark_object(eParserError);
894
+ rb_gc_register_mark_object(eNestingError);
854
895
  rb_define_alloc_func(cParser, cJSON_parser_s_allocate);
855
896
  rb_define_method(cParser, "initialize", cParser_initialize, -1);
856
897
  rb_define_method(cParser, "parse", cParser_parse, 0);
857
898
  rb_define_method(cParser, "source", cParser_source, 0);
858
899
 
859
900
  CNaN = rb_const_get(mJSON, rb_intern("NaN"));
901
+ rb_gc_register_mark_object(CNaN);
902
+
860
903
  CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
904
+ rb_gc_register_mark_object(CInfinity);
905
+
861
906
  CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
907
+ rb_gc_register_mark_object(CMinusInfinity);
862
908
 
863
909
  i_json_creatable_p = rb_intern("json_creatable?");
864
910
  i_json_create = rb_intern("json_create");
@@ -879,7 +925,9 @@ void Init_parser(void)
879
925
  i_aref = rb_intern("[]");
880
926
  i_leftshift = rb_intern("<<");
881
927
  i_new = rb_intern("new");
882
- i_BigDecimal = rb_intern("BigDecimal");
928
+ i_try_convert = rb_intern("try_convert");
929
+ i_freeze = rb_intern("freeze");
930
+ i_uminus = rb_intern("-@");
883
931
  }
884
932
 
885
933
  /*
@@ -1,2 +1,3 @@
1
1
  require 'mkmf'
2
+
2
3
  create_makefile('json')
Binary file
@@ -2,55 +2,575 @@
2
2
  require 'json/common'
3
3
 
4
4
  ##
5
- # = JavaScript Object Notation (JSON)
5
+ # = JavaScript \Object Notation (\JSON)
6
6
  #
7
- # JSON is a lightweight data-interchange format. It is easy for us
8
- # humans to read and write. Plus, equally simple for machines to generate or parse.
9
- # JSON is completely language agnostic, making it the ideal interchange format.
7
+ # \JSON is a lightweight data-interchange format.
10
8
  #
11
- # Built on two universally available structures:
12
- # 1. A collection of name/value pairs. Often referred to as an _object_, hash table, record, struct, keyed list, or associative array.
13
- # 2. An ordered list of values. More commonly called an _array_, vector, sequence or list.
9
+ # A \JSON value is one of the following:
10
+ # - Double-quoted text: <tt>"foo"</tt>.
11
+ # - Number: +1+, +1.0+, +2.0e2+.
12
+ # - Boolean: +true+, +false+.
13
+ # - Null: +null+.
14
+ # - \Array: an ordered list of values, enclosed by square brackets:
15
+ # ["foo", 1, 1.0, 2.0e2, true, false, null]
14
16
  #
15
- # To read more about JSON visit: http://json.org
17
+ # - \Object: a collection of name/value pairs, enclosed by curly braces;
18
+ # each name is double-quoted text;
19
+ # the values may be any \JSON values:
20
+ # {"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}
16
21
  #
17
- # == Parsing JSON
22
+ # A \JSON array or object may contain nested arrays, objects, and scalars
23
+ # to any depth:
24
+ # {"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}
25
+ # [{"foo": 0, "bar": 1}, ["baz", 2]]
18
26
  #
19
- # To parse a JSON string received by another application or generated within
20
- # your existing application:
27
+ # == Using \Module \JSON
21
28
  #
29
+ # To make module \JSON available in your code, begin with:
22
30
  # require 'json'
23
31
  #
24
- # my_hash = JSON.parse('{"hello": "goodbye"}')
25
- # puts my_hash["hello"] => "goodbye"
32
+ # All examples here assume that this has been done.
26
33
  #
27
- # Notice the extra quotes <tt>''</tt> around the hash notation. Ruby expects
28
- # the argument to be a string and can't convert objects like a hash or array.
34
+ # === Parsing \JSON
29
35
  #
30
- # Ruby converts your string into a hash
36
+ # You can parse a \String containing \JSON data using
37
+ # either of two methods:
38
+ # - <tt>JSON.parse(source, opts)</tt>
39
+ # - <tt>JSON.parse!(source, opts)</tt>
31
40
  #
32
- # == Generating JSON
41
+ # where
42
+ # - +source+ is a Ruby object.
43
+ # - +opts+ is a \Hash object containing options
44
+ # that control both input allowed and output formatting.
33
45
  #
34
- # Creating a JSON string for communication or serialization is
35
- # just as simple.
46
+ # The difference between the two methods
47
+ # is that JSON.parse! omits some checks
48
+ # and may not be safe for some +source+ data;
49
+ # use it only for data from trusted sources.
50
+ # Use the safer method JSON.parse for less trusted sources.
36
51
  #
37
- # require 'json'
52
+ # ==== Parsing \JSON Arrays
38
53
  #
39
- # my_hash = {:hello => "goodbye"}
40
- # puts JSON.generate(my_hash) => "{\"hello\":\"goodbye\"}"
54
+ # When +source+ is a \JSON array, JSON.parse by default returns a Ruby \Array:
55
+ # json = '["foo", 1, 1.0, 2.0e2, true, false, null]'
56
+ # ruby = JSON.parse(json)
57
+ # ruby # => ["foo", 1, 1.0, 200.0, true, false, nil]
58
+ # ruby.class # => Array
41
59
  #
42
- # Or an alternative way:
60
+ # The \JSON array may contain nested arrays, objects, and scalars
61
+ # to any depth:
62
+ # json = '[{"foo": 0, "bar": 1}, ["baz", 2]]'
63
+ # JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]]
43
64
  #
44
- # require 'json'
45
- # puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}"
65
+ # ==== Parsing \JSON \Objects
66
+ #
67
+ # When the source is a \JSON object, JSON.parse by default returns a Ruby \Hash:
68
+ # json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}'
69
+ # ruby = JSON.parse(json)
70
+ # ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil}
71
+ # ruby.class # => Hash
72
+ #
73
+ # The \JSON object may contain nested arrays, objects, and scalars
74
+ # to any depth:
75
+ # json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}'
76
+ # JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]}
77
+ #
78
+ # ==== Parsing \JSON Scalars
79
+ #
80
+ # When the source is a \JSON scalar (not an array or object),
81
+ # JSON.parse returns a Ruby scalar.
82
+ #
83
+ # \String:
84
+ # ruby = JSON.parse('"foo"')
85
+ # ruby # => 'foo'
86
+ # ruby.class # => String
87
+ # \Integer:
88
+ # ruby = JSON.parse('1')
89
+ # ruby # => 1
90
+ # ruby.class # => Integer
91
+ # \Float:
92
+ # ruby = JSON.parse('1.0')
93
+ # ruby # => 1.0
94
+ # ruby.class # => Float
95
+ # ruby = JSON.parse('2.0e2')
96
+ # ruby # => 200
97
+ # ruby.class # => Float
98
+ # Boolean:
99
+ # ruby = JSON.parse('true')
100
+ # ruby # => true
101
+ # ruby.class # => TrueClass
102
+ # ruby = JSON.parse('false')
103
+ # ruby # => false
104
+ # ruby.class # => FalseClass
105
+ # Null:
106
+ # ruby = JSON.parse('null')
107
+ # ruby # => nil
108
+ # ruby.class # => NilClass
109
+ #
110
+ # ==== Parsing Options
111
+ #
112
+ # ====== Input Options
113
+ #
114
+ # Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed;
115
+ # defaults to +100+; specify +false+ to disable depth checking.
116
+ #
117
+ # With the default, +false+:
118
+ # source = '[0, [1, [2, [3]]]]'
119
+ # ruby = JSON.parse(source)
120
+ # ruby # => [0, [1, [2, [3]]]]
121
+ # Too deep:
122
+ # # Raises JSON::NestingError (nesting of 2 is too deep):
123
+ # JSON.parse(source, {max_nesting: 1})
124
+ # Bad value:
125
+ # # Raises TypeError (wrong argument type Symbol (expected Fixnum)):
126
+ # JSON.parse(source, {max_nesting: :foo})
127
+ #
128
+ # ---
129
+ #
130
+ # Option +allow_nan+ (boolean) specifies whether to allow
131
+ # NaN, Infinity, and MinusInfinity in +source+;
132
+ # defaults to +false+.
133
+ #
134
+ # With the default, +false+:
135
+ # # Raises JSON::ParserError (225: unexpected token at '[NaN]'):
136
+ # JSON.parse('[NaN]')
137
+ # # Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
138
+ # JSON.parse('[Infinity]')
139
+ # # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
140
+ # JSON.parse('[-Infinity]')
141
+ # Allow:
142
+ # source = '[NaN, Infinity, -Infinity]'
143
+ # ruby = JSON.parse(source, {allow_nan: true})
144
+ # ruby # => [NaN, Infinity, -Infinity]
145
+ #
146
+ # ====== Output Options
147
+ #
148
+ # Option +symbolize_names+ (boolean) specifies whether returned \Hash keys
149
+ # should be Symbols;
150
+ # defaults to +false+ (use Strings).
151
+ #
152
+ # With the default, +false+:
153
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
154
+ # ruby = JSON.parse(source)
155
+ # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
156
+ # Use Symbols:
157
+ # ruby = JSON.parse(source, {symbolize_names: true})
158
+ # ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
159
+ #
160
+ # ---
161
+ #
162
+ # Option +object_class+ (\Class) specifies the Ruby class to be used
163
+ # for each \JSON object;
164
+ # defaults to \Hash.
165
+ #
166
+ # With the default, \Hash:
167
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
168
+ # ruby = JSON.parse(source)
169
+ # ruby.class # => Hash
170
+ # Use class \OpenStruct:
171
+ # ruby = JSON.parse(source, {object_class: OpenStruct})
172
+ # ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
173
+ #
174
+ # ---
175
+ #
176
+ # Option +array_class+ (\Class) specifies the Ruby class to be used
177
+ # for each \JSON array;
178
+ # defaults to \Array.
179
+ #
180
+ # With the default, \Array:
181
+ # source = '["foo", 1.0, true, false, null]'
182
+ # ruby = JSON.parse(source)
183
+ # ruby.class # => Array
184
+ # Use class \Set:
185
+ # ruby = JSON.parse(source, {array_class: Set})
186
+ # ruby # => #<Set: {"foo", 1.0, true, false, nil}>
187
+ #
188
+ # ---
189
+ #
190
+ # Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
191
+ # See {\JSON Additions}[#module-JSON-label-JSON+Additions].
192
+ #
193
+ # === Generating \JSON
194
+ #
195
+ # To generate a Ruby \String containing \JSON data,
196
+ # use method <tt>JSON.generate(source, opts)</tt>, where
197
+ # - +source+ is a Ruby object.
198
+ # - +opts+ is a \Hash object containing options
199
+ # that control both input allowed and output formatting.
200
+ #
201
+ # ==== Generating \JSON from Arrays
202
+ #
203
+ # When the source is a Ruby \Array, JSON.generate returns
204
+ # a \String containing a \JSON array:
205
+ # ruby = [0, 's', :foo]
206
+ # json = JSON.generate(ruby)
207
+ # json # => '[0,"s","foo"]'
208
+ #
209
+ # The Ruby \Array array may contain nested arrays, hashes, and scalars
210
+ # to any depth:
211
+ # ruby = [0, [1, 2], {foo: 3, bar: 4}]
212
+ # json = JSON.generate(ruby)
213
+ # json # => '[0,[1,2],{"foo":3,"bar":4}]'
214
+ #
215
+ # ==== Generating \JSON from Hashes
216
+ #
217
+ # When the source is a Ruby \Hash, JSON.generate returns
218
+ # a \String containing a \JSON object:
219
+ # ruby = {foo: 0, bar: 's', baz: :bat}
220
+ # json = JSON.generate(ruby)
221
+ # json # => '{"foo":0,"bar":"s","baz":"bat"}'
222
+ #
223
+ # The Ruby \Hash array may contain nested arrays, hashes, and scalars
224
+ # to any depth:
225
+ # ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
226
+ # json = JSON.generate(ruby)
227
+ # json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}'
228
+ #
229
+ # ==== Generating \JSON from Other Objects
230
+ #
231
+ # When the source is neither an \Array nor a \Hash,
232
+ # the generated \JSON data depends on the class of the source.
233
+ #
234
+ # When the source is a Ruby \Integer or \Float, JSON.generate returns
235
+ # a \String containing a \JSON number:
236
+ # JSON.generate(42) # => '42'
237
+ # JSON.generate(0.42) # => '0.42'
238
+ #
239
+ # When the source is a Ruby \String, JSON.generate returns
240
+ # a \String containing a \JSON string (with double-quotes):
241
+ # JSON.generate('A string') # => '"A string"'
242
+ #
243
+ # When the source is +true+, +false+ or +nil+, JSON.generate returns
244
+ # a \String containing the corresponding \JSON token:
245
+ # JSON.generate(true) # => 'true'
246
+ # JSON.generate(false) # => 'false'
247
+ # JSON.generate(nil) # => 'null'
248
+ #
249
+ # When the source is none of the above, JSON.generate returns
250
+ # a \String containing a \JSON string representation of the source:
251
+ # JSON.generate(:foo) # => '"foo"'
252
+ # JSON.generate(Complex(0, 0)) # => '"0+0i"'
253
+ # JSON.generate(Dir.new('.')) # => '"#<Dir>"'
254
+ #
255
+ # ==== Generating Options
256
+ #
257
+ # ====== Input Options
46
258
  #
47
- # <tt>JSON.generate</tt> only allows objects or arrays to be converted
48
- # to JSON syntax. <tt>to_json</tt>, however, accepts many Ruby classes
49
- # even though it acts only as a method for serialization:
259
+ # Option +allow_nan+ (boolean) specifies whether
260
+ # +NaN+, +Infinity+, and <tt>-Infinity</tt> may be generated;
261
+ # defaults to +false+.
50
262
  #
263
+ # With the default, +false+:
264
+ # # Raises JSON::GeneratorError (920: NaN not allowed in JSON):
265
+ # JSON.generate(JSON::NaN)
266
+ # # Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
267
+ # JSON.generate(JSON::Infinity)
268
+ # # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
269
+ # JSON.generate(JSON::MinusInfinity)
270
+ #
271
+ # Allow:
272
+ # ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity]
273
+ # JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
274
+ #
275
+ # ---
276
+ #
277
+ # Option +max_nesting+ (\Integer) specifies the maximum nesting depth
278
+ # in +obj+; defaults to +100+.
279
+ #
280
+ # With the default, +100+:
281
+ # obj = [[[[[[0]]]]]]
282
+ # JSON.generate(obj) # => '[[[[[[0]]]]]]'
283
+ #
284
+ # Too deep:
285
+ # # Raises JSON::NestingError (nesting of 2 is too deep):
286
+ # JSON.generate(obj, max_nesting: 2)
287
+ #
288
+ # ====== Output Options
289
+ #
290
+ # The default formatting options generate the most compact
291
+ # \JSON data, all on one line and with no whitespace.
292
+ #
293
+ # You can use these formatting options to generate
294
+ # \JSON data in a more open format, using whitespace.
295
+ # See also JSON.pretty_generate.
296
+ #
297
+ # - Option +array_nl+ (\String) specifies a string (usually a newline)
298
+ # to be inserted after each \JSON array; defaults to the empty \String, <tt>''</tt>.
299
+ # - Option +object_nl+ (\String) specifies a string (usually a newline)
300
+ # to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
301
+ # - Option +indent+ (\String) specifies the string (usually spaces) to be
302
+ # used for indentation; defaults to the empty \String, <tt>''</tt>;
303
+ # defaults to the empty \String, <tt>''</tt>;
304
+ # has no effect unless options +array_nl+ or +object_nl+ specify newlines.
305
+ # - Option +space+ (\String) specifies a string (usually a space) to be
306
+ # inserted after the colon in each \JSON object's pair;
307
+ # defaults to the empty \String, <tt>''</tt>.
308
+ # - Option +space_before+ (\String) specifies a string (usually a space) to be
309
+ # inserted before the colon in each \JSON object's pair;
310
+ # defaults to the empty \String, <tt>''</tt>.
311
+ #
312
+ # In this example, +obj+ is used first to generate the shortest
313
+ # \JSON data (no whitespace), then again with all formatting options
314
+ # specified:
315
+ #
316
+ # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
317
+ # json = JSON.generate(obj)
318
+ # puts 'Compact:', json
319
+ # opts = {
320
+ # array_nl: "\n",
321
+ # object_nl: "\n",
322
+ # indent: ' ',
323
+ # space_before: ' ',
324
+ # space: ' '
325
+ # }
326
+ # puts 'Open:', JSON.generate(obj, opts)
327
+ #
328
+ # Output:
329
+ # Compact:
330
+ # {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
331
+ # Open:
332
+ # {
333
+ # "foo" : [
334
+ # "bar",
335
+ # "baz"
336
+ # ],
337
+ # "bat" : {
338
+ # "bam" : 0,
339
+ # "bad" : 1
340
+ # }
341
+ # }
342
+ #
343
+ # == \JSON Additions
344
+ #
345
+ # When you "round trip" a non-\String object from Ruby to \JSON and back,
346
+ # you have a new \String, instead of the object you began with:
347
+ # ruby0 = Range.new(0, 2)
348
+ # json = JSON.generate(ruby0)
349
+ # json # => '0..2"'
350
+ # ruby1 = JSON.parse(json)
351
+ # ruby1 # => '0..2'
352
+ # ruby1.class # => String
353
+ #
354
+ # You can use \JSON _additions_ to preserve the original object.
355
+ # The addition is an extension of a ruby class, so that:
356
+ # - \JSON.generate stores more information in the \JSON string.
357
+ # - \JSON.parse, called with option +create_additions+,
358
+ # uses that information to create a proper Ruby object.
359
+ #
360
+ # This example shows a \Range being generated into \JSON
361
+ # and parsed back into Ruby, both without and with
362
+ # the addition for \Range:
363
+ # ruby = Range.new(0, 2)
364
+ # # This passage does not use the addition for Range.
365
+ # json0 = JSON.generate(ruby)
366
+ # ruby0 = JSON.parse(json0)
367
+ # # This passage uses the addition for Range.
368
+ # require 'json/add/range'
369
+ # json1 = JSON.generate(ruby)
370
+ # ruby1 = JSON.parse(json1, create_additions: true)
371
+ # # Make a nice display.
372
+ # display = <<EOT
373
+ # Generated JSON:
374
+ # Without addition: #{json0} (#{json0.class})
375
+ # With addition: #{json1} (#{json1.class})
376
+ # Parsed JSON:
377
+ # Without addition: #{ruby0.inspect} (#{ruby0.class})
378
+ # With addition: #{ruby1.inspect} (#{ruby1.class})
379
+ # EOT
380
+ # puts display
381
+ #
382
+ # This output shows the different results:
383
+ # Generated JSON:
384
+ # Without addition: "0..2" (String)
385
+ # With addition: {"json_class":"Range","a":[0,2,false]} (String)
386
+ # Parsed JSON:
387
+ # Without addition: "0..2" (String)
388
+ # With addition: 0..2 (Range)
389
+ #
390
+ # The \JSON module includes additions for certain classes.
391
+ # You can also craft custom additions.
392
+ # See {Custom \JSON Additions}[#module-JSON-label-Custom+JSON+Additions].
393
+ #
394
+ # === Built-in Additions
395
+ #
396
+ # The \JSON module includes additions for certain classes.
397
+ # To use an addition, +require+ its source:
398
+ # - BigDecimal: <tt>require 'json/add/bigdecimal'</tt>
399
+ # - Complex: <tt>require 'json/add/complex'</tt>
400
+ # - Date: <tt>require 'json/add/date'</tt>
401
+ # - DateTime: <tt>require 'json/add/date_time'</tt>
402
+ # - Exception: <tt>require 'json/add/exception'</tt>
403
+ # - OpenStruct: <tt>require 'json/add/ostruct'</tt>
404
+ # - Range: <tt>require 'json/add/range'</tt>
405
+ # - Rational: <tt>require 'json/add/rational'</tt>
406
+ # - Regexp: <tt>require 'json/add/regexp'</tt>
407
+ # - Set: <tt>require 'json/add/set'</tt>
408
+ # - Struct: <tt>require 'json/add/struct'</tt>
409
+ # - Symbol: <tt>require 'json/add/symbol'</tt>
410
+ # - Time: <tt>require 'json/add/time'</tt>
411
+ #
412
+ # To reduce punctuation clutter, the examples below
413
+ # show the generated \JSON via +puts+, rather than the usual +inspect+,
414
+ #
415
+ # \BigDecimal:
416
+ # require 'json/add/bigdecimal'
417
+ # ruby0 = BigDecimal(0) # 0.0
418
+ # json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"}
419
+ # ruby1 = JSON.parse(json, create_additions: true) # 0.0
420
+ # ruby1.class # => BigDecimal
421
+ #
422
+ # \Complex:
423
+ # require 'json/add/complex'
424
+ # ruby0 = Complex(1+0i) # 1+0i
425
+ # json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0}
426
+ # ruby1 = JSON.parse(json, create_additions: true) # 1+0i
427
+ # ruby1.class # Complex
428
+ #
429
+ # \Date:
430
+ # require 'json/add/date'
431
+ # ruby0 = Date.today # 2020-05-02
432
+ # json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0}
433
+ # ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02
434
+ # ruby1.class # Date
435
+ #
436
+ # \DateTime:
437
+ # require 'json/add/date_time'
438
+ # ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00
439
+ # json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0}
440
+ # ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00
441
+ # ruby1.class # DateTime
442
+ #
443
+ # \Exception (and its subclasses including \RuntimeError):
444
+ # require 'json/add/exception'
445
+ # ruby0 = Exception.new('A message') # A message
446
+ # json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null}
447
+ # ruby1 = JSON.parse(json, create_additions: true) # A message
448
+ # ruby1.class # Exception
449
+ # ruby0 = RuntimeError.new('Another message') # Another message
450
+ # json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null}
451
+ # ruby1 = JSON.parse(json, create_additions: true) # Another message
452
+ # ruby1.class # RuntimeError
453
+ #
454
+ # \OpenStruct:
455
+ # require 'json/add/ostruct'
456
+ # ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # #<OpenStruct name="Matz", language="Ruby">
457
+ # json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}}
458
+ # ruby1 = JSON.parse(json, create_additions: true) # #<OpenStruct name="Matz", language="Ruby">
459
+ # ruby1.class # OpenStruct
460
+ #
461
+ # \Range:
462
+ # require 'json/add/range'
463
+ # ruby0 = Range.new(0, 2) # 0..2
464
+ # json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]}
465
+ # ruby1 = JSON.parse(json, create_additions: true) # 0..2
466
+ # ruby1.class # Range
467
+ #
468
+ # \Rational:
469
+ # require 'json/add/rational'
470
+ # ruby0 = Rational(1, 3) # 1/3
471
+ # json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3}
472
+ # ruby1 = JSON.parse(json, create_additions: true) # 1/3
473
+ # ruby1.class # Rational
474
+ #
475
+ # \Regexp:
476
+ # require 'json/add/regexp'
477
+ # ruby0 = Regexp.new('foo') # (?-mix:foo)
478
+ # json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"}
479
+ # ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo)
480
+ # ruby1.class # Regexp
481
+ #
482
+ # \Set:
483
+ # require 'json/add/set'
484
+ # ruby0 = Set.new([0, 1, 2]) # #<Set: {0, 1, 2}>
485
+ # json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]}
486
+ # ruby1 = JSON.parse(json, create_additions: true) # #<Set: {0, 1, 2}>
487
+ # ruby1.class # Set
488
+ #
489
+ # \Struct:
490
+ # require 'json/add/struct'
491
+ # Customer = Struct.new(:name, :address) # Customer
492
+ # ruby0 = Customer.new("Dave", "123 Main") # #<struct Customer name="Dave", address="123 Main">
493
+ # json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]}
494
+ # ruby1 = JSON.parse(json, create_additions: true) # #<struct Customer name="Dave", address="123 Main">
495
+ # ruby1.class # Customer
496
+ #
497
+ # \Symbol:
498
+ # require 'json/add/symbol'
499
+ # ruby0 = :foo # foo
500
+ # json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"}
501
+ # ruby1 = JSON.parse(json, create_additions: true) # foo
502
+ # ruby1.class # Symbol
503
+ #
504
+ # \Time:
505
+ # require 'json/add/time'
506
+ # ruby0 = Time.now # 2020-05-02 11:28:26 -0500
507
+ # json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000}
508
+ # ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500
509
+ # ruby1.class # Time
510
+ #
511
+ #
512
+ # === Custom \JSON Additions
513
+ #
514
+ # In addition to the \JSON additions provided,
515
+ # you can craft \JSON additions of your own,
516
+ # either for Ruby built-in classes or for user-defined classes.
517
+ #
518
+ # Here's a user-defined class +Foo+:
519
+ # class Foo
520
+ # attr_accessor :bar, :baz
521
+ # def initialize(bar, baz)
522
+ # self.bar = bar
523
+ # self.baz = baz
524
+ # end
525
+ # end
526
+ #
527
+ # Here's the \JSON addition for it:
528
+ # # Extend class Foo with JSON addition.
529
+ # class Foo
530
+ # # Serialize Foo object with its class name and arguments
531
+ # def to_json(*args)
532
+ # {
533
+ # JSON.create_id => self.class.name,
534
+ # 'a' => [ bar, baz ]
535
+ # }.to_json(*args)
536
+ # end
537
+ # # Deserialize JSON string by constructing new Foo object with arguments.
538
+ # def self.json_create(object)
539
+ # new(*object['a'])
540
+ # end
541
+ # end
542
+ #
543
+ # Demonstration:
51
544
  # require 'json'
545
+ # # This Foo object has no custom addition.
546
+ # foo0 = Foo.new(0, 1)
547
+ # json0 = JSON.generate(foo0)
548
+ # obj0 = JSON.parse(json0)
549
+ # # Lood the custom addition.
550
+ # require_relative 'foo_addition'
551
+ # # This foo has the custom addition.
552
+ # foo1 = Foo.new(0, 1)
553
+ # json1 = JSON.generate(foo1)
554
+ # obj1 = JSON.parse(json1, create_additions: true)
555
+ # # Make a nice display.
556
+ # display = <<EOT
557
+ # Generated JSON:
558
+ # Without custom addition: #{json0} (#{json0.class})
559
+ # With custom addition: #{json1} (#{json1.class})
560
+ # Parsed JSON:
561
+ # Without custom addition: #{obj0.inspect} (#{obj0.class})
562
+ # With custom addition: #{obj1.inspect} (#{obj1.class})
563
+ # EOT
564
+ # puts display
565
+ #
566
+ # Output:
52
567
  #
53
- # 1.to_json => "1"
568
+ # Generated JSON:
569
+ # Without custom addition: "#<Foo:0x0000000006534e80>" (String)
570
+ # With custom addition: {"json_class":"Foo","a":[0,1]} (String)
571
+ # Parsed JSON:
572
+ # Without custom addition: "#<Foo:0x0000000006534e80>" (String)
573
+ # With custom addition: #<Foo:0x0000000006473bb8 @bar=0, @baz=1> (Foo)
54
574
  #
55
575
  module JSON
56
576
  require 'json/version'