Skip to content
Browse files

now dumping struct in the correct format. Loading still unimplemented

  • Loading branch information...
1 parent 4d28d2c commit 0dfa515b00b921f8bff48681263e43ca38e533fc Peter Ohler committed Feb 28, 2012
Showing with 144 additions and 16 deletions.
  1. +9 −3 README.md
  2. +90 −0 ext/oj/dump.c
  3. +9 −3 ext/oj/load.c
  4. +1 −10 notes
  5. +35 −0 test/tests.rb
View
12 README.md
@@ -260,17 +260,23 @@ key. After that each entry is treated as a variable of the Object where the
key is the variable name without the preceeding '@'. An example is
{"^o":"Oj::Bag","x":58,"y":"marbles"}.
-9. When encoding an Object, if the variable name does not begin with an '@'
+9. A "^u" JSON Object key indicates the value should be converted to a Ruby
+Struct. The first entry in the JSON Object must be a class with the "^u"
+key. After that each entry is is given a numeric position in the struct and
+that is used as the key in the JSON Object. An example is
+{"^u":["Range",1,7,false]}.
+
+10. When encoding an Object, if the variable name does not begin with an '@'
character then the name preceeded by a '~' character. This occurs in the
Exception class. An example is {"^o":"StandardError","~mesg":"A
Message","~bt":[".\/tests.rb:345:in `test_exception'"]}
-10. If a Hash entry has a key that is not a String or Symbol then the entry is
+11. If a Hash entry has a key that is not a String or Symbol then the entry is
encoded with a key of the form "^#n" where n is a hex number. The value that
is an Array where the first element is the key in the Hash and the second is
the value. An example is {"^#3":[2,5]}.
-11. A "^i" JSON entry in either an Object or Array is the ID of the Ruby
+12. A "^i" JSON entry in either an Object or Array is the ID of the Ruby
Object being encoded. It is used when the :circular flag is set. It can appear
in either a JSON Object or in a JSON Array. If alone it represented a link to
the original Hash or JSON. If an added attribute it is the ID of the original
View
90 ext/oj/dump.c
@@ -98,6 +98,8 @@ static void dump_data_comp(VALUE obj, Out out);
static void dump_data_obj(VALUE obj, Out out);
static void dump_obj_comp(VALUE obj, int depth, Out out);
static void dump_obj_obj(VALUE obj, int depth, Out out);
+static void dump_struct_comp(VALUE obj, int depth, Out out);
+static void dump_struct_obj(VALUE obj, int depth, Out out);
static int dump_attr_cb(ID key, VALUE value, Out out);
static void dump_obj_attrs(VALUE obj, int with_class, int depth, Out out);
@@ -154,6 +156,19 @@ fill_indent(Out out, int cnt) {
}
}
+inline static const char*
+ulong2str(uint32_t num, char *end) {
+ char *b;
+
+ *end-- = '\0';
+ for (b = end; 0 < num || b == end; num /= 10, b--) {
+ *b = (num % 10) + '0';
+ }
+ b++;
+
+ return b;
+}
+
static void
grow(Out out, size_t len) {
size_t size = out->end - out->buf;
@@ -789,6 +804,73 @@ dump_obj_attrs(VALUE obj, int with_class, int depth, Out out) {
}
static void
+dump_struct_comp(VALUE obj, int depth, Out out) {
+ if (rb_respond_to(obj, oj_to_hash_id)) {
+ VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
+
+ if (T_HASH != rb_type(h)) {
+ rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
+ }
+ dump_hash(h, depth, out->opts->mode, out);
+ } else if (rb_respond_to(obj, oj_to_json_id)) {
+ VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
+ const char *s = StringValuePtr(rs);
+ int len = (int)RSTRING_LEN(rs);
+
+ if (out->end - out->cur <= len) {
+ grow(out, len);
+ }
+ memcpy(out->cur, s, len);
+ out->cur += len;
+ } else {
+ dump_struct_obj(obj, depth, out);
+ }
+}
+
+static void
+dump_struct_obj(VALUE obj, int depth, Out out) {
+ VALUE clas = rb_obj_class(obj);
+ const char *class_name = rb_class2name(clas);
+ VALUE *vp;
+ int i;
+ int d2 = depth + 1;
+ int d3 = d2 + 1;
+ size_t len = strlen(class_name);
+ size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
+
+ if (out->end - out->cur <= (long)size) {
+ grow(out, size);
+ }
+ *out->cur++ = '{';
+ fill_indent(out, d2);
+ *out->cur++ = '"';
+ *out->cur++ = '^';
+ *out->cur++ = 'u';
+ *out->cur++ = '"';
+ *out->cur++ = ':';
+ *out->cur++ = '[';
+ fill_indent(out, d3);
+ *out->cur++ = '"';
+ memcpy(out->cur, class_name, len);
+ out->cur += len;
+ *out->cur++ = '"';
+ *out->cur++ = ',';
+ size = d3 * out->indent + 2;
+ for (i = (int)RSTRUCT_LEN(obj), vp = RSTRUCT_PTR(obj); 0 < i; i--, vp++) {
+ if (out->end - out->cur <= (long)size) {
+ grow(out, size);
+ }
+ fill_indent(out, d3);
+ dump_val(*vp, d3, out);
+ *out->cur++ = ',';
+ }
+ out->cur--;
+ *out->cur++ = ']';
+ *out->cur++ = '}';
+ *out->cur = '\0';
+}
+
+static void
raise_strict(VALUE obj) {
rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in strict mode.\n", rb_class2name(rb_obj_class(obj)));
}
@@ -850,6 +932,14 @@ dump_val(VALUE obj, int depth, Out out) {
}
break;
case T_STRUCT: // for Range
+ switch (out->opts->mode) {
+ case StrictMode: raise_strict(obj); break;
+ case NullMode: dump_nil(out); break;
+ case CompatMode: dump_struct_comp(obj, depth, out); break;
+ case ObjectMode:
+ default: dump_struct_obj(obj, depth, out); break;
+ }
+ break;
#if (defined T_COMPLEX && defined RCOMPLEX)
case T_COMPLEX:
#endif
View
12 ext/oj/load.c
@@ -54,7 +54,7 @@ typedef struct _ParseInfo {
static VALUE classname2class(const char *name, ParseInfo pi);
static VALUE read_next(ParseInfo pi, int hint);
static VALUE read_obj(ParseInfo pi);
-static VALUE read_array(ParseInfo pi);
+static VALUE read_array(ParseInfo pi, int hint);
static VALUE read_str(ParseInfo pi, int hint);
static VALUE read_num(ParseInfo pi);
static VALUE read_time(ParseInfo pi);
@@ -207,7 +207,7 @@ read_next(ParseInfo pi, int hint) {
obj = read_obj(pi);
break;
case '[':
- obj = read_array(pi);
+ obj = read_array(pi, hint);
break;
case '"':
obj = read_str(pi, hint);
@@ -306,6 +306,11 @@ read_obj(ParseInfo pi) {
obj_type = T_OBJECT;
key = Qundef;
break;
+ case 'u': // Struct
+ obj = read_next(pi, T_STRUCT);
+ obj_type = T_STRUCT;
+ key = Qundef;
+ break;
case 'i': // Id for circular reference
// TBD
default:
@@ -370,10 +375,11 @@ read_obj(ParseInfo pi) {
}
static VALUE
-read_array(ParseInfo pi) {
+read_array(ParseInfo pi, int hint) {
VALUE a = rb_ary_new();
VALUE e;
+ // TBD postpone creation of array if hint is T_STRUCT, then load for struct
pi->s++;
next_non_white(pi);
if (']' == *pi->s) {
View
11 notes
@@ -5,22 +5,13 @@
- next
+ - circular reference
- complete all types for dump and load in :object mode
case T_STRUCT: // for Range
case T_COMPLEX:
case T_RATIONAL:
case T_REGEXP:
- - document format
- - ^? for different types
- - ^s Symbol
- - ^o Object
- - ^c Class
- - ^t Time
- - ~ special attr without @ as in Exception mesg and bt
- - ^# for hash entry
-
-
- stream
- load
- dump
View
35 test/tests.rb
@@ -42,6 +42,12 @@ def to_hash()
end
end # Jazz
+class Range
+ def to_hash()
+ { 'begin' => self.begin, 'end' => self.end, 'exclude_end' => self.exclude_end? }
+ end
+end # Range
+
class Juice < ::Test::Unit::TestCase
def test_get_options
@@ -339,6 +345,7 @@ def test_object_object
assert_equal(obj, obj2)
end
+ # Exception
def test_exception
err = nil
begin
@@ -356,6 +363,34 @@ def test_exception
assert_equal(e, e2);
end
+ # Range
+ def test_range_strict
+ begin
+ json = Oj.dump(1..7, :mode => :strict)
+ rescue Exception => e
+ assert(true)
+ end
+ end
+ def test_range_null
+ json = Oj.dump(1..7, :mode => :null)
+ assert_equal('null', json)
+ end
+ def test_range_compat
+ json = Oj.dump(1..7, :mode => :compat)
+ assert_equal(%{{"begin":1,"end":7,"exclude_end":false}}, json)
+ json = Oj.dump(1...7, :mode => :compat)
+ assert_equal(%{{"begin":1,"end":7,"exclude_end":true}}, json)
+ end
+ def test_range_object
+ Oj.default_options = { :mode => :object }
+ json = Oj.dump(1..7, :mode => :object, :indent => 0)
+ assert_equal(%{{"^u":["Range",1,7,false]}}, json)
+ dump_and_load(1..7, true)
+ dump_and_load(1..1, true)
+ dump_and_load(1...7, true)
+ end
+
+ # autodefine Oj::Bag
def test_bag
json = %{{
"^o":"Jem",

0 comments on commit 0dfa515

Please sign in to comment.
Something went wrong with that request. Please try again.