Protocol Buffer是谷歌开源的一种序列化和反序列化机制,类似于XML,JSON 解析,但是Protocol Buffer 更灵活、更高效、更简单。protocol buffer 是 一种数据交换的格式,它独立于语言,独立于平台。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。
Protocol Buffer下载地址:https://github.com/google/protobuf/tree/master/java
本文以protobuf-3.1.x为列.本例从官网下载下来的pb:http://download.youkuaiyun.com/detail/yzr_java/9853141
ProtoBuf的官方下载包并不包含jar文件,需要用户自己自行编译(本文跳过Maven的安装环境和配置),编译需要先下载protoc.exe文件,根据protocolbuffer版本在官网下载,
Protoc.exe执行程序下载地址:http://central.maven.org/maven2/com/google/protobuf/protoc/
将下载下来的protoc文件比如:protoc-3.1.0-windows-x86_64.exe 重命名为protoc.exe放到protobuf-3.1.x/src文件夹下.
使用cmd进入G:\Tools\JAVA\protocolbuffer\protobuf-3.1.x\src目录下,如果不需要验证编译结果(不执行单元测试),则可以如下:执行mvn install –Dmaven.test.skip=true.
若按官网的话则过程如下:
执行之后在G:\Tools\JAVA\protocolbuffer\protobuf-3.1.x\java\core\target目录下会自动生成protobuf-java-3.1.0.jar
使用protocolbuffer实现序列化和反序列
1.首先需要先创建一个后缀为.proto的文件,本列为eboy.proto
内容如下:
syntax = "proto2";
option java_package = "com.proto";
option java_outer_classname = "UserInfoModule";
message UserInfo{
required int64 userId = 1;
required string UserName = 2;
repeated Dog dogList = 3;
}
message Dog{
required int64 dogId = 1;
required string dogName = 2;
}
关于syntax为proto2和proto3的区别,可以查看 http://www.cppblog.com/tx7do/archive/2016/11/17/214414.html
protocolbuffer的数据类型,proto文件的语法可以参考:http://blog.youkuaiyun.com/sylar_d/article/details/51325987
上例中的内容是创建了两个对象,一个为UserInfo,另外一个是Dog,两个对象之间的关联是UserInfo有一个List<Dog>属性.
2.使用protoc.exe生成java文件
为了方便,我们在项目中创建一个proto文件夹,存放刚才创建的eboy.proto文件.然后创建一个bat批处理文件,内容为
build.bat:
protoc ./proto/eboy.proto --java_out=./src
pause
将生成的java文件放入到项目中的src中.执行之后,刷新一下项目,就会看到自动生成的UserInfoModule.java文件
3.使用protocolbuffer序列化和反序列化
package com.proto;
import java.util.Arrays;
import com.proto.UserInfoModule.Dog;
import com.proto.UserInfoModule.UserInfo;
import com.proto.UserInfoModule.UserInfo.Builder;
public class PbToBytes {
public static void main(String[] args) throws Exception {
byte[] bytes = toBytes();
toUserInfo(bytes);
}
/**
* 序列化
*/
public static byte[] toBytes(){
com.proto.UserInfoModule.Dog.Builder dogBuilder = UserInfoModule.Dog.newBuilder();
dogBuilder.setDogId(1).setDogName("Wangwang");
Dog dog=dogBuilder.build();
//获取一个PBPlayer的构造器
Builder builder = UserInfoModule.UserInfo.newBuilder();
//设置数据
builder.addDogList(dog).setUserId(2011344223).setUserName("YZR");
//builder.addDogList(UserInfoModule.Dog.newBuilder().setDogId(1).setDogName("Wangwang")).setUserId(2011344223).setUserName("YZR");
//构造出对象
UserInfo user = builder.build();
//序列化成字节数组
byte[] byteArray = user.toByteArray();
System.out.println(Arrays.toString(byteArray));
return byteArray;
}
/**
* 反序列化
* @param bs
* @throws Exception
*/
public static void toUserInfo(byte[] bs) throws Exception{
UserInfo user = UserInfoModule.UserInfo.parseFrom(bs);
System.out.println("userId:" + user.getUserId());
System.out.println("userName:" + user.getUserName());
System.out.println("Dog:" + (Arrays.toString(user.getDogListList().toArray())));
}
}
在控制台中打印的信息为:
[8, -33, -38, -118, -65, 7, 18, 3, 89, 90, 82, 26, 12, 8, 1, 18, 8, 87, 97, 110, 103, 119, 97, 110, 103]
userId:2011344223
userName:YZR
Dog:[dogId: 1
dogName: "Wangwang"
]
我们能看到protcolbuffer将UserInfo对象序列化成一个字节数据类型的对象,然后又能通过这个字节数组序列化成对象.
其实我们也可以使用java API序列化对象成一个字节类型数组.实现的代码如下:
package com.java;
import java.io.Serializable;
public class Dog implements Serializable {
/**
*
*/
private static final long serialVersionUID = 8047025244053232753L;
private int dogId;
private String dogName;
public int getDogId() {
return dogId;
}
public void setDogId(int dogId) {
this.dogId = dogId;
}
public String getDogName() {
return dogName;
}
public void setDogName(String dogName) {
this.dogName = dogName;
}
public Dog(int id,String name){
this.dogId=id;
this.dogName=name;
}
@Override
public String toString() {
return "Dog [dogId=" + dogId + ", dogName=" + dogName + "]";
}
}
package com.java;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class UserInfo implements Serializable {
/**
*
*/
private static final long serialVersionUID = -1586269696229279818L;
private int userId;
private String userName;
private List<Dog> dogList=new ArrayList<>();
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public List<Dog> getDogList() {
return dogList;
}
public void setDogList(List<Dog> dogList) {
this.dogList = dogList;
}
public UserInfo(int id,String name){
this.userId=id;
this.userName=name;
}
}
package com.java;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
public class JAVA2Bytes {
public static void main(String[] args) throws Exception {
byte[] bytes = toBytes();
toPlayer(bytes);
}
/**
* 序列化
* @throws IOException
*/
public static byte[] toBytes() throws IOException{
UserInfo user=new UserInfo(2017,"YZR");
user.getDogList().add(new Dog(1,"WangWang"));
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
//写入对象
objectOutputStream.writeObject(user);
//获取 字节数组
byte[] byteArray = byteArrayOutputStream.toByteArray();
System.out.println(Arrays.toString(byteArray));
return byteArray;
}
/**
* 反序列化
* @param bs
* @throws Exception
*/
public static void toPlayer(byte[] bs) throws Exception{
ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(bs));
UserInfo user = (UserInfo)inputStream.readObject();
//打印
System.out.println("userId:" + user.getUserId());
System.out.println("username:" + user.getUserName());
System.out.println("dogs:" + (Arrays.toString(user.getDogList().toArray())));
}
}
在控制台中打印的信息如下:
[-84, -19, 0, 5, 115, 114, 0, 17, 99, 111, 109, 46, 106, 97, 118, 97, 46, 85, 115, 101, 114, 73, 110, 102, 111, -23, -4, 112, 29, -98, 73, 119, -74, 2, 0, 3, 73, 0, 6, 117, 115, 101, 114, 73, 100, 76, 0, 7, 100, 111, 103, 76, 105, 115, 116, 116, 0, 16, 76, 106, 97, 118, 97, 47, 117, 116, 105, 108, 47, 76, 105, 115, 116, 59, 76, 0, 8, 117, 115, 101, 114, 78, 97, 109, 101, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 120, 112, 0, 0, 7, -31, 115, 114, 0, 19, 106, 97, 118, 97, 46, 117, 116, 105, 108, 46, 65, 114, 114, 97, 121, 76, 105, 115, 116, 120, -127, -46, 29, -103, -57, 97, -99, 3, 0, 1, 73, 0, 4, 115, 105, 122, 101, 120, 112, 0, 0, 0, 1, 119, 4, 0, 0, 0, 1, 115, 114, 0, 12, 99, 111, 109, 46, 106, 97, 118, 97, 46, 68, 111, 103, 111, -84, -58, -45, 18, 100, -36, 113, 2, 0, 2, 73, 0, 5, 100, 111, 103, 73, 100, 76, 0, 7, 100, 111, 103, 78, 97, 109, 101, 113, 0, 126, 0, 2, 120, 112, 0, 0, 0, 1, 116, 0, 8, 87, 97, 110, 103, 87, 97, 110, 103, 120, 116, 0, 3, 89, 90, 82]
userId:2017
username:YZR
dogs:[Dog [dogId=1, dogName=WangWang]]
和上面使用PB序列化的结果相对比来说,本次序列化成的字节数组的大小相差将近10倍.
整个项目如下:
代码下载: