ProtoBuf的介紹以及在Java中使用protobuf將對象進行序列化與反序列化


場景

ProtoBuf簡介

protocol buffers 是一種語言無關、平台無關、可擴展的序列化結構數據的方法,它可用於(數據)通信協議、數據存儲等。

Protocol Buffers 是一種靈活,高效,自動化機制的結構數據序列化方法-可類比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更為簡單。

你可以定義數據的結構,然后使用特殊生成的源代碼輕松的在各種數據流中使用各種語言進行編寫和讀取結構數據。你甚至可以更新數據結構,而不破壞由舊數據結構編譯的已部署程序。

ProtocolBuffer擁有多項比XML更高級的串行化結構數據的特性,ProtocolBuffer:

·        更簡單

·        小3-10倍

·        快20-100倍

·        更少的歧義

·        可以方便的生成數據存取類

怎樣使用ProtoBuf

定義proto描述文件

首先需要在一個 .proto 文件中定義你需要做串行化的數據結構信息。每個ProtocolBuffer信息是一小段邏輯記錄,包含一系列的鍵值對。這里有個非常簡單的 .proto 文件定義了個人信息:

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 phone=4;
}

當然這里的protobuf的秒數文件.proto也是有版本語法限制的,比如protoBuf3中沒有required和optional。

每個消息類型擁有一個或多個特定的數字字段,每個字段擁有一個名字和一個值類型。值類型可以是數字(整數或浮點)、布 爾型、字符串、原始字節或者其他ProtocolBuffer類型,還允許數據結構的分級。

上面的message就類似於類,下面是屬性。

required在protobuf2代表是必填的,optional在protobuf2中代表是可選的。repeated代表重復字段,類似於list。

下面的enum代表是枚舉。

下面的message是在上面Person的里面,代表內部類。default代表默認值。

需要注意的是這里的=1,=2等都不是對其進行賦值,而是對屬性做一個唯一標志。

編譯描述文件

一旦你定義了自己的報文格式(message),你就可以運行ProtocolBuffer編譯器,將你的 .proto 文件編譯成特定語言的類。這些類提供了簡單的方法訪問每個字段(像是 query() 和set_query()),像是訪問類的方法一樣將結構串行化或反串行化。

編譯器下載地址:

https://github.com/protocolbuffers/protobuf/releases

選擇指定的操作系統的指定protobuf版本的protoc編譯器進行下載

 

 

這里操作系統是Windows64位並且在下面要使用的protobuf的版本是3.13

所以這里下載對應的版本

 

 

將其下載到電腦上某路徑下解壓

后續就可以使用protoc.exe對proto文件進行編譯。

為了使用方便,這里將protoc.exe添加進環境變量

 

 

然后打開cmd,輸入protoc后回車,會輸出編譯的提示命令則是成功

 

 

 

Java中使用ProtoBuf序列化和反序列化示例

新建項目並引入依賴

打開IDEA-新建一個gradle項目,當然你也可以新建一個Maven項目。

這里以使用gradle作為依賴管理進行示例。

新建gradle項目后選擇Java語言,然后添加protobuf相關的依賴。

來到Maven的中央倉庫

https://mvnrepository.com/

搜索protobuf

 

 

這兩個就是需要引入的依賴。

分別進入兩個依賴中選擇跟上面的編譯的版本一致的版本

 

 

這里是Gradle的依賴,你也可以使用Maven並復制Maven的依賴。

 

 

找到IDEA中新建的gradle項目的build.gradle將兩個依賴添加進去。

dependencies {
    compile (
            [group:'com.google.protobuf', name: 'protobuf-java', version: '3.13.0'],
            [group:'com.google.protobuf', name: 'protobuf-java-util', version: '3.13.0']
    )
}

因為設置了自動導入,所以會在項目中引入Google的相關protobuf的依賴的jar包

 

 

編寫proto描述文件

然后在src下新建protobuf目錄,在此目錄下新建文件Student.proto

 

 

此時IDEA會提示,選擇Text即可。

syntax = "proto3";

package com.badao.protobuf;

option optimize_for =SPEED;
option java_package = "com.badao.protobuf";
option java_outer_classname = "DataInfo";

message Student {
    string name = 1;
    int32 age = 2;
    string address = 3;
}

然后將其內容修改如下

這里的syntax代表使用的語法規則是protobuf3

然后下面的package是缺省時的包名,如果下面沒有設置java_package則會使用此設置,如果設置了則不會使用。

下面的是一些配置

optimize_for是文件級別的選項,Protocol Buffer定義三種優化級別SPEED/CODE_SIZE/LITE_RUNTIME。缺省情況下是SPEED。

SPEED: 表示生成的代碼運行效率高,但是由此生成的代碼編譯后會占用更多的空間。

然后java_package就是配置的生成代碼的包名,

java_outer_classname選項表明想要生成Java類的名稱。如果在.proto文件中沒有明確的java_outer_classname定義,生成的class名稱將會根據.proto文件的名稱采用駝峰式的命名方式進行生成。如(foo_bar.proto生成的java類名為FooBar.java)。

這里配置的類名就叫DataInfo。

編譯描述文件生成代碼

在IDEA中下面的Terminal新建一個終端

 

 

因為已經將protoc編譯命令的exe添加進環境變量,所以這里直接使用protoc命令即可。

protoc --java_out=src/main/java src/protobuf/Student.proto

這里--java_out=src/main/java就是設置的要生成代碼后存放的位置

后面跟着個空格然后跟的是 proto描述文件的位置

回車后如果沒有報錯,並且已經在src/main/java/com/badao/protobuf下生成DataInfo類

 

 

則編譯成功。

對象數據的序列化與反序列化

在此包下新建一個ProtobuTest類,並編寫main方法

package com.badao.protobuf;

import com.google.protobuf.InvalidProtocolBufferException;

public class ProtobufTest {
    public static void main(String[] args) throws InvalidProtocolBufferException {
        DataInfo.Student student = DataInfo.Student.newBuilder()
                .setName("公眾號:霸道的程序猿").setAge(100).setAddress("中國").build();
        byte[] bytes = student.toByteArray();

        DataInfo.Student student1 = DataInfo.Student.parseFrom(bytes);
        System.out.println(student1.getName());
        System.out.println(student1.getAge());
        System.out.println(student1.getAddress());
    }
}

注意這里的新建對象並對屬性進行賦值時必須采用如上這種方式

        DataInfo.Student student = DataInfo.Student.newBuilder()
                .setName("公眾號:霸道的程序猿").setAge(100).setAddress("中國").build();

然后將對象進行序列化為字節數組就可以通過

byte[] bytes = student.toByteArray();

將字節數據反序列化為對象就可以通過

DataInfo.Student student1 = DataInfo.Student.parseFrom(bytes);

然后運行該main方法。

 

 

示例代碼下載

https://download.csdn.net/download/BADAO_LIUMANG_QIZHI/12858952

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM