oj 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of oj might be problematic. Click here for more details.

data/README.md CHANGED
@@ -32,11 +32,13 @@ A fast JSON parser and Object marshaller as a Ruby gem.
32
32
 
33
33
  ## <a name="release">Release Notes</a>
34
34
 
35
- ### Release 2.0.0
35
+ ### Release 2.0.1
36
36
 
37
- - Thanks to yuki24 Floats are now output with a decimal even if they are an integer value.
37
+ - BigDecimals now dump to a string in compat mode thanks to cgriego.
38
38
 
39
- - <b>The Simple API for JSON (SAJ) API has been added. Read more about it on the [Oj::Saj page](http://www.ohler.com/oj/Oj/Saj.html).</b>
39
+ - High precision time (nano time) can be turned off for better compatibility with other JSON parsers.
40
+
41
+ - Times before 1970 now encode correctly.
40
42
 
41
43
  ## <a name="description">Description</a>
42
44
 
@@ -928,6 +928,7 @@ dump_time(VALUE obj, Out out) {
928
928
  char *b = buf + sizeof(buf) - 1;
929
929
  long size;
930
930
  char *dot = b - 10;
931
+ int neg = 0;
931
932
  #if HAS_RB_TIME_TIMESPEC
932
933
  struct timespec ts = rb_time_timespec(obj);
933
934
  time_t sec = ts.tv_sec;
@@ -941,13 +942,40 @@ dump_time(VALUE obj, Out out) {
941
942
  #endif
942
943
  #endif
943
944
 
945
+ if (0 > sec) {
946
+ neg = 1;
947
+ sec = -sec;
948
+ if (0 < nsec) {
949
+ nsec = 1000000000 - nsec;
950
+ #ifndef JRUBY_RUBY
951
+ sec--;
952
+ #endif
953
+ }
954
+ }
944
955
  *b-- = '\0';
945
- for (; dot < b; b--, nsec /= 10) {
946
- *b = '0' + (nsec % 10);
956
+ if (0 < out->opts->sec_prec) {
957
+ if (9 > out->opts->sec_prec) {
958
+ int i;
959
+
960
+ for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
961
+ dot++;
962
+ nsec = (nsec + 5) / 10;
963
+ }
964
+ }
965
+ for (; dot < b; b--, nsec /= 10) {
966
+ *b = '0' + (nsec % 10);
967
+ }
968
+ *b-- = '.';
969
+ }
970
+ if (0 == sec) {
971
+ *b-- = '0';
972
+ } else {
973
+ for (; 0 < sec; b--, sec /= 10) {
974
+ *b = '0' + (sec % 10);
975
+ }
947
976
  }
948
- *b-- = '.';
949
- for (; 0 < sec; b--, sec /= 10) {
950
- *b = '0' + (sec % 10);
977
+ if (neg) {
978
+ *b-- = '-';
951
979
  }
952
980
  b++;
953
981
  size = sizeof(buf) - (b - buf) - 1;
@@ -1012,7 +1040,7 @@ dump_xml_time(VALUE obj, Out out) {
1012
1040
  tzmin = (int)(tm->tm_gmtoff / 60) - (tzhour * 60);
1013
1041
  }
1014
1042
  #endif
1015
- if (0 == nsec) {
1043
+ if (0 == nsec || 0 == out->opts->sec_prec) {
1016
1044
  if (0 == tzhour && 0 == tzmin) {
1017
1045
  sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ",
1018
1046
  tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
@@ -1026,11 +1054,23 @@ dump_xml_time(VALUE obj, Out out) {
1026
1054
  dump_cstr(buf, 25, 0, 0, out);
1027
1055
  }
1028
1056
  } else {
1029
- sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d",
1057
+ char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
1058
+ int len = 35;
1059
+
1060
+ if (9 > out->opts->sec_prec) {
1061
+ int i;
1062
+
1063
+ format[32] = '0' + out->opts->sec_prec;
1064
+ for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
1065
+ nsec = (nsec + 5) / 10;
1066
+ len--;
1067
+ }
1068
+ }
1069
+ sprintf(buf, format,
1030
1070
  tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1031
1071
  tm->tm_hour, tm->tm_min, tm->tm_sec, nsec,
1032
1072
  tzsign, tzhour, tzmin);
1033
- dump_cstr(buf, 35, 0, 0, out);
1073
+ dump_cstr(buf, len, 0, 0, out);
1034
1074
  }
1035
1075
  }
1036
1076
 
@@ -1074,14 +1114,8 @@ dump_data_comp(VALUE obj, Out out) {
1074
1114
  } else {
1075
1115
  VALUE rstr;
1076
1116
 
1077
- if (oj_bigdecimal_class == clas) {
1078
- //rstr = rb_funcall(obj, oj_to_s_id, 1, rb_intern("E"));
1079
- rstr = rb_funcall(obj, oj_to_s_id, 0);
1080
- dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1081
- } else {
1082
- rstr = rb_funcall(obj, oj_to_s_id, 0);
1083
- dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1084
- }
1117
+ rstr = rb_funcall(obj, oj_to_s_id, 0);
1118
+ dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1085
1119
  }
1086
1120
  }
1087
1121
 
@@ -1146,7 +1180,8 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
1146
1180
  if (oj_bigdecimal_class == clas) {
1147
1181
  VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1148
1182
 
1149
- dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1183
+ //dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
1184
+ dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
1150
1185
  } else {
1151
1186
  Odd odd = oj_get_odd(clas);
1152
1187
 
@@ -825,7 +825,12 @@ static VALUE
825
825
  read_time(ParseInfo pi) {
826
826
  time_t v = 0;
827
827
  long v2 = 0;
828
+ int neg = 0;
828
829
 
830
+ if ('-' == *pi->s) {
831
+ pi->s++;
832
+ neg = 1;
833
+ }
829
834
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
830
835
  v = v * 10 + (*pi->s - '0');
831
836
  }
@@ -840,6 +845,13 @@ read_time(ParseInfo pi) {
840
845
  v2 *= 10;
841
846
  }
842
847
  }
848
+ if (neg) {
849
+ v = -v;
850
+ if (0 < v2) {
851
+ v--;
852
+ v2 = 1000000000 - v2;
853
+ }
854
+ }
843
855
  #if HAS_NANO_TIME
844
856
  return rb_time_nano_new(v, v2);
845
857
  #else
@@ -91,6 +91,7 @@ static VALUE mode_sym;
91
91
  static VALUE null_sym;
92
92
  static VALUE object_sym;
93
93
  static VALUE ruby_sym;
94
+ static VALUE sec_prec_sym;
94
95
  static VALUE strict_sym;
95
96
  static VALUE symbol_keys_sym;
96
97
  static VALUE time_format_sym;
@@ -128,6 +129,7 @@ struct _Options oj_default_options = {
128
129
  UnixTime, // time_format
129
130
  json_class, // create_id
130
131
  65536, // max_stack
132
+ 9, // sec_prec
131
133
  0, // dump_opts
132
134
  };
133
135
 
@@ -157,6 +159,7 @@ oj_get_odd(VALUE clas) {
157
159
  * - time_format: [:unix|:xmlschema|:ruby] time format when dumping in :compat mode
158
160
  * - create_id: [String|nil] create id for json compatible object encoding, default is 'json_create'
159
161
  * - max_stack: [Fixnum|nil] maximum json size to allocate on the stack, default is 65536
162
+ * - second_precision: [Fixnum|nil] number of digits after the decimal when dumping the seconds portion of time
160
163
  * @return [Hash] all current option settings.
161
164
  */
162
165
  static VALUE
@@ -164,6 +167,7 @@ get_def_opts(VALUE self) {
164
167
  VALUE opts = rb_hash_new();
165
168
 
166
169
  rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
170
+ rb_hash_aset(opts, sec_prec_sym, INT2FIX(oj_default_options.sec_prec));
167
171
  rb_hash_aset(opts, max_stack_sym, INT2FIX(oj_default_options.max_stack));
168
172
  rb_hash_aset(opts, circular_sym, (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
169
173
  rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
@@ -210,6 +214,7 @@ get_def_opts(VALUE self) {
210
214
  * :ruby Time.to_s formatted String
211
215
  * @param [String|nil] :create_id create id for json compatible object encoding
212
216
  * @param [Fixnum|nil] :max_stack maximum size to allocate on the stack for a JSON String
217
+ * @param [Fixnum|nil] :second_precision number of digits after the decimal when dumping the seconds portion of time
213
218
  * @return [nil]
214
219
  */
215
220
  static VALUE
@@ -230,6 +235,19 @@ set_def_opts(VALUE self, VALUE opts) {
230
235
  Check_Type(v, T_FIXNUM);
231
236
  oj_default_options.indent = FIX2INT(v);
232
237
  }
238
+ v = rb_hash_aref(opts, sec_prec_sym);
239
+ if (Qnil != v) {
240
+ int n;
241
+
242
+ Check_Type(v, T_FIXNUM);
243
+ n = FIX2INT(v);
244
+ if (0 > n) {
245
+ n = 0;
246
+ } else if (9 < n) {
247
+ n = 9;
248
+ }
249
+ oj_default_options.sec_prec = n;
250
+ }
233
251
  v = rb_hash_aref(opts, max_stack_sym);
234
252
  if (Qnil != v) {
235
253
  int i;
@@ -323,6 +341,20 @@ parse_options(VALUE ropts, Options copts) {
323
341
  }
324
342
  copts->indent = NUM2INT(v);
325
343
  }
344
+ if (Qnil != (v = rb_hash_lookup(ropts, sec_prec_sym))) {
345
+ int n;
346
+
347
+ if (rb_cFixnum != rb_obj_class(v)) {
348
+ rb_raise(rb_eArgError, ":second_precision must be a Fixnum.");
349
+ }
350
+ n = NUM2INT(v);
351
+ if (0 > n) {
352
+ n = 0;
353
+ } else if (9 < n) {
354
+ n = 9;
355
+ }
356
+ copts->sec_prec = n;
357
+ }
326
358
  if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
327
359
  if (object_sym == v) {
328
360
  copts->mode = ObjectMode;
@@ -1009,6 +1041,7 @@ void Init_oj() {
1009
1041
  null_sym = ID2SYM(rb_intern("null")); rb_gc_register_address(&null_sym);
1010
1042
  object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
1011
1043
  ruby_sym = ID2SYM(rb_intern("ruby")); rb_gc_register_address(&ruby_sym);
1044
+ sec_prec_sym = ID2SYM(rb_intern("second_precision"));rb_gc_register_address(&sec_prec_sym);
1012
1045
  strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
1013
1046
  symbol_keys_sym = ID2SYM(rb_intern("symbol_keys")); rb_gc_register_address(&symbol_keys_sym);
1014
1047
  time_format_sym = ID2SYM(rb_intern("time_format")); rb_gc_register_address(&time_format_sym);
@@ -110,6 +110,7 @@ typedef struct _Options {
110
110
  char time_format; // TimeFormat
111
111
  const char *create_id; // 0 or string
112
112
  size_t max_stack; // max size to allocate on the stack
113
+ int sec_prec; // second precision when dumping time
113
114
  DumpOpts dump_opts;
114
115
  } *Options;
115
116
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '2.0.0'
4
+ VERSION = '2.0.1'
5
5
  end
@@ -354,7 +354,6 @@ class DocTest < ::Test::Unit::TestCase
354
354
  end
355
355
  end
356
356
 
357
-
358
357
  def test_open_no_close
359
358
  json = %{{"a":[1,2,3]}}
360
359
  doc = Oj::Doc.open(json)
@@ -110,6 +110,7 @@ class Juice < ::Test::Unit::TestCase
110
110
  def test0_get_options
111
111
  opts = Oj.default_options()
112
112
  assert_equal({ :indent=>0,
113
+ :second_precision=>9,
113
114
  :circular=>false,
114
115
  :auto_define=>true,
115
116
  :symbol_keys=>false,
@@ -123,6 +124,7 @@ class Juice < ::Test::Unit::TestCase
123
124
  def test0_set_options
124
125
  orig = {
125
126
  :indent=>0,
127
+ :second_precision=>9,
126
128
  :circular=>false,
127
129
  :auto_define=>true,
128
130
  :symbol_keys=>false,
@@ -133,6 +135,7 @@ class Juice < ::Test::Unit::TestCase
133
135
  :create_id=>'json_class'}
134
136
  o2 = {
135
137
  :indent=>4,
138
+ :second_precision=>7,
136
139
  :circular=>true,
137
140
  :auto_define=>false,
138
141
  :symbol_keys=>true,
@@ -265,6 +268,22 @@ class Juice < ::Test::Unit::TestCase
265
268
  json = Oj.dump(t, :mode => :compat)
266
269
  assert_equal(%{1325775487.123456000}, json)
267
270
  end
271
+ def test_unix_time_compat_precision
272
+ t = Time.xmlschema("2012-01-05T23:58:07.123456789+09:00")
273
+ #t = Time.local(2012, 1, 5, 23, 58, 7, 123456)
274
+ json = Oj.dump(t, :mode => :compat, :second_precision => 5)
275
+ assert_equal(%{1325775487.12346}, json)
276
+ end
277
+ def test_unix_time_compat_early
278
+ t = Time.xmlschema("1954-01-05T00:00:00.123456789+00:00")
279
+ json = Oj.dump(t, :mode => :compat, :second_precision => 5)
280
+ assert_equal(%{-504575999.87654}, json)
281
+ end
282
+ def test_unix_time_compat_1970
283
+ t = Time.xmlschema("1970-01-01T00:00:00.123456789+00:00")
284
+ json = Oj.dump(t, :mode => :compat, :second_precision => 5)
285
+ assert_equal(%{0.12346}, json)
286
+ end
268
287
  def test_ruby_time_compat
269
288
  t = Time.xmlschema("2012-01-05T23:58:07.123456000+09:00")
270
289
  json = Oj.dump(t, :mode => :compat, :time_format => :ruby)
@@ -309,6 +328,25 @@ class Juice < ::Test::Unit::TestCase
309
328
  assert_equal(%{"2012-01-05T23:58:07%s%02d:%02d"} % [sign, tz / 3600, tz / 60 % 60], json)
310
329
  end
311
330
  end
331
+ def test_xml_time_compat_precision
332
+ begin
333
+ t = Time.new(2012, 1, 5, 23, 58, 7.123456789, 32400)
334
+ json = Oj.dump(t, :mode => :compat, :time_format => :xmlschema, :second_precision => 5)
335
+ assert_equal(%{"2012-01-05T23:58:07.12346+09:00"}, json)
336
+ rescue Exception
337
+ # some Rubies (1.8.7) do not allow the timezome to be set
338
+ t = Time.local(2012, 1, 5, 23, 58, 7, 123456)
339
+ json = Oj.dump(t, :mode => :compat, :time_format => :xmlschema, :second_precision => 5)
340
+ tz = t.utc_offset
341
+ # Ruby does not handle a %+02d properly so...
342
+ sign = '+'
343
+ if 0 > tz
344
+ sign = '-'
345
+ tz = -tz
346
+ end
347
+ assert_equal(%{"2012-01-05T23:58:07.12346%s%02d:%02d"} % [sign, tz / 3600, tz / 60 % 60], json)
348
+ end
349
+ end
312
350
  def test_xml_time_compat_zulu
313
351
  begin
314
352
  t = Time.new(2012, 1, 5, 23, 58, 7.0, 0)
@@ -327,6 +365,11 @@ class Juice < ::Test::Unit::TestCase
327
365
  Oj.default_options = { :mode => :object }
328
366
  dump_and_load(t, false)
329
367
  end
368
+ def test_time_object_early
369
+ t = Time.xmlschema("1954-01-05T00:00:00.123456")
370
+ Oj.default_options = { :mode => :object }
371
+ dump_and_load(t, false)
372
+ end
330
373
 
331
374
  # Class
332
375
  def test_class_strict
@@ -666,10 +709,14 @@ class Juice < ::Test::Unit::TestCase
666
709
  Oj.default_options = {:mode => mode}
667
710
  end
668
711
  def test_bigdecimal_compat
712
+ orig = BigDecimal.new('80.51')
713
+ json = Oj.dump(orig, :mode => :compat)
714
+ bg = Oj.load(json, :mode => :compat)
715
+ assert_equal(orig.to_s, bg)
669
716
  orig = BigDecimal.new('3.14159265358979323846')
670
717
  json = Oj.dump(orig, :mode => :compat)
671
718
  bg = Oj.load(json, :mode => :compat)
672
- assert_equal(orig, bg)
719
+ assert_equal(orig.to_s, bg)
673
720
  end
674
721
  def test_bigdecimal_object
675
722
  mode = Oj.default_options[:mode]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-18 00:00:00.000000000 Z
12
+ date: 2013-01-15 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! 'The fastest JSON parser and object serializer. '
15
15
  email: peter@ohler.com