Optimized JSON (Oj), as the name implies was written to provide speed optimized JSON handling.
Oj has several dump or serialization modes which control how Objects are converted to JSON. These modes are set with the :mode option in either the default options or as one of the options to the dump() method.
:strict mode will only allow the 7 basic JSON types to be serialized. Any other Object will raise and Exception.
:null mode replaces any Object that is not one of the JSON types is replaced by a JSON null.
:object mode will dump any Object as a JSON Object with keys that match the Ruby Object's variable names without the '@' character. This is the highest performance mode.
:compat mode is is the compatible with other systems. It will serialize any Object but will check to see if the Object implements a to_hash() or to_json() method. If either exists that method is used for serializing the Object. The to_hash() is more flexible and produces more consistent output so it has a preference over the to_json() method. If neither the to_json() or to_hash() methods exist then the Oj internal Object variable encoding is used.
for older versions of JSON, the deprecated unparse methods
Current version of the module.
Returns the default load and dump options as a Hash. The options are
indent: [Fixnum] number of spaces to indent each element in an JSON document
circular: [true|false|nil] support circular references while dumping
auto_define: [true|false|nil] automatically define classes if they do not exist
symbol_keys: [true|false|nil] use symbols instead of strings for hash keys
mode: [:object|:strict|:compat|:null] load and dump modes to use for JSON
time_format: [:unix|:xmlschema|:ruby] time format when dumping in :compat mode
create_id: [String|nil] create id for json compatible object encoding, default is 'json_create'
max_stack: [Fixnum|nil] maximum json size to allocate on the stack, default is 65536
@return [Hash] all current option settings.
static VALUE
get_def_opts(VALUE self) {
VALUE opts = rb_hash_new();
rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
rb_hash_aset(opts, max_stack_sym, INT2FIX(oj_default_options.max_stack));
rb_hash_aset(opts, circular_sym, (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
rb_hash_aset(opts, ascii_only_sym, (Yes == oj_default_options.ascii_only) ? Qtrue : ((No == oj_default_options.ascii_only) ? Qfalse : Qnil));
rb_hash_aset(opts, symbol_keys_sym, (Yes == oj_default_options.sym_key) ? Qtrue : ((No == oj_default_options.sym_key) ? Qfalse : Qnil));
switch (oj_default_options.mode) {
case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
case NullMode: rb_hash_aset(opts, mode_sym, null_sym); break;
case ObjectMode:
default: rb_hash_aset(opts, mode_sym, object_sym); break;
}
switch (oj_default_options.time_format) {
case XmlTime: rb_hash_aset(opts, time_format_sym, xmlschema_sym); break;
case RubyTime: rb_hash_aset(opts, time_format_sym, ruby_sym); break;
case UnixTime:
default: rb_hash_aset(opts, time_format_sym, unix_sym); break;
}
rb_hash_aset(opts, create_id_sym, (0 == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
return opts;
}
Sets the default options for load and dump. @param [Hash] opts options to change @param [Fixnum] :indent number of spaces to indent each element in an JSON document @param [true|false|nil] :circular support circular references while dumping @param [true|false|nil] :auto_define automatically define classes if they do not exist @param [true|false|nil] :symbol_keys convert hash keys to symbols @param [true|false|nil] :ascii_only encode all high-bit characters as escaped sequences if true @param [:object|:strict|:compat|:null] load and dump mode to use for JSON
:strict raises an exception when a non-supported Object is encountered. :compat attempts to extract variable values from an Object using to_json() or to_hash() then it walks the Object's variables if neither is found. The :object mode ignores to_hash() and to_json() methods and encodes variables using code internal to the Oj gem. The :null mode ignores non-supported Objects and replaces them with a null.
@param [:unix|:xmlschema|:ruby] time format when dumping in :compat mode
:unix decimal number denoting the number of seconds since 1/1/1970, :xmlschema date-time format taken from XML Schema as a String, :ruby Time.to_s formatted String
@param [String|nil] :create_id create id for json compatible object encoding @param [Fixnum|nil] :max_stack maximum size to allocate on the stack for a JSON String @return [nil]
static VALUE
set_def_opts(VALUE self, VALUE opts) {
struct _YesNoOpt ynos[] = {
{ circular_sym, &oj_default_options.circular },
{ auto_define_sym, &oj_default_options.auto_define },
{ symbol_keys_sym, &oj_default_options.sym_key },
{ ascii_only_sym, &oj_default_options.ascii_only },
{ Qnil, 0 }
};
YesNoOpt o;
VALUE v;
Check_Type(opts, T_HASH);
v = rb_hash_aref(opts, indent_sym);
if (Qnil != v) {
Check_Type(v, T_FIXNUM);
oj_default_options.indent = FIX2INT(v);
}
v = rb_hash_aref(opts, max_stack_sym);
if (Qnil != v) {
int i;
Check_Type(v, T_FIXNUM);
i = FIX2INT(v);
if (0 > i) {
i = 0;
}
oj_default_options.max_stack = (size_t)i;
}
v = rb_hash_lookup(opts, mode_sym);
if (Qnil == v) {
// ignore
} else if (object_sym == v) {
oj_default_options.mode = ObjectMode;
} else if (strict_sym == v) {
oj_default_options.mode = StrictMode;
} else if (compat_sym == v) {
oj_default_options.mode = CompatMode;
} else if (null_sym == v) {
oj_default_options.mode = NullMode;
} else {
rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
}
v = rb_hash_lookup(opts, time_format_sym);
if (Qnil == v) {
// ignore
} else if (unix_sym == v) {
oj_default_options.time_format = UnixTime;
} else if (xmlschema_sym == v) {
oj_default_options.time_format = XmlTime;
} else if (ruby_sym == v) {
oj_default_options.time_format = RubyTime;
} else {
rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.");
}
if (Qtrue == rb_funcall(opts, rb_intern("has_key?"), 1, create_id_sym)) {
if (0 != oj_default_options.create_id) {
if (json_class != oj_default_options.create_id) {
xfree((char*)oj_default_options.create_id);
}
oj_default_options.create_id = 0;
}
v = rb_hash_lookup(opts, create_id_sym);
if (Qnil != v) {
size_t len = RSTRING_LEN(v) + 1;
oj_default_options.create_id = ALLOC_N(char, len);
strcpy((char*)oj_default_options.create_id, StringValuePtr(v));
}
}
for (o = ynos; 0 != o->attr; o++) {
if (Qtrue != rb_funcall(opts, rb_intern("has_key?"), 1, o->sym)) {
continue;
}
if (Qnil != (v = rb_hash_lookup(opts, o->sym))) {
if (Qtrue == v) {
*o->attr = Yes;
} else if (Qfalse == v) {
*o->attr = No;
} else {
rb_raise(rb_eArgError, "%s must be true, false, or nil.", rb_id2name(SYM2ID(o->sym)));
}
}
}
return Qnil;
}
Encodes an object as a JSON String.
@param [Object] obj object to convert to encode as JSON @param [IO] anIO an IO that allows writing @param [Fixnum] limit ignored
static VALUE
dump(int argc, VALUE *argv, VALUE self) {
char *json;
struct _Options copts = oj_default_options;
VALUE rstr;
if (2 == argc) {
parse_options(argv[1], &copts);
}
if (0 == (json = oj_write_obj_to_str(*argv, &copts))) {
rb_raise(rb_eNoMemError, "Not enough memory.");
}
rstr = rb_str_new2(json);
#if HAS_ENCODING_SUPPORT
rb_enc_associate(rstr, oj_utf8_encoding);
#endif
xfree(json);
return rstr;
}
Loads a Ruby Object from a JSON source that can be either a String or an IO. If Proc is given or a block is providedit is called with each nested element of the loaded Object.
@param [String|IO] source JSON source @param [Proc] proc to yield to on each element or nil
static VALUE
load(int argc, VALUE *argv, VALUE self) {
struct _Options options = oj_default_options;
if (1 > argc) {
rb_raise(rb_eArgError, "Wrong number of arguments to load().");
}
if (2 <= argc) {
parse_options(argv[1], &options);
}
return load_with_opts(*argv, &options);
}
Parses a JSON document from a file into a Hash, Array, String, Fixnum, Float, true, false, or nil. Raises an exception if the JSON is malformed or the classes specified are not valid.
@param [String] path path to a file containing a JSON document @param [Hash] options load options (same as default_options)
static VALUE
load_file(int argc, VALUE *argv, VALUE self) {
char *path;
char *json;
FILE *f;
unsigned long len;
VALUE obj;
struct _Options options = oj_default_options;
size_t max_stack = oj_default_options.max_stack;
Check_Type(*argv, T_STRING);
path = StringValuePtr(*argv);
if (0 == (f = fopen(path, "r"))) {
rb_raise(rb_eIOError, "%s", strerror(errno));
}
fseek(f, 0, SEEK_END);
len = ftell(f);
if (max_stack < len) {
json = ALLOC_N(char, len + 1);
} else {
json = ALLOCA_N(char, len + 1);
}
fseek(f, 0, SEEK_SET);
if (len != fread(json, 1, len, f)) {
fclose(f);
rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")), "Failed to read %ld bytes from %s.", len, path);
}
fclose(f);
json[len] = '\0';
if (2 <= argc) {
parse_options(argv[1], &options);
}
obj = oj_parse(json, &options);
if (max_stack < len) {
xfree(json);
}
return obj;
}
Creates the JSON module with methods and classes to mimic the JSON gem. After this method is invoked calls that expect the JSON module will use Oj instead and be faster than the original JSON. Most options that could be passed to the JSON methods are supported. The calls to set parser or generator will not raise an Exception but will not have any effect.
static VALUE
define_mimic_json(int argc, VALUE *argv, VALUE self) {
if (Qnil == mimic) {
VALUE ext;
VALUE dummy;
if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
rb_raise(rb_const_get_at(Oj, rb_intern("MimicError")),
"JSON module already exists. Can not mimic. Do not require 'json' before calling mimic_JSON.");
}
mimic = rb_define_module("JSON");
ext = rb_define_module_under(mimic, "Ext");
dummy = rb_define_class_under(ext, "Parser", rb_cObject);
dummy = rb_define_class_under(ext, "Generator", rb_cObject);
// convince Ruby that the json gem has already been loaded
dummy = rb_gv_get("$LOADED_FEATURES");
if (rb_type(dummy) == T_ARRAY) {
rb_ary_push(dummy, rb_str_new2("json"));
if (0 < argc) {
VALUE mimic_args[1];
*mimic_args = *argv;
rb_funcall2(Oj, rb_intern("mimic_loaded"), 1, mimic_args);
} else {
rb_funcall2(Oj, rb_intern("mimic_loaded"), 0, 0);
}
}
rb_define_module_function(mimic, "parser=", no_op1, 1);
rb_define_module_function(mimic, "generator=", no_op1, 1);
rb_define_module_function(mimic, "create_id=", mimic_create_id, 1);
rb_define_module_function(mimic, "dump", mimic_dump, -1);
rb_define_module_function(mimic, "load", mimic_load, -1);
rb_define_module_function(mimic, "restore", mimic_load, -1);
rb_define_module_function(mimic, "recurse_proc", mimic_recurse_proc, 1);
rb_define_module_function(mimic, "[]", mimic_dump_load, -1);
rb_define_module_function(mimic, "generate", mimic_generate, -1);
rb_define_module_function(mimic, "fast_generate", mimic_generate, -1);
rb_define_module_function(mimic, "pretty_generate", mimic_pretty_generate, -1);
/* for older versions of JSON, the deprecated unparse methods */
rb_define_module_function(mimic, "unparse", mimic_generate, -1);
rb_define_module_function(mimic, "fast_unparse", mimic_generate, -1);
rb_define_module_function(mimic, "pretty_unparse", mimic_pretty_generate, -1);
rb_define_module_function(mimic, "parse", mimic_parse, -1);
rb_define_module_function(mimic, "parse!", mimic_parse, -1);
array_nl_sym = ID2SYM(rb_intern("array_nl")); rb_gc_register_address(&array_nl_sym);
create_additions_sym = ID2SYM(rb_intern("create_additions")); rb_gc_register_address(&create_additions_sym);
object_nl_sym = ID2SYM(rb_intern("object_nl")); rb_gc_register_address(&object_nl_sym);
space_before_sym = ID2SYM(rb_intern("space_before")); rb_gc_register_address(&space_before_sym);
space_sym = ID2SYM(rb_intern("space")); rb_gc_register_address(&space_sym);
symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
oj_default_options.mode = CompatMode;
oj_default_options.ascii_only = Yes;
}
return mimic;
}
# File lib/oj/mimic.rb, line 4 def self.mimic_loaded(mimic_paths=[]) gems_dir = File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))) Dir.foreach(gems_dir) do |gem| next unless (gem.start_with?('json-') || gem.start_with?('json_pure')) $LOADED_FEATURES << File.join(gems_dir, gem, 'lib', 'json.rb') end mimic_paths.each { |p| $LOADED_FEATURES << p } end
Parses an IO stream or file containing an JSON document. Raises an exception if the JSON is malformed. @param [Oj::Saj] handler SAJ (responds to Oj::Saj methods) like handler @param [IO|String] io IO Object to read from
static VALUE
saj_parse(int argc, VALUE *argv, VALUE self) {
struct _Options copts = oj_default_options;
char *json;
size_t len;
VALUE input = argv[1];
if (argc < 2) {
rb_raise(rb_eArgError, "Wrong number of arguments to saj_parse.\n");
}
if (rb_type(input) == T_STRING) {
// the json string gets modified so make a copy of it
len = RSTRING_LEN(input) + 1;
if (copts.max_stack < len) {
json = ALLOC_N(char, len);
} else {
json = ALLOCA_N(char, len);
}
strcpy(json, StringValuePtr(input));
} else {
VALUE clas = rb_obj_class(input);
VALUE s;
if (oj_stringio_class == clas) {
s = rb_funcall2(input, oj_string_id, 0, 0);
len = RSTRING_LEN(s) + 1;
if (copts.max_stack < len) {
json = ALLOC_N(char, len);
} else {
json = ALLOCA_N(char, len);
}
strcpy(json, StringValuePtr(s));
#ifndef JRUBY_RUBY
#if !IS_WINDOWS
// JRuby gets confused with what is the real fileno.
} else if (rb_respond_to(input, oj_fileno_id) && Qnil != (s = rb_funcall(input, oj_fileno_id, 0))) {
int fd = FIX2INT(s);
ssize_t cnt;
len = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
if (copts.max_stack < len) {
json = ALLOC_N(char, len + 1);
} else {
json = ALLOCA_N(char, len + 1);
}
if (0 >= (cnt = read(fd, json, len)) || cnt != (ssize_t)len) {
rb_raise(rb_eIOError, "failed to read from IO Object.");
}
json[len] = '\0';
#endif
#endif
} else if (rb_respond_to(input, oj_read_id)) {
s = rb_funcall2(input, oj_read_id, 0, 0);
len = RSTRING_LEN(s) + 1;
if (copts.max_stack < len) {
json = ALLOC_N(char, len);
} else {
json = ALLOCA_N(char, len);
}
strcpy(json, StringValuePtr(s));
} else {
rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
}
}
oj_saj_parse(*argv, json);
if (copts.max_stack < len) {
xfree(json);
}
return Qnil;
}
Dumps an Object to the specified file. @param [String] file_path file path to write the JSON document to @param [Object] obj Object to serialize as an JSON document String @param [Hash] options formating options @param [Fixnum] :indent format expected @param [true|false] :circular allow circular references, default: false
static VALUE
to_file(int argc, VALUE *argv, VALUE self) {
struct _Options copts = oj_default_options;
if (3 == argc) {
parse_options(argv[2], &copts);
}
Check_Type(*argv, T_STRING);
oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
return Qnil;
}
Generated with the Darkfish Rdoc Generator 2.