MessagePack for JavaをJRubyで使う

 こんにちは。那由多屋の加藤です。

 データシリアライズ形式である「MessagePack」をGAE/JRubyで使いたいのですが、Pure Rubyな実装は存在しないようです。

 そこでMessagePack for Javaを使うことを試みたのですが・・・疲れました。

 JavaRubyとのデータ変換が思ったよりも大変そうです。とりあえずスキーマを指定すれば、まあまあ動作するようにはなりました。コードはgistに上げておきます。

http://gist.github.com/319395

 誰か続きを作ってくれないかなー。

msgpack-for-java.rb

module MessagePack
  def self.pack(obj, schema = nil)
    ostream = java.io.ByteArrayOutputStream.new
    packer  = org.msgpack.Packer.new(ostream)
    value   = self.ruby_to_java(obj)

    if schema
      packer.packWithSchema(value, org.msgpack.Schema.parse(schema))
    else
      packer.pack(value)
    end

    bytes = ostream.toByteArray

    return bytes.to_a.pack("c*")
  end

  def self.unpack(binary, schema = nil)
    bytes    = binary.unpack("c*").to_java(:byte)
    istream  = java.io.ByteArrayInputStream.new(bytes)
    unpacker = org.msgpack.Unpacker.new(istream)
    unpacker.useSchema(org.msgpack.Schema.parse(schema)) if schema
    iterator = unpacker.iterator
    value    = iterator.next
    return self.java_to_ruby(value)
  end

  def self.ruby_to_java(obj)
    case obj
    when Fixnum then return java.lang.Long.new(obj)
    when Array
      return java.util.ArrayList.new(obj.map { |item|
        self.ruby_to_java(item)
      })
    when Hash
      return obj.inject({}) { |memo, (key, value)|
        memo[self.ruby_to_java(key)] = self.ruby_to_java(value)
        memo
      }
    else return obj
    end
  end

  def self.java_to_ruby(value)
    if value.java_kind_of?(java.util.HashMap)
      return Hash[value.map { |key, value|
        [self.java_to_ruby(key), self.java_to_ruby(value)]
      }]
    else
      if value.respond_to?(:getClass)
        case value.getClass.getName
        when "java.util.Arrays$ArrayList" then return value.to_a
        else return value
        end
      else
        return value
      end
    end
  end
end