ProtoBuf—— 序列化 & 反序列化过程
- 序列化 & 反序列化过程
- 1、序列化:toByteArray()
- 2、反序列化:parseFrom(byteArray)
序列化 & 反序列化过程
1、序列化:toByteArray()
序列化过程描述: 编码 & 数据存储两个过程
- 1、创建一个输出流
- 2、计算出序列化后的二进制流长度,分配该长度的空间,以备以后将每个字段填充到该空间
- 3、判断每个字段是否有设置值,有值才会进行编码
若optional 或 repeated 字段没有被设置字段值,那么该字段在序列化时的数据中是完全不存在的,即不进行序列化(少编码一个字段);在解码时,相应的字段才会被设置为默认值
- 4、根据 字段标识号&数据类型 将 字段值 通过不同的编码方式进行编码
- 5、将已经编码成功的字节写入到 输出流,即数据存储,最终等待输出
相关部分源码:
// 仅贴出关键代码
public static final class Person extends
com.google.protobuf.GeneratedMessage implements PersonOrBuilder {
// 直接进行序列化成二进制字节流
public byte[] toByteArray() {
try {
final byte[] result = new byte[getSerializedSize()];
// 创建一个字节数组
final CodedOutputStream output = CodedOutputStream.newInstance(result);
// 创建一个输出流
writeTo(output);
// 将 消息类对象 序列化到输出流里 -->关注1
}
<-- writeTo()分析-->
// 以下便是真正序列化的过程
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
// 计算出序列化后的二进制流长度,分配该长度的空间,以备以后将每个字段填充到该空间
getSerializedSize();
// 判断每个字段是否有设置值
// 有才会进行写操作(编码)
if (((bitField0_ & 0x00000001) == 0x00000001)) {
output.writeBytes(1, getNameBytes()); // ->>关注2
// 根据 字段标识号 将已经赋值的 字段标识号和字段值 通过不同的编码方式进行编码
// 最终写入到输出流
}
if (((bitField0_ & 0x00000002) == 0x00000002)) {
output.writeInt32(2, id_); // ->>关注3
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
output.writeBytes(3, getEmailBytes());
}
// NumberPhone为嵌套的message
// 通过递归进行里面每个字段的序列化
for (int i = 0; i < phone_.size(); i++) {
output.writeMessage(4, phone_.get(i)); // ->>关注4
}
getUnknownFields().writeTo(output);
}
<-- 关注2:String字段类型的编码方式:LENGTH_DELIMITED-->
public void writeBytes(final int fieldNumber, final ByteString value)
throws IOException {
writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
// 将字段的标识号和字段类型按照Tag = (field_number << 3) | wire_type组成Tag
writeBytesNoTag(value);
// 对字段值进行编码
}
public void writeBytesNoTag(final ByteString value) throws IOException {
writeRawVarint32(value.size());
// 对于String字段类型的字段长度采用了Varint的编码方式
writeRawBytes(value);
// 对于String字段类型的字段值采用了utf-8的编码方式
}
<-- 关注3:Int32字段类型的编码方式:VARINT-->
public void writeInt32(final int fieldNumber, final int value)
throws IOException {
writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
// 将字段的标识号和字段类型按照Tag = (field_number << 3) | wire_type组成Tag
writeInt32NoTag(value);
// 对字段值采用了Varint的编码方式
}
<-- 关注4:Message字段类型的编码方式:LENGTH_DELIMITED-->
public void writeMessage(final int fieldNumber, final MessageLite value)
throws IOException {
writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
writeMessageNoTag(value);
}
}
2、反序列化:parseFrom(byteArray)
反序列化的过程总结如下:
- 1、从 输入流 依次读 字段的标签值(即Tag值)
- 2、根据从标签值(即
Tag
值)值解析出来的标识号(Field_Number
),判断对应的数据类型(wire_type
) - 3、调用对应的解码方法 解析 对应字段值
部分源码如下:
// 仅贴出关键代码
public static final class Person extends
com.google.protobuf.GeneratedMessage implements PersonOrBuilder {
public static com.carson.proto.Demo.Person parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data); // ->>关注1
}
<-- 关注1-->
public MessageType parseFrom(byte[] data)
throws InvalidProtocolBufferException {
return parseFrom(data, EMPTY_REGISTRY);// ->>关注2
}
<-- 关注2-->
public MessageType parseFrom(byte[] data,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return parseFrom(data, 0, data.length, extensionRegistry); // ->>关注3
}
<-- 关注3-->
public MessageType parseFrom(byte[] data, int off, int len,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return checkMessageInitialized(
parsePartialFrom(data, off, len, extensionRegistry));// ->>关注4
}
<-- 关注4 -->
public MessageType parsePartialFrom(byte[] data, int off, int len,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
CodedInputStream input = CodedInputStream.newInstance(data, off, len);
// 创建一个输入流
MessageType message = parsePartialFrom(input, extensionRegistry);
// ->>关注5
try {
input.checkLastTagWas(0);
return message;
}
}
<-- 关注5 -->
public Person parsePartialFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return new Person(input, extensionRegistry);
// 最终创建并返回了一个消息类Person的对象
// 创建时会调用Person 带有该两个参数的构造方法 ->>关注6
}
};
<-- 关注6:调用的构造方法 -->
// 作用:反序列化消息
private Person(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
initFields();
int mutable_bitField0_ = 0;
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
com.google.protobuf.UnknownFieldSet.newBuilder();
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
// 通过While循环 从 输入流 依次读tag值
// 根据从tag值解析出来的标识号,通过case分支读取对应字段类型的数据并通过反编码对字段进行解析 & 赋值
// 字段越多,case分支越多
switch (tag) {
case 0:
done = true;
break;
default: {
if (!parseUnknownField(input, unknownFields,
extensionRegistry, tag)) {
done = true;
}
break;
}
case 10: {
com.google.protobuf.ByteString bs = input.readBytes();
bitField0_ |= 0x00000001;
name_ = bs;
break;
}
case 16: {
bitField0_ |= 0x00000002;
id_ = input.readInt32();
break;
}
case 26: {
com.google.protobuf.ByteString bs = input.readBytes();
bitField0_ |= 0x00000004;
email_ = bs;
break;
}
case 34: {
if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) {
phone_ = new java.util.ArrayList<com.carson.proto.Demo.Person.PhoneNumber>();
mutable_bitField0_ |= 0x00000008;
}
phone_.add(input.readMessage(com.carson.proto.Demo.Person.PhoneNumber.PARSER, extensionRegistry));
break;
}
}
}
// 最终是返回了一个 消息类 对象