protobuf入门Word格式文档下载.docx

上传人:b****5 文档编号:19741520 上传时间:2023-01-09 格式:DOCX 页数:14 大小:161.23KB
下载 相关 举报
protobuf入门Word格式文档下载.docx_第1页
第1页 / 共14页
protobuf入门Word格式文档下载.docx_第2页
第2页 / 共14页
protobuf入门Word格式文档下载.docx_第3页
第3页 / 共14页
protobuf入门Word格式文档下载.docx_第4页
第4页 / 共14页
protobuf入门Word格式文档下载.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

protobuf入门Word格式文档下载.docx

《protobuf入门Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《protobuf入门Word格式文档下载.docx(14页珍藏版)》请在冰豆网上搜索。

protobuf入门Word格式文档下载.docx

ProtocolBuffers可以灵活,高效且自动化的解决该问题,只需要:

1.创建一个.proto文件,描述希望数据存储结构

2.使用PBcompiler创建一个类,该类可以高效的,以二进制方式自动编码和解析PB数据

该生成类提供组成PB数据字段的getter和setter方法,甚至考虑了如何高效的读写PB数据。

更厉害的是,PB友好的支持字段拓展,拓展后的代码,依然能够正确的读取原来格式编码的数据。

3.定义协议格式

首先需要创建一个.proto文件。

非常简单,每一个需要序列化的数据结构,编码一个PBmessage,然后为message中的字段指明一个名字和类型即可。

该“通讯簿”的.proto文件addressbook.proto定义如下:

packagetutorial;

optionjava_package="

com.example.tutorial"

;

optionjava_outer_classname="

AddressBookProtos"

messagePerson{

requiredstringname=1;

requiredint32id=2;

optionalstringemail=3;

enumPhoneType{

MOBILE=0;

HOME=1;

WORK=2;

}

messagePhoneNumber{

requiredstringnumber=1;

optionalPhoneTypetype=2[default=HOME];

repeatedPhoneNumberphone=4;

}

messageAddressBook{

repeatedPersonperson=1;

可以看到,语法非常类似Java或者C++,接下来,我们一条一条来过一遍每句话的含义:

∙.proto文件以一个package声明开始。

该声明有助于避免不同项目建设的命名冲突。

Java版的PB,在没有指明java_package的情况下,生成的类默认的package即为此package。

这里我们生命的java_package,所以最终生成的类会位于com.example.tutorialpackage下。

这里需要强调一下,即使指明了java_package,我们建议依旧定义.proto文件的package。

∙在package声明之后,紧接着是专门为java指定的两个选项:

java_package以及java_outer_classname。

java_package我们已经说过,不再赘述。

java_outer_classname为生成类的名字,该类包含了所有在.proto中定义的类。

如果该选项不显式指明的话,会按照驼峰规则,将.proto文件的名字作为该类名。

例如“addressbook.proto”将会是“Addressbook”,“address_book.proto”即为“AddressBook”

∙java指定选项后边,即为message定义。

每个message是一个包含了一系列指明了类型的字段的集合。

这里的字段类型包含大多数的标准简单数据类型,包括bool,int32,float,double以及string。

Message中也可以定义嵌套的message,例如“Person”message包含“PhoneNumber”message。

也可以将已定义的message作为新的数据类型,例如上例中,PhoneNumber类型在Person内部定义,但他是phone的type。

在需要一个字段包含预先定义的一个列表的时候,也可以定义枚举类型,例如“PhoneType”。

∙我们注意到,每一个message中的字段,都有“=1”,“=2”这样的标记,这可不是初始化赋值,该值是message中,该字段的唯一标示符,在二进制编码时候会用到。

数字1~15的表示需求少于一个字节,所以在编码的时候,有这样一个优化,你可以用1~15标记最常使用或者重复字段元素(repeatedelements)。

用16或者更大的数字来标记不太常用的可选元素。

再重复字段中,每一个元素都需重复编码标签数字,所以,该优化对重复字段最佳(repeatfileds)。

message的没一个字段,都要用如下的三个修饰符(modifier)来声明:

1.required:

必须赋值,不能为空,否则该条message会被认为是“uninitialized”。

build一个“uninitialized”message会抛出一个RuntimeException异常,解析一条“uninitialized”message会抛出一条IOException异常。

除此之外,“required”字段跟“optional”字段并无差别。

2.optional:

字段可以赋值,也可以不赋值。

假如没有赋值的话,会被赋上默认值。

对于简单类型,默认值可以自己设定,例如上例的PhoneNumber中的PhoneType字段。

如果没有自行设定,会被赋上一个系统默认值,数字类型会被赋为0,String类型会被赋为空字符串,bool类型会被赋为false。

对于内置的message,默认值为该message的默认实例或者原型,即其内所有字段均为设置。

当获取没有显式设置值的optional字段的值时,就会返回该字段的默认值。

3.repeated:

该字段可以重复任意次数,包括0次。

重复数据的顺序将会保存在protocolbuffer中,将这个字段想象成一个可以自动设置size的数组就可以了。

Notice:

应该格外小心定义Required字段。

当因为某原因要把Required字段改为Optional字段是,会有问题,老版本读取器会认为消息中没有该字段不完整,可能会拒绝或者丢弃该字段(Google文档是这么说的,但是我试了一下,将required的改为optional的,再用原来required时候的解析代码去读,如果字段赋值的话,并不会出错,但是如果字段未赋值,会报这样错误:

Exceptioninthread“main”com.google.protobuf.InvalidProtocolBufferException:

Messagemissingrequiredfields:

fieldname)。

在设计时,尽量将这种验证放在应用程序端的完成。

Google的一些工程师对此也很困惑,他们觉得,required类型坏处大于好处,应该尽量仅适用optional或者repeated的。

但也并不是所有的人都这么想。

如果想深入学习.proto文件书写,可以参考ProtocolBufferLanguageGuide。

但是不要妄想会有类似于类继承这样的机制,ProtocolBuffers不做这个…

4.编译ProtocolBuffers

定义好.proto文件后,接下来,就是使用该文件,运行PB的编译器protoc,编译.proto文件,生成相关类,可以使用这些类读写“通讯簿”没得message。

接下来我们要做:

1.如果你还没有安装PB编译器,到这里现在安装:

downloadthepackage

2.安装后,运行protoc,结束后会发现在项目com.example.tutorialpackage下,生成了AddressBookProtos.java文件:

protoc-I=$SRC_DIR--java_out=$DST_DIR$SRC_DIR/addressbook.proto

#forexample

protoc-I=G:

\workspace\protobuf\message--java_out=G:

\workspace\protobuf\src\main\javaG:

\workspace\protobuf\messages\addressbook.proto

∙-I:

指明应用程序的源码位置,假如不赋值,则有当前路径(说实话,该处我是直译了,并不明白是什么意思。

我做了尝试,该值不能为空,如果为空,则提示赋了一个空文件夹,如果是当前路径,请用.代替,我用.代替,又提示不对。

但是可以是任何一个路径,都运行正确,只要不为空);

∙–java_out:

指明目的路径,即生成代码输出路径。

因为我们这里是基于java来说的,所以这里是–java_out,相对其他语言,设置为相对语言即可

∙最后一个参数即.proto文件

此处运行完毕后,查看生成的代码,很有可能会出现一些类没有定义等错误,例如:

com.googlecannotberesolvedtoatype等。

这是因为项目中缺少protocolbuffers的相应library。

在ProtocolBuffers的源码包里,你会发现java/src/main/java,将这下边的文件拷贝到你的项目,大概可以解决问题。

我只能说大概,因为当时我在弄得时候,也是刚学,各种出错,比较恶心。

有一个简单的方法,呵呵,对于懒汉来说。

创建一个maven的java项目,在pom.xml中,添加ProtocolBuffers的依赖即可解决所有问题~在pom.xml中添加如下依赖(注意版本):

<

dependency>

<

groupId>

com.google.protobuf<

/groupId>

artifactId>

protobuf-java<

/artifactId>

version>

2.5.0<

/version>

/dependency>

5. 

ProtocolBufferJavaAPI

5.1产生的类及方法

接下来看一下PB编译器创建了那些类以及方法。

首先会发现一个.java文件,其内部定义了一个AddressBookProtos类,即我们在addressbook.proto文件java_outer_classname指定的。

该类内部有一系列内部类,对应分别是我们在addressbook.proto中定义的message。

每个类内部都有相应的Builder类,我们可以用它创建类的实例。

生成的类及类内部的Builder类,均自动生成了获取message中字段的方法,不同的是,生成的类仅有getter方法,而生成类内部的Builder既有getter方法,又有setter方法。

本例中Person类,其仅有getter方法,如图所示:

但是Person.Builder类,既有getter方法,又有setter方法,如图:

person.builder

从上边两张图可以看到:

1.每一个字段都有JavaBean风格的getter和setter

2.对于每一个简单类型变量,还对应都有一个has这样的一个方法,如果该字段被赋值了,则返回true,否则,返回false

3.对每一个变量,都有一个clear方法,用于置空字段

对于repeated字段:

repeatedfiled

从图上看:

1.从person.builder图上看出,对于repeated字段,还有一个特殊的getter,即getPhoneCount方法,及repeated字段还有一个特殊的count方法

2.其getter和setter方法根据index获取或设置一个数据项

3.add()方法用于附加一个数据项

4.addAll()方法来直接增加一个容器中的所有数据项

注意到一点:

所有的这些方法均命名均符合驼峰规则,即使在.proto文件中是小写的。

PBcompiler生成的方法及字段等都是按照驼峰规则来产生,以符合基本的Java规范,当然,其他语言也尽量如此。

所以,在proto文件中,命名最好使用用“_”来分割不同小写的单词。

5.2枚举及嵌套类

从代码中可以发现,还产生了一个枚举:

PhoneType,该枚举位于Person类内部:

publicenumPhoneType

implementscom.google.protobuf.ProtocolMessageEnum{

/**

*<

code>

MOBILE=0;

/code>

*/

MOBILE(0,0),

HOME=1;

HOME(1,1),

WORK=2;

WORK(2,2),

;

...

除此之外,如我们所预料,还有一个Person.PhoneNumber内部类,嵌套在Person类中,可以自行看一下生成代码,不再粘贴。

5.3Buildersvs.Messages

由PBcompiler生成的消息类是不可变的。

一旦一个消息对象构建出来,他就不再能够修改,就像java中的String一样。

在构建一个message之前,首先要构建一个builder,然后使用builder的setter或者add()等方法为所需字段赋值,之后调用builder对象的build方法。

在使用中会发现,这些构造message对象的builder的方法,都又会返回一个新的builder,事实上,该builder跟调用这个方法的builder是同一方法。

这样做的目的,仅是为了方便而已,我们可以把所有的setter写在一行内。

如下构造一个Person实例:

Personjohn=Person

.newBuilder()

.setId

(1)

.setName("

john"

.setEmail("

**************"

.addPhone(

PhoneNumber

.newBuilder()

.setNumber("

1861xxxxxxx"

.setType(PhoneType.WORK)

.build()

).build();

5.4标准消息方法

每一个消息类及Builder类,基本都包含一些公用方法,用来检查和维护这个message,包括:

isInitialized():

检查是否所有的required字段是否被赋值

2.toString():

返回一个便于阅读的message表示(本来是二进制的,不可读),尤其在debug时候比较有用

3.mergeFrom(Messageother):

仅builder有此方法,将其message的内容与此message合并,覆盖简单及重复字段

4.clear():

仅builder有此方法,清空所有的字段

5.5解析及序列化

对于每一个PB类,均提供了读写二进制数据的方法:

1.byte[]toByteArray();

:

序列化message并且返回一个原始字节类型的字节数组

2.staticPersonparseFrom(byte[]data);

将给定的字节数组解析为message

3.voidwriteTo(OutputStreamoutput);

将序列化后的message写入到输出流

4.staticPersonparseFrom(InputStreaminput);

读入并且将输入流解析为一个message

这里仅列出了几个解析及序列化方法,完整列表,可以参见:

Message 

APIreference

6.使用PB生成类写入

接下来使用这些生成的PB类,初始化一些联系人,并将其写入一个文件中。

下面的程序首先从一个文件中读取一个通讯簿(AddressBook),然后添加一个新的联系人,再将新的通讯簿写回到文件。

packagecom.example.tutorial;

importcom.example.tutorial.AddressBookProtos.AddressBook;

importcom.example.tutorial.AddressBookProtos.Person;

importjava.io.BufferedReader;

importjava.io.FileInputStream;

importjava.io.FileNotFoundException;

importjava.io.FileOutputStream;

importjava.io.InputStreamReader;

importjava.io.IOException;

importjava.io.PrintStream;

classAddPerson{

//ThisfunctionfillsinaPersonmessagebasedonuserinput.

staticPersonPromptForAddress(BufferedReaderstdin,PrintStreamstdout)

throwsIOException{

Person.Builderperson=Person.newBuilder();

stdout.print("

EnterpersonID:

"

);

person.setId(Integer.valueOf(stdin.readLine()));

Entername:

person.setName(stdin.readLine());

Enteremailaddress(blankfornone):

Stringemail=stdin.readLine();

if(email.length()>

0){

person.setEmail(email);

}

while(true){

stdout.print("

Enteraphonenumber(orleaveblanktofinish):

Stringnumber=stdin.readLine();

if(number.length()==0){

break;

}

Person.PhoneNumber.BuilderphoneNumber=Person.PhoneNumber

.newBuilder().setNumber(number);

Isthisamobile,home,orworkphone?

Stringtype=stdin.readLine();

if(type.equals("

mobile"

)){

phoneNumber.setType(Person.PhoneType.MOBILE);

}elseif(type.equals("

home"

phoneNumber.setType(Person.PhoneType.HOME);

work"

phoneNumber.setType(Person.PhoneType.WORK);

}else{

stdout.println("

Unknownphonetype.Usingdefault."

person.addPhone(phoneNumber);

returnperson.build();

//Mainfunction:

Readstheentireaddressbookfromafile,

//addsonepersonbasedonuserinput,thenwritesitbackouttothesame

//file.

publicstaticvoidmain(String[]args)throwsException{

if(args.length!

=1){

System.err.println("

Usage:

AddPersonADDRESS_BOOK_FILE"

System.exit(-1);

AddressBook.BuilderaddressBook=AddressBook.newBuilder();

//Readtheexistingaddressbook.

try{

addressBook.mergeFrom(newFileInputStream(args[0]));

}catch(FileNotFoundExceptione){

System.out.println(args[0]

+"

Filenotfound.Creatinganewfile."

//Addanaddress.

addressBook.addPerson(PromptForAddress(newBufferedReader(

newInputStreamReader(System.in)),System.out));

//Writethenewaddressbookb

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 广告传媒

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1