oj 3.13.3 → 3.13.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/oj/oj.c CHANGED
@@ -107,6 +107,7 @@ static VALUE bigdecimal_load_sym;
107
107
  static VALUE bigdecimal_sym;
108
108
  static VALUE cache_keys_sym;
109
109
  static VALUE cache_str_sym;
110
+ static VALUE cache_string_sym;
110
111
  static VALUE circular_sym;
111
112
  static VALUE class_cache_sym;
112
113
  static VALUE compat_bigdecimal_sym;
@@ -287,7 +288,7 @@ struct _options oj_default_options = {
287
288
  * - *:ignore* [_nil_|_Array_] either nil or an Array of classes to ignore when dumping
288
289
  * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are ignored when dumping in
289
290
  *object or custom mode.
290
- * - *:cache_keys* [_Boolean_] if true then hash keys are cached
291
+ * - *:cache_keys* [_Boolean_] if true then hash keys are cached if less than 35 bytes.
291
292
  * - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less than this are cached)
292
293
  * - *:integer_range* [_Range_] Dump integers outside range as strings.
293
294
  * - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false (trace is off)
@@ -692,7 +693,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts)
692
693
  sprintf(copts->float_fmt, "%%0.%dg", n);
693
694
  copts->float_prec = n;
694
695
  }
695
- } else if (cache_str_sym == k) {
696
+ } else if (cache_str_sym == k || cache_string_sym == k) {
696
697
  int n;
697
698
 
698
699
  #ifdef RUBY_INTEGER_UNIFICATION
@@ -1920,6 +1921,8 @@ void Init_oj() {
1920
1921
  rb_gc_register_address(&cache_keys_sym);
1921
1922
  cache_str_sym = ID2SYM(rb_intern("cache_str"));
1922
1923
  rb_gc_register_address(&cache_str_sym);
1924
+ cache_string_sym = ID2SYM(rb_intern("cache_string"));
1925
+ rb_gc_register_address(&cache_string_sym);
1923
1926
  circular_sym = ID2SYM(rb_intern("circular"));
1924
1927
  rb_gc_register_address(&circular_sym);
1925
1928
  class_cache_sym = ID2SYM(rb_intern("class_cache"));
data/ext/oj/parser.c CHANGED
@@ -610,6 +610,9 @@ static void parse(ojParser p, const byte *json) {
610
610
  printf("*** parse - mode: %c %s\n", p->map[256], (const char *)json);
611
611
  #endif
612
612
  for (; '\0' != *b; b++) {
613
+ #if DEBUG
614
+ printf("*** parse - mode: %c %02x %s => %c\n", p->map[256], *b, b, p->map[*b]);
615
+ #endif
613
616
  switch (p->map[*b]) {
614
617
  case SKIP_NEWLINE:
615
618
  p->line++;
@@ -887,13 +890,17 @@ static void parse(ojParser p, const byte *json) {
887
890
  buf_append_string(&p->buf, (const char *)start, b - start);
888
891
  }
889
892
  if ('"' == *b) {
893
+ p->funcs[p->stack[p->depth]].add_str(p);
890
894
  p->map = p->next_map;
891
895
  break;
892
896
  }
893
897
  b--;
894
898
  break;
895
899
  case STR_SLASH: p->map = esc_map; break;
896
- case STR_QUOTE: p->map = p->next_map; break;
900
+ case STR_QUOTE:
901
+ p->funcs[p->stack[p->depth]].add_str(p);
902
+ p->map = p->next_map;
903
+ break;
897
904
  case ESC_U:
898
905
  p->map = u_map;
899
906
  p->ri = 0;
@@ -1135,13 +1142,42 @@ extern void oj_set_parser_saj(ojParser p);
1135
1142
  extern void oj_set_parser_usual(ojParser p);
1136
1143
  extern void oj_set_parser_debug(ojParser p);
1137
1144
 
1145
+ static int opt_cb(VALUE rkey, VALUE value, VALUE ptr) {
1146
+ ojParser p = (ojParser)ptr;
1147
+ const char *key = NULL;
1148
+ char set_key[64];
1149
+ long klen;
1150
+
1151
+ switch (rb_type(rkey)) {
1152
+ case RUBY_T_SYMBOL:
1153
+ rkey = rb_sym2str(rkey);
1154
+ // fall through
1155
+ case RUBY_T_STRING:
1156
+ key = rb_string_value_ptr(&rkey);
1157
+ klen = RSTRING_LEN(rkey);
1158
+ break;
1159
+ default: rb_raise(rb_eArgError, "option keys must be a symbol or string");
1160
+ }
1161
+ if ((long)sizeof(set_key) - 1 <= klen) {
1162
+ return ST_CONTINUE;
1163
+ }
1164
+ memcpy(set_key, key, klen);
1165
+ set_key[klen] = '=';
1166
+ set_key[klen + 1] = '\0';
1167
+ p->option(p, set_key, value);
1168
+
1169
+ return ST_CONTINUE;
1170
+ }
1171
+
1138
1172
  /* Document-method: new
1139
1173
  * call-seq: new(mode=nil)
1140
1174
  *
1141
1175
  * Creates a new Parser with the specified mode. If no mode is provided
1142
- * validation is assumed.
1176
+ * validation is assumed. Optional arguments can be provided that match the
1177
+ * mode. For example with the :usual mode the call might look like
1178
+ * Oj::Parser.new(:usual, cache_keys: true).
1143
1179
  */
1144
- static VALUE parser_new(VALUE self, VALUE mode) {
1180
+ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
1145
1181
  ojParser p = ALLOC(struct _ojParser);
1146
1182
 
1147
1183
  #if HAVE_RB_EXT_RACTOR_SAFE
@@ -1151,33 +1187,45 @@ static VALUE parser_new(VALUE self, VALUE mode) {
1151
1187
  memset(p, 0, sizeof(struct _ojParser));
1152
1188
  buf_init(&p->key);
1153
1189
  buf_init(&p->buf);
1154
-
1155
1190
  p->map = value_map;
1156
- if (Qnil == mode) {
1157
- oj_set_parser_validator(p);
1191
+
1192
+ if (argc < 1) {
1193
+ oj_set_parser_validator(p);
1158
1194
  } else {
1159
- const char *ms = NULL;
1160
-
1161
- switch (rb_type(mode)) {
1162
- case RUBY_T_SYMBOL:
1163
- mode = rb_sym2str(mode);
1164
- // fall through
1165
- case RUBY_T_STRING: ms = RSTRING_PTR(mode); break;
1166
- default: rb_raise(rb_eArgError, "mode must be :validate, :usual, :saj, or :object");
1167
- }
1168
- if (0 == strcmp("usual", ms) || 0 == strcmp("standard", ms) || 0 == strcmp("strict", ms) ||
1169
- 0 == strcmp("compat", ms)) {
1170
- oj_set_parser_usual(p);
1171
- } else if (0 == strcmp("object", ms)) {
1172
- // TBD
1173
- } else if (0 == strcmp("saj", ms)) {
1174
- oj_set_parser_saj(p);
1175
- } else if (0 == strcmp("validate", ms)) {
1195
+ VALUE mode = argv[0];
1196
+
1197
+ if (Qnil == mode) {
1176
1198
  oj_set_parser_validator(p);
1177
- } else if (0 == strcmp("debug", ms)) {
1178
- oj_set_parser_debug(p);
1179
1199
  } else {
1180
- rb_raise(rb_eArgError, "mode must be :validate, :usual, :saj, or :object");
1200
+ const char *ms = NULL;
1201
+
1202
+ switch (rb_type(mode)) {
1203
+ case RUBY_T_SYMBOL:
1204
+ mode = rb_sym2str(mode);
1205
+ // fall through
1206
+ case RUBY_T_STRING: ms = RSTRING_PTR(mode); break;
1207
+ default: rb_raise(rb_eArgError, "mode must be :validate, :usual, :saj, or :object");
1208
+ }
1209
+ if (0 == strcmp("usual", ms) || 0 == strcmp("standard", ms) || 0 == strcmp("strict", ms) ||
1210
+ 0 == strcmp("compat", ms)) {
1211
+ oj_set_parser_usual(p);
1212
+ } else if (0 == strcmp("object", ms)) {
1213
+ // TBD
1214
+ } else if (0 == strcmp("saj", ms)) {
1215
+ oj_set_parser_saj(p);
1216
+ } else if (0 == strcmp("validate", ms)) {
1217
+ oj_set_parser_validator(p);
1218
+ } else if (0 == strcmp("debug", ms)) {
1219
+ oj_set_parser_debug(p);
1220
+ } else {
1221
+ rb_raise(rb_eArgError, "mode must be :validate, :usual, :saj, or :object");
1222
+ }
1223
+ }
1224
+ if (1 < argc) {
1225
+ VALUE ropts = argv[1];
1226
+
1227
+ Check_Type(ropts, T_HASH);
1228
+ rb_hash_foreach(ropts, opt_cb, (VALUE)p);
1181
1229
  }
1182
1230
  }
1183
1231
  return Data_Wrap_Struct(parser_class, parser_mark, parser_free, p);
@@ -1204,7 +1252,7 @@ static VALUE parser_new(VALUE self, VALUE mode) {
1204
1252
  * - *:usual*
1205
1253
  * - _cache_keys=_ sets the value of the _cache_keys_ flag.
1206
1254
  * - _cache_keys_ returns the value of the _cache_keys_ flag.
1207
- * - _cache_strings=_ sets the value of the _cache_strings_ to an positive integer less than 35. Strings shorter than
1255
+ * - _cache_strings=_ sets the value of the _cache_strings_ to a positive integer less than 35. Strings shorter than
1208
1256
  * that length are cached.
1209
1257
  * - _cache_strings_ returns the value of the _cache_strings_ integer value.
1210
1258
  * - _cache_expunge=_ sets the value of the _cache_expunge_ where 0 never expunges, 1 expunges slowly, 2 expunges
@@ -1469,7 +1517,7 @@ static VALUE parser_validate(VALUE self) {
1469
1517
  */
1470
1518
  void oj_parser_init() {
1471
1519
  parser_class = rb_define_class_under(Oj, "Parser", rb_cObject);
1472
- rb_define_module_function(parser_class, "new", parser_new, 1);
1520
+ rb_define_module_function(parser_class, "new", parser_new, -1);
1473
1521
  rb_define_method(parser_class, "parse", parser_parse, 1);
1474
1522
  rb_define_method(parser_class, "load", parser_load, 1);
1475
1523
  rb_define_method(parser_class, "file", parser_file, 1);
data/ext/oj/strict.c CHANGED
@@ -16,7 +16,7 @@
16
16
  VALUE oj_cstr_to_value(const char *str, size_t len, size_t cache_str) {
17
17
  volatile VALUE rstr = Qnil;
18
18
 
19
- if (len <= cache_str) {
19
+ if (len < cache_str) {
20
20
  rstr = oj_str_intern(str, len);
21
21
  } else {
22
22
  rstr = rb_str_new(str, len);
@@ -37,7 +37,7 @@ VALUE oj_calc_hash_key(ParseInfo pi, Val parent) {
37
37
  } else {
38
38
  rkey = rb_str_new(parent->key, parent->klen);
39
39
  rkey = oj_encode(rkey);
40
- OBJ_FREEZE(rkey);
40
+ OBJ_FREEZE(rkey); // frozen when used as a Hash key anyway
41
41
  }
42
42
  return rkey;
43
43
  }
data/ext/oj/usual.c CHANGED
@@ -39,7 +39,7 @@ typedef struct _col {
39
39
  typedef union _key {
40
40
  struct {
41
41
  int16_t len;
42
- char buf[22];
42
+ char buf[30];
43
43
  };
44
44
  struct {
45
45
  int16_t xlen; // should be the same as len
@@ -209,21 +209,21 @@ static void push(ojParser p, VALUE v) {
209
209
  static VALUE cache_key(ojParser p, Key kp) {
210
210
  Delegate d = (Delegate)p->ctx;
211
211
 
212
- if ((size_t)kp->len < sizeof(kp->buf) - 1) {
212
+ if ((size_t)kp->len < sizeof(kp->buf)) {
213
213
  return cache_intern(d->key_cache, kp->buf, kp->len);
214
214
  }
215
215
  return cache_intern(d->key_cache, kp->key, kp->len);
216
216
  }
217
217
 
218
218
  static VALUE str_key(ojParser p, Key kp) {
219
- if ((size_t)kp->len < sizeof(kp->buf) - 1) {
219
+ if ((size_t)kp->len < sizeof(kp->buf)) {
220
220
  return rb_str_freeze(rb_utf8_str_new(kp->buf, kp->len));
221
221
  }
222
222
  return rb_str_freeze(rb_utf8_str_new(kp->key, kp->len));
223
223
  }
224
224
 
225
225
  static VALUE sym_key(ojParser p, Key kp) {
226
- if ((size_t)kp->len < sizeof(kp->buf) - 1) {
226
+ if ((size_t)kp->len < sizeof(kp->buf)) {
227
227
  return rb_str_freeze(rb_str_intern(rb_utf8_str_new(kp->buf, kp->len)));
228
228
  }
229
229
  return rb_str_freeze(rb_str_intern(rb_utf8_str_new(kp->key, kp->len)));
@@ -232,7 +232,7 @@ static VALUE sym_key(ojParser p, Key kp) {
232
232
  static ID get_attr_id(ojParser p, Key kp) {
233
233
  Delegate d = (Delegate)p->ctx;
234
234
 
235
- if ((size_t)kp->len < sizeof(kp->buf) - 1) {
235
+ if ((size_t)kp->len < sizeof(kp->buf)) {
236
236
  return (ID)cache_intern(d->attr_cache, kp->buf, kp->len);
237
237
  }
238
238
  return (ID)cache_intern(d->attr_cache, kp->key, kp->len);
@@ -253,7 +253,7 @@ static void push_key(ojParser p) {
253
253
  d->kend = d->khead + cap;
254
254
  }
255
255
  d->ktail->len = klen;
256
- if (klen <= sizeof(d->ktail->buf) + 1) {
256
+ if (klen < sizeof(d->ktail->buf)) {
257
257
  memcpy(d->ktail->buf, key, klen);
258
258
  d->ktail->buf[klen] = '\0';
259
259
  } else {
@@ -336,7 +336,7 @@ static void close_object(ojParser p) {
336
336
  #if HAVE_RB_HASH_BULK_INSERT
337
337
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
338
338
  *vp = d->get_key(p, kp);
339
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
339
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
340
340
  xfree(kp->key);
341
341
  }
342
342
  }
@@ -344,7 +344,7 @@ static void close_object(ojParser p) {
344
344
  #else
345
345
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
346
346
  rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
347
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
347
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
348
348
  xfree(kp->key);
349
349
  }
350
350
  }
@@ -368,7 +368,7 @@ static void close_object_class(ojParser p) {
368
368
 
369
369
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
370
370
  rb_funcall(obj, hset_id, 2, d->get_key(p, kp), *(vp + 1));
371
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
371
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
372
372
  xfree(kp->key);
373
373
  }
374
374
  }
@@ -396,7 +396,7 @@ static void close_object_create(ojParser p) {
396
396
  #if HAVE_RB_HASH_BULK_INSERT
397
397
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
398
398
  *vp = d->get_key(p, kp);
399
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
399
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
400
400
  xfree(kp->key);
401
401
  }
402
402
  }
@@ -404,7 +404,7 @@ static void close_object_create(ojParser p) {
404
404
  #else
405
405
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
406
406
  rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
407
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
407
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
408
408
  xfree(kp->key);
409
409
  }
410
410
  }
@@ -413,7 +413,7 @@ static void close_object_create(ojParser p) {
413
413
  obj = rb_class_new_instance(0, NULL, d->hash_class);
414
414
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
415
415
  rb_funcall(obj, hset_id, 2, d->get_key(p, kp), *(vp + 1));
416
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
416
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
417
417
  xfree(kp->key);
418
418
  }
419
419
  }
@@ -428,7 +428,7 @@ static void close_object_create(ojParser p) {
428
428
  #if HAVE_RB_HASH_BULK_INSERT
429
429
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
430
430
  *vp = d->get_key(p, kp);
431
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
431
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
432
432
  xfree(kp->key);
433
433
  }
434
434
  }
@@ -436,7 +436,7 @@ static void close_object_create(ojParser p) {
436
436
  #else
437
437
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
438
438
  rb_hash_aset(arg, d->get_key(p, kp), *(vp + 1));
439
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
439
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
440
440
  xfree(kp->key);
441
441
  }
442
442
  }
@@ -446,7 +446,7 @@ static void close_object_create(ojParser p) {
446
446
  obj = rb_class_new_instance(0, NULL, clas);
447
447
  for (vp = head; kp < d->ktail; kp++, vp += 2) {
448
448
  rb_ivar_set(obj, get_attr_id(p, kp), *(vp + 1));
449
- if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
449
+ if (sizeof(kp->buf) <= (size_t)kp->len) {
450
450
  xfree(kp->key);
451
451
  }
452
452
  }
data/lib/oj/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '3.13.3'
4
+ VERSION = '3.13.7'
5
5
  end
data/pages/Options.md CHANGED
@@ -75,12 +75,22 @@ parse option to match the JSON gem. In that case either `Float`,
75
75
  If true Hash keys are cached or interned. There are trade-offs with
76
76
  caching keys. Large caches will use more memory and in extreme cases
77
77
  (like over a million) the cache may be slower than not using
78
- it. Repeated parsing of similar JSON docs is where cache_keys shines.
78
+ it. Repeated parsing of similar JSON docs is where cache_keys shines
79
+ especially with symbol keys.
80
+
81
+ There is a maximum length for cached keys. Any key longer than 34
82
+ bytes is not cached. Everything still works but the key is not cached.
79
83
 
80
84
  ### :cache_strings [Int]
81
85
 
82
86
  Shorter strings can be cached for better performance. A limit,
83
- cache_strings, defines the upper limit on what strings are cached.
87
+ cache_strings, defines the upper limit on what strings are cached. As
88
+ with cached keys only strings less than 35 bytes are cached even if
89
+ the limit is set higher. Setting the limit to zero effectively
90
+ disables the caching of string values.
91
+
92
+ Note that caching for strings is for string values and not Hash keys
93
+ or Object attributes.
84
94
 
85
95
  ### :circular [Boolean]
86
96
 
data/test/bar.rb CHANGED
@@ -1,35 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $: << File.dirname(__FILE__)
4
- $oj_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
5
- %w(lib ext).each do |dir|
6
- $: << File.join($oj_dir, dir)
7
- end
8
-
9
- require 'active_support'
10
- require "active_support/json"
11
-
12
- $s = "\u2014 & \n \u{1F618}"
13
-
14
- =begin
15
- def check(label)
16
- puts "\n--- #{label} --------------------"
17
-
18
- ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
19
- puts "with standard_json == true: t.to_json - #{$t.to_json}"
20
- ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
21
- puts "with standard_json == false: t.to_json - #{$t.to_json}"
22
- end
23
-
24
- check('Before Oj')
25
- =end
3
+ $: << '.'
4
+ $: << File.join(File.dirname(__FILE__), "../lib")
5
+ $: << File.join(File.dirname(__FILE__), "../ext")
26
6
 
27
7
  require 'oj'
28
8
 
29
- ActiveSupport::JSON::Encoding.escape_html_entities_in_json = false
30
- puts "ActiveSupport.encode(s) - #{ActiveSupport::JSON.encode($s)}"
9
+ json = %|[{"x12345678901234567890": true}]|
31
10
 
32
- Oj.optimize_rails
33
- Oj.default_options = { mode: :rails }
11
+ p = Oj::Parser.new(:usual)
12
+ p.cache_keys = false
13
+ p.symbol_keys = true
14
+ x = p.parse(json)
34
15
 
35
- puts "Oj.dump(s) - #{Oj.dump($s)}"
16
+ pp x
data/test/foo.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $: << File.dirname(__FILE__)
4
- $oj_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
5
- %w(lib ext).each do |dir|
6
- $: << File.join($oj_dir, dir)
7
- end
8
-
9
3
  require 'oj'
10
4
 
11
- p = Oj::Parser.new(:debug)
5
+ Oj::default_options = {cache_str: 0, cache_keys: true, mode: :strict}
6
+
7
+ puts "Ruby version: #{RUBY_VERSION}"
8
+ puts "Oj version: #{Oj::VERSION}"
9
+
10
+ puts "cache_keys: #{Oj::default_options[:cache_keys]}"
11
+ puts "cache_str: #{Oj::default_options[:cache_str]}"
12
12
 
13
- p.parse("[true, false]")
13
+ Oj.load('{"":""}').each_pair {|k,v| puts "k.frozen?: #{k.frozen?}\nv.frozen?: #{v.frozen?}"}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding: UTF-8
2
+ # encoding: utf-8
3
3
 
4
4
  $: << File.dirname(__FILE__)
5
5
 
@@ -73,9 +73,14 @@ class UsualTest < Minitest::Test
73
73
  assert_equal({a: true, b: false}, doc)
74
74
  end
75
75
 
76
- def test_capacity
76
+ def test_strings
77
77
  p = Oj::Parser.new(:usual)
78
- p.capacity = 1000
78
+ doc = p.parse('{"ぴ": "", "ぴ ": "x", "c": "ぴーたー", "d": " ぴーたー "}')
79
+ assert_equal({'ぴ' => '', 'ぴ ' => 'x', 'c' => 'ぴーたー', 'd' => ' ぴーたー '}, doc)
80
+ end
81
+
82
+ def test_capacity
83
+ p = Oj::Parser.new(:usual, capacity: 1000)
79
84
  assert_equal(4096, p.capacity)
80
85
  p.capacity = 5000
81
86
  assert_equal(5000, p.capacity)
@@ -181,8 +186,7 @@ class UsualTest < Minitest::Test
181
186
  end
182
187
 
183
188
  def test_missing_class
184
- p = Oj::Parser.new(:usual)
185
- p.create_id = '^'
189
+ p = Oj::Parser.new(:usual, create_id: '^')
186
190
  json = '{"a":true,"^":"Auto","b":false}'
187
191
  doc = p.parse(json)
188
192
  assert_equal(Hash, doc.class)