google的protocol buffer的使用

介绍

protocol buffer简称protobuf。

在网络数据传输的时候,我们需要序列化和反序列化。

java自带的序列化和反序列化只支持java语言。如果client用python写,server用java写,这样就无法完成数据传输。

将数据序列化到xml文件中也行,但是很占空间,并且xml的树结构没有java的字段简单易读。

所以,我们使用一个google的rpc库:protobuf。

下载

首先我们要下载编译器:

在github上找到google的protobuf项目,并且下载适合自己电脑的编译器:

将编译器放在系统变量中:

这就算是安装完了。


下载对应的java库:

// https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java
    compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.12.2'
    // https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util
    compile group: 'com.google.protobuf', name: 'protobuf-java-util', version: '3.12.2'

编写.proto文件

.proto文件是你要存储的数据的描述文件。

在src下建一个文件:protobuf/addressbook.proto(必须要小写)

idea要安插件才能认识.proto结尾的文件,你就按着它的提示安装就行了。

address book.proto:

syntax = "proto2";

package protobuftest;

option java_package = "com.ocean.protobuftest";
option java_outer_classname = "AddressBookProtos";

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

message AddressBook {
  repeated Person people = 1;
}

syntax是一个版本,提供错误检查。

package指定生成的class文件在那个包中。java_package是更加具体的指定包名。

java_outer_classname指定生成的class文件的名字。也就是所谓的类名。

然后我们定义了两个message:Person和AddressBook。

message就像内部类一样。

在Person里面,还有一个message:PhoneNumber,以及一个枚举类:PhoneType。

首先,看一下字段的基本数据类型:bool, int32, float, double, string

这和java极其像。

字段描述有required,optional,repeated

  • required表示必须赋值。

  • optional可以不赋值。如果不给,就用默认值。比如

optional PhoneType type = 2 [default = HOME];

PhoneType如果不赋值就默认为HOME。

如果你也没有默认值,就用系统默认值:

数值型的就是0,字符串就是空串,布尔型就是false。

  • repeated就像一个数组。
repeated Person people = 1;

表示Person这个message可以有多个。

至于后面的=1=2之类的,并非赋值,而是一个标记。在二进制编解码过程中确保字段的唯一性。

0-15的标记比16及以上的标记在编码的时候少一个字节,所以会用在较为常用的字段上,或者是repeated字段上。


编译.proto文件

命令:

-I是.proto文件的目录。–java_out是目标目录。后面还跟着.proto文件的地址。

最后的结果:

这个类是不能修改的。


向文件中写入消息

我们将构建Person对象和AddressBook对象。

这里的构建方法是建造者模式。比如:

import com.ocean.protobuftest.AddressBookProtos.Person;

public class TestBuild {
	public static void main(String[] args) {
		Person john =
				Person.newBuilder()
						.setId(1234)
						.setName("John Doe")
						.setEmail("jdoe@example.com")
						.addPhones(
								Person.PhoneNumber.newBuilder()
										.setNumber("555-4321")
										.setType(Person.PhoneType.HOME))
						.build();


		System.out.println(john.getId());
		System.out.println(john.getName());
		System.out.println(john.getEmail());
		Person.PhoneNumber phone = john.getPhones(0);
		System.out.println(phone.getNumber() + "\t" + phone.getType());
	}

}

这就构建了一个Person实例。


我们在resources目录下建addressbook.txt:

我们要把构建的电话本对象写到这个文件中:

package com.ocean.protobuftest;

import com.ocean.protobuftest.AddressBookProtos.AddressBook;
import com.ocean.protobuftest.AddressBookProtos.Person;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;

public class AddPerson {
	// This function fills in a Person message based on user input.
	static AddressBookProtos.Person PromptForAddress(BufferedReader stdin,
			PrintStream stdout) throws IOException {
		Person.Builder person = Person.newBuilder();

		stdout.println("Enter person ID: ");
		person.setId(Integer.valueOf(stdin.readLine()));

		stdout.println("Enter name: ");
		person.setName(stdin.readLine());

		stdout.println("Enter email address (blank for none): ");
		String email = stdin.readLine();
		if (email.length() > 0) {
			person.setEmail(email);
		}

		while (true) {
			stdout.println("Enter a phone number (or leave blank to finish): ");
			String number = stdin.readLine();
			if (number.length() == 0) {
				break;
			}

			Person.PhoneNumber.Builder phoneNumber =
					Person.PhoneNumber.newBuilder().setNumber(number);

			stdout.println("Is this a mobile, home, or work phone? ");
			String type = stdin.readLine();
			if (type.equals("mobile")) {
				phoneNumber.setType(Person.PhoneType.MOBILE);
			} else if (type.equals("home")) {
				phoneNumber.setType(Person.PhoneType.HOME);
			} else if (type.equals("work")) {
				phoneNumber.setType(Person.PhoneType.WORK);
			} else {
				stdout.println("Unknown phone type.  Using default.");
			}

			person.addPhones(phoneNumber);
		}

		return person.build();
	}

	// Main function:  Reads the entire address book from a file,
	//   adds one person based on user input, then writes it back out to the same
	//   file.
	public static void main(String[] args) throws Exception {
		if (args.length != 1) {
			System.err.println("Usage:  AddPerson ADDRESS_BOOK_FILE");
			System.exit(-1);
		}

		AddressBook.Builder addressBook = AddressBook.newBuilder();


		// Read the existing address book.
		try {
			addressBook.mergeFrom(new FileInputStream(args[0]));
		} catch (FileNotFoundException e) {
			System.out.println(args[0] + ": File not found.  Creating a new file.");
		}


		// Add a person.
		addressBook.addPeople(
				PromptForAddress(new BufferedReader(new InputStreamReader(System.in)),
						System.out));

		// Write the new address book back to disk.
		FileOutputStream output = new FileOutputStream(args[0]);
		addressBook.build().writeTo(output);
		output.close();
	}
}

我们将获取到控制台的输出流,设置Person对象的属性。

在调用PromptForAddress方法之前,程序会先去读文件中原来已经存在的数据。

加入动态参数:

运行:

如此addressbook.txt就有值了。

现在我们要将其读出来。

从文件中读取消息

package com.ocean.protobuftest;

import java.io.FileInputStream;

import com.ocean.protobuftest.AddressBookProtos.Person;
import com.ocean.protobuftest.AddressBookProtos.AddressBook;


public class ListPeople {
	// Iterates though all people in the AddressBook and prints info about them.
	static void Print(AddressBook addressBook) {
		for (Person person: addressBook.getPeopleList()) {
			System.out.println("Person ID: " + person.getId());
			System.out.println("  Name: " + person.getName());
			if (person.hasEmail()) {
				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());
			}
		}
	}

	// Main function:  Reads the entire address book from a file and prints all
	//   the information inside.
	public static void main(String[] args) throws Exception {
		if (args.length != 1) {
			System.err.println("Usage:  ListPeople ADDRESS_BOOK_FILE");
			System.exit(-1);
		}

		// Read the existing address book.
		AddressBook addressBook =
				AddressBook.parseFrom(new FileInputStream(args[0]));

		Print(addressBook);
	}
}

同样的,我们要加入动态参数:

运行:

我加了两个人,所以遍历出两个。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值