Class: Yajl::Encoder

Inherits:
Object show all
Defined in:
ext/yajl/yajl_ext.c,
lib/yajl.rb,
ext/yajl/yajl_ext.c

Overview

This class contains methods for encoding a Ruby object into JSON, streaming it's output into an IO object. The IO object need only respond to #write(str) The JSON stream created is written to the IO in chunks, as it's being created.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeObject

call-seq: initialize([:pretty => false[, :indent => ' '][, :terminator => “n”]])

:pretty will enable/disable beautifying or “pretty priting” the output string.

:indent is the character(s) used to indent the output string.

:terminator allows you to specify a character to be used as the termination character after a full JSON string has been generated by the encoder. This would be especially useful when encoding in chunks (via a block or callback during the encode process), to be able to determine when the last chunk of the current encode is sent. If you specify this option to be nil, it will be ignored if encoding directly to an IO or simply returning a string. But if a block is used, the encoder will still pass it - I hope that makes sense ;).



654
655
656
# File 'ext/yajl/yajl_ext.c', line 654

static VALUE rb_yajl_encoder_init(int argc, VALUE * argv, VALUE self) {
    return self;
}

Class Method Details

.enable_json_gem_compatabilityObject

call-seq: enable_json_gem_compatability

Enables the JSON gem compatibility API



878
879
880
881
882
883
884
885
886
887
888
# File 'ext/yajl/yajl_ext.c', line 878

static VALUE rb_yajl_encoder_enable_json_gem_ext(VALUE klass) {
    rb_define_method(rb_cHash, "to_json", rb_yajl_json_ext_hash_to_json, -1);
    rb_define_method(rb_cArray, "to_json", rb_yajl_json_ext_array_to_json, -1);
    rb_define_method(rb_cFixnum, "to_json", rb_yajl_json_ext_fixnum_to_json, -1);
    rb_define_method(rb_cFloat, "to_json", rb_yajl_json_ext_float_to_json, -1);
    rb_define_method(rb_cString, "to_json", rb_yajl_json_ext_string_to_json, -1);
    rb_define_method(rb_cTrueClass, "to_json", rb_yajl_json_ext_true_to_json, -1);
    rb_define_method(rb_cFalseClass, "to_json", rb_yajl_json_ext_false_to_json, -1);
    rb_define_method(rb_cNilClass, "to_json", rb_yajl_json_ext_nil_to_json, -1);
    return Qnil;
}

.encode(obj, *args, &block) ⇒ Object

A helper method for encode-and-forget use-cases

Examples:

Yajl::Encoder.encode(obj[, io, :pretty => true, :indent => "\t", &block])

output = Yajl::Encoder.encode(obj[, :pretty => true, :indent => "\t", &block])

obj is a ruby object to encode to JSON format

io is the optional IO stream to encode the ruby object to. If io isn't passed, the resulting JSON string is returned. If io is passed, nil is returned.

The options hash allows you to set two encoding options - :pretty and :indent

:pretty accepts a boolean and will enable/disable “pretty printing” the resulting output

:indent accepts a string and will be used as the indent character(s) during the pretty print process

If a block is passed, it will be used as (and work the same as) the on_progress callback



61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/yajl.rb', line 61

def self.encode(obj, *args, &block)
  # TODO: this code smells, any ideas?
  args.flatten!
  options = {}
  io = nil
  args.each do |arg|
    if arg.is_a?(Hash)
      options = arg
    elsif arg.respond_to?(:write)
      io = arg
    end
  end if args.any?
  new(options).encode(obj, io, &block)
end

.newObject

call-seq: initialize([:pretty => false[, :indent => ' '][, :terminator => “n”]])

:pretty will enable/disable beautifying or “pretty priting” the output string.

:indent is the character(s) used to indent the output string.

:terminator allows you to specify a character to be used as the termination character after a full JSON string has been generated by the encoder. This would be especially useful when encoding in chunks (via a block or callback during the encode process), to be able to determine when the last chunk of the current encode is sent. If you specify this option to be nil, it will be ignored if encoding directly to an IO or simply returning a string. But if a block is used, the encoder will still pass it - I hope that makes sense ;).



587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
# File 'ext/yajl/yajl_ext.c', line 587

static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) {
    yajl_encoder_wrapper * wrapper;
    yajl_gen_config cfg;
    VALUE opts, obj, indent;
    unsigned char *indentString = NULL, *actualIndent = NULL;
    int beautify = 0, htmlSafe = 0;

    /* Scan off config vars */
    if (rb_scan_args(argc, argv, "01", &opts) == 1) {
        Check_Type(opts, T_HASH);

        if (rb_hash_aref(opts, sym_pretty) == Qtrue) {
            beautify = 1;
            indent = rb_hash_aref(opts, sym_indent);
            if (indent != Qnil) {
#ifdef HAVE_RUBY_ENCODING_H
                indent = rb_str_export_to_enc(indent, utf8Encoding);
#endif
                Check_Type(indent, T_STRING);
                indentString = (unsigned char*)xmalloc(RSTRING_LEN(indent)+1);
                memcpy(indentString, RSTRING_PTR(indent), RSTRING_LEN(indent));
                indentString[RSTRING_LEN(indent)] = '\0';
                actualIndent = indentString;
            }
        }
        if (rb_hash_aref(opts, sym_html_safe) == Qtrue) {
          htmlSafe = 1;
        }
    }
    if (!indentString) {
      indentString = defaultIndentString;
    }
    cfg = (yajl_gen_config){beautify, (const char *)indentString, htmlSafe};

    obj = Data_Make_Struct(klass, yajl_encoder_wrapper, yajl_encoder_wrapper_mark, yajl_encoder_wrapper_free, wrapper);
    wrapper->indentString = actualIndent;
    wrapper->encoder = yajl_gen_alloc(&cfg, NULL);
    wrapper->on_progress_callback = Qnil;
    if (opts != Qnil && rb_funcall(opts, intern_has_key, 1, sym_terminator) == Qtrue) {
        wrapper->terminator = rb_hash_aref(opts, sym_terminator);
#ifdef HAVE_RUBY_ENCODING_H
        if (TYPE(wrapper->terminator) == T_STRING) {
            wrapper->terminator = rb_str_export_to_enc(wrapper->terminator, utf8Encoding);
        }
#endif
    } else {
        wrapper->terminator = 0;
    }
    rb_obj_call_init(obj, 0, 0);
    return obj;
}

Instance Method Details

#encodeObject

call-seq: encode(obj[, io[, &block]])

obj is the Ruby object to encode to JSON

io is an optional IO used to stream the encoded JSON string to. If io isn't specified, this method will return the resulting JSON string. If io is specified, this method returns nil

If an optional block is passed, it's called when encoding is complete and passed the resulting JSON string

It should be noted that you can reuse an instance of this class to continue encoding multiple JSON to the same stream. Just continue calling this method, passing it the same IO object with new/different ruby objects to encode. This is how streaming is accomplished.



674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
# File 'ext/yajl/yajl_ext.c', line 674

static VALUE rb_yajl_encoder_encode(int argc, VALUE * argv, VALUE self) {
    yajl_encoder_wrapper * wrapper;
    const unsigned char * buffer;
    unsigned int len;
    VALUE obj, io, blk, outBuff;

    GetEncoder(self, wrapper);

    rb_scan_args(argc, argv, "11&", &obj, &io, &blk);

    if (blk != Qnil) {
        wrapper->on_progress_callback = blk;
    }

    /* begin encode process */
    yajl_encode_part(wrapper, obj, io);

    /* just make sure we output the remaining buffer */
    yajl_gen_get_buf(wrapper->encoder, &buffer, &len);
    outBuff = rb_str_new((const char *)buffer, len);
#ifdef HAVE_RUBY_ENCODING_H
    rb_enc_associate(outBuff, utf8Encoding);
#endif
    yajl_gen_clear(wrapper->encoder);

    if (io != Qnil) {
        rb_io_write(io, outBuff);
        if (wrapper->terminator != 0 && wrapper->terminator != Qnil) {
            rb_io_write(io, wrapper->terminator);
        }
        return Qnil;
    } else if (blk != Qnil) {
        rb_funcall(blk, intern_call, 1, outBuff);
        if (wrapper->terminator != 0) {
            rb_funcall(blk, intern_call, 1, wrapper->terminator);
        }
        return Qnil;
    } else {
        if (wrapper->terminator != 0 && wrapper->terminator != Qnil) {
            rb_str_concat(outBuff, wrapper->terminator);
        }
        return outBuff;
    }
    return Qnil;
}

#on_progress=Object

call-seq: on_progress = Proc.new {|str| …}

This callback setter allows you to pass a Proc/lambda or any other object that responds to #call.

It will pass the caller a chunk of the encode buffer after it's reached it's internal max buffer size (defaults to 8kb). For example, encoding a large object that would normally result in 24288 bytes of data will result in 3 calls to this callback (assuming the 8kb default encode buffer).



730
731
732
733
734
735
# File 'ext/yajl/yajl_ext.c', line 730

static VALUE rb_yajl_encoder_set_progress_cb(VALUE self, VALUE callback) {
    yajl_encoder_wrapper * wrapper;
    GetEncoder(self, wrapper);
    wrapper->on_progress_callback = callback;
    return Qnil;
}