上一篇,介绍了proto文件,以及用idea的工具编译了proto文件。当然,我们也可以直接用protoc命令来编译。
protoc -I=SRC_DIR --java_out=DST_DIR SRC_DIR/hello.proto
F:/soft/protoc-3.10.1-win64/bin
的目录(以自己的环境为准),要么配置在系统环境变量path里。 -I=IMPORT_PATH
写法同 --proto_path=IMPORT_PATH
,用来指定proto文件目录, --java_out=
指定生成的java文件存放的目录,java的类名同proto文件名,但是如果Message的名称和proto名称一样,比如上一篇的简单例子,proto文件名是Person.proto,Message也是Person,那类名就是PersonOutClass。如果是proto文件名是foo_bar.proto,那类名就是FooBar。这边要注意的是,文件夹是要存在的,protoc不会帮我们创建。 proto文件
syntax = "proto3"; package ch13; option java_package = "com.example.ch13"; option java_outer_classname = "AddressBookProtos"; message Person { string name = 1; int32 id = 2; string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { string number = 1; PhoneType type = 2 ; } repeated PhoneNumber phones = 4; } message AddressBook { repeated Person person = 1; }
生成java文件AddressBookProtos.java后,我们看看java类里有哪些内容。
PersonOrBuilder继承了 com.google.protobuf.MessageOrBuilder
,我们看看这个接口的几个方法,都是对应我们在proto文件中定义的字段,很像我们的javaBean,但是没有set方法,有get方法。
对于Repeated字段,还提供了其他的方法,比如通过索引查找、获取个数等。
// 对应着name java.lang.String getName(); com.google.protobuf.ByteString getNameBytes(); // 对应着id int getId(); // 对应着email java.lang.String getEmail(); com.google.protobuf.ByteString getEmailBytes(); // 对应着phones java.util.List<com.example.ch13.AddressBookProtos.Person.PhoneNumber> getPhonesList(); com.example.ch13.AddressBookProtos.Person.PhoneNumber getPhones(int index); int getPhonesCount(); java.util.List<? extends com.example.ch13.AddressBookProtos.Person.PhoneNumberOrBuilder> getPhonesOrBuilderList(); com.example.ch13.AddressBookProtos.Person.PhoneNumberOrBuilder getPhonesOrBuilder(int index);
Person继承了com.google.protobuf.GeneratedMessageV3,还实现了上面的PersonOrBuilder接口。
Person类中定义了一个枚举PhoneType:
Message中嵌套了PhoneNumber,所以Person中包含了PhoneNumberOrBuilder和PhoneNumber,这两个结构同PersonOrBuilder和Person:
Person类中还包含了重要的Builder,这个后面在详细说。
除了上面几个枚举、接口、类,还包含了几个方法:
getDefaultInstance
:返回Person的单例。等同于 Person.newBuilder().build()
public static com.example.ch13.AddressBookProtos.Person getDefaultInstance() { return DEFAULT_INSTANCE; }
parseFrom(...)
:解析给定的数据源,转成Message对象的Person。
newBuilder
:创建一个build
Builder继承了 com.google.protobuf.GeneratedMessageV3.Builder<Builder>
,还实现了PersonOrBuilder接口。
主要有构建类,对字段的set、get、clear等操作。
先获取Builder,然后对各个实例进行set或者add操作,最后build返回一个AddressBook,通过AddressBook的writeTo方法,把序列号的值写入文件。
AddPerson
class AddPerson { public static void main(String[] args) throws Exception { AddressBook.Builder builder =AddressBook.newBuilder().addPerson(Person.newBuilder().setName("aa") .setId(1) .setEmail("123@qq.com") .addPhones(Person.PhoneNumber.newBuilder() .setNumber("111") .setType(Person.PhoneType.HOME)) .addPhones(Person.PhoneNumber.newBuilder() .setNumber("222") .setType(Person.PhoneType.MOBILE))).addPerson(Person.newBuilder().setName("bb") .setId(2) .setEmail("456@qq.com") .addPhones(Person.PhoneNumber.newBuilder() .setNumber("333") .setType(Person.PhoneType.HOME)) .addPhones(Person.PhoneNumber.newBuilder() .setNumber("444") .setType(Person.PhoneType.MOBILE))); AddressBook addressBook = builder.build(); PersonList.Print(addressBook); addressBook.writeTo(new FileOutputStream("f://1.txt")); } }
PersonList
class PersonList { static void Print(AddressBook addressBook) { for (Person person: addressBook.getPersonList()) { System.out.println("Person ID: " + person.getId()); System.out.println(" Name: " + person.getName()); System.out.println(" E-mail address: " + person.getEmail()); for (Person.PhoneNumber phoneNumber : person.getPhonesList()) { switch (phoneNumber.getType()) { case MOBILE: System.out.print(" Mobile phone #: "); break; case HOME: System.out.print(" Home phone #: "); break; case WORK: System.out.print(" Work phone #: "); break; } System.out.println(phoneNumber.getNumber()); } } } }
运行结果:
ListPeople,通过parseFrom读取
class ListPeople { public static void main(String[] args) throws Exception { AddressBook addressBook = AddressBook.parseFrom(new FileInputStream("f://1.txt")); PersonList.Print(addressBook); } }
运行结果同上。
public class MergePerson { public static void main(String[] args) { Person person1 =Person.newBuilder().setId(1).addPhones(Person.PhoneNumber.newBuilder().setNumber("111").setType(Person.PhoneType.MOBILE)).build(); Person person2 =Person.newBuilder().setId(2).addPhones(Person.PhoneNumber.newBuilder().setNumber("222").setType(Person.PhoneType.WORK)).build(); person1 = person1.toBuilder().mergeFrom(person2).build(); System.out.println("Person ID: " + person1.getId()); for (Person.PhoneNumber phoneNumber : person1.getPhonesList()) { switch (phoneNumber.getType()) { case MOBILE: System.out.print(" Mobile phone #: "); break; case HOME: System.out.print(" Home phone #: "); break; case WORK: System.out.print(" Work phone #: "); break; } System.out.println(phoneNumber.getNumber()); } } }
运行结果如下:
可以看出id被后面的覆盖了,而PhoneNumber是累加的