Google Protobuf编解码(序列化/反序列化)框架

最近工作中使用到protobuffer,查了很多资料,这篇有用,转发收藏,留着以后查阅
本文转自:https://my.oschina.net/pierrecai/blog/873359

编码/解码在Java中又称序列化/反序列化,Java本身的序列化反序列化技术生成的二进制码流太大,且转化效率低下,一般不适用于远程跨节点调用的编码框架。

Protobuf全称Protocol Buffers,由谷歌开源而来,特点如下:

  1. 码流小、效率高
  2. 语言平台无关,不只Java可以用,C++、python亦可
  3. 使用数据描述文件,可自动生成代码
  4. 和Facebook Thirft对比,Protobuf并不需要完全在开始就完全定义好全部结构,而可以在后期extends之前的结构,并且仍然可以读取之前结构编码的信息

使用步骤:

  1. 编写.proto类定义文件
  2. 通过提供的编译器产生对应的Java类
  3. 使用提供的API读写信息

1、编写.proto类定义文件

package tutorial;

option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}


<h3>文件说明:</h3> 
<ol> 
 <li>package用于区分同名文件,在java_package缺省时,就是产生的bean的包</li> 
  • java_outer_classname指定包含文件中定义的所有类的类,如果缺省,会自动将.proto文件名设为类名
  • message定义一个类,message中可以包含message
  • 每个属性后面的 =1、=2等数字是在二进制化数据时,属性的“唯一标识”,在每个message中独立编号
  • 支持的修饰符:

    1. required:标识必须被提供的字段,如果required字段的值没有被提供,编码解码时都会跑出异常;
    2. optional:标识可以提供或不提供的字段,如果不提供,则返回默认值。默认值和java中相似,但是“子message”的字段会全部为空。默认值也可以用[default = value]进行设置;
    3. repeated:标识ArrayList
    4. 每个属性后面的 =1、=2等数字,注意0-15比之后的数字再序列化时少一个字节,固0-15一般用在required和repeated字段上

    支持的数据类型:boolint32floatdouble, stringenum 

    .proto TypeNotesC++ TypeJava TypePython TypeGo Type
    double doubledoublefloat*float64
    float floatfloatfloat*float32
    int32虽然可以有负数,但是效率不高int32intint*int32
    int64虽然可以有负数,但是效率不高int64longint/long[3]*int64
    uint32无符号整数,只能是正数uint32intint/long[3]*uint32
    uint64无符号整数,只能是正数uint64longint/long[3]*uint64
    sint32有符号整数,表示负数的效率更高int32intint*int32
    sint64有符号整数,表示负数的效率更高int64longint/long[3]*int64
    fixed32永远占据4个字节,如果超过256的话,效率更高uint32intint*uint32
    fixed64永远占据8个字节,如果超过256的话,效率更高uint64longint/long[3]*uint64
    sfixed32永远占据4个字节int32intint*int32
    sfixed64永远占据8个字节int64longint/long[3]*int64
    bool boolbooleanbool*bool
    string字符串必须是UTF-8或者7-bit ASCII。stringStringstr/unicode*string
    bytes stringByteStringstr[]byte

    备注:

    [3] Python3中不再有int和long之分,而只有不限长度的整型int。

    2、通过提供的编译器产生对应的Java类

    2.1、将protoc.exe和对应的.proto文件放到项目所在的java文件夹下

    protoc.exe --java_out=.\ .\Request.proto

    就可以在上面的proto文件中的java_package描述的位置产生指定的文件

    3、使用提供的API读写信息

    public class TestSubscribeReqProto {
        /**
         * 1、构建对象
         * @return
         */
        public static SubscribeReqProto.SubscribeReq createSubscribeReq(){
            SubscribeReqProto.SubscribeReq.Builder builder =
                    SubscribeReqProto.SubscribeReq.newBuilder();
            builder.setSubReqID(1);
            builder.setUserName("caizhijie");
            builder.setProductName("Yellow Book");
            List<String> address = new ArrayList<>();
            address.add("NanJing YuHuaTai");
            address.add("BeiJing ZhiJinCheng");
            address.add("ShenZhen HongShuLin");
            builder.addAllAddress(address);
            return builder.build();
        }
    
        /**
         * 2、编码
         * @param req
         * @return
         */
        public static byte[] encode(SubscribeReqProto.SubscribeReq req){
            return req.toByteArray();
        }
    
        /**
         * 3、解码
         * @param body
         * @return
         * @throws InvalidProtocolBufferException
         */
        public static SubscribeReqProto.SubscribeReq decode(byte[] body) throws InvalidProtocolBufferException {
            return SubscribeReqProto.SubscribeReq.parseFrom(body);
        }
    
        public static void main(String[] args) throws InvalidProtocolBufferException {
            SubscribeReqProto.SubscribeReq req = createSubscribeReq();
            System.out.println("Before encode : "+req.toString());
            SubscribeReqProto.SubscribeReq req2 = decode(encode(req));
            System.out.println("After encode : "+req2.toString());
            System.out.println("Assert equal : -->" + req2.equals(req));
        }
    }
    
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值