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;
            }
          }
        }

// 最终是返回了一个 消息类 对象