Protobuf Java (2)

接上一篇文章 Protobuf Java (1)   ,接下来写一个demo ,使用protobuf 读写消息.

目录

1、写消息

2、读一个消息

3、扩展Protocol Buffer 


1、写消息

现在让我们尝试使用协议缓冲区类。您希望地址簿应用程序能够做的第一件事是将个人详细信息写入地址簿文件。为此,您需要创建并填充协议缓冲区类的实例,然后将它们写入输出流。

这是一个程序,它从一个文件中读取地址簿,根据用户输入向其中添加一个新用户,然后再次将新的地址簿写回文件。协议编译器直接调用或引用代码的部分是重点

package com.example.tutorial;

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

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

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

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

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

    while (true) {
      stdout.print("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.print("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.addPhone(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 an address.
    addressBook.addPerson(
      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();
  }
}

运行,添加命令行参数.

控制台输入如下数字:

生成一个文件,内容如下:

2、读一个消息

当然,如果你不能从地址簿中获得任何信息,地址簿就没有多大用处!本例读取上面示例创建的文件并打印其中的所有信息。

package com.example.tutorial;

import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;

class ListPeople {
  // Iterates though all people in the AddressBook and prints info about them.
  static void Print(AddressBook addressBook) {
    for (Person person: addressBook.getPersonList()) {
      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.getPhoneList()) {
        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);
  }
}

运行,输入读取的文件,打印如下:

至此,写入读取的流程结束.

3、扩展Protocol Buffer 

在发布使用协议缓冲区的代码之后,您迟早会希望“改进”协议缓冲区的定义。如果您希望新的缓冲区向后兼容,而旧的缓冲区向前兼容——您几乎肯定希望这样——那么您需要遵循一些规则。在协议缓冲区的新版本中:

  • you must not change the tag numbers of any existing fields.
  • you must not add or delete any required fields.
  • you may delete optional or repeated fields.
  • you may add new optional or repeated fields but you must use fresh tag numbers (i.e. tag numbers that were never used in this protocol buffer, not even by deleted fields).

(这些规则有一些例外some exceptions ,但很少使用。)

如果您遵循这些规则,旧代码将愉快地阅读新消息,并简单地忽略任何新字段。对于旧的代码,被删除的可选字段将只有它们的默认值,而被删除的重复字段将为空。新代码也将透明地读取旧消息。但是,请记住,新的可选字段不会出现在旧的消息中,因此您需要显式地检查它们是否用has_设置,或者在.proto文件中提供一个合理的默认值,在标记号后面加上[default = value]。如果未为可选元素指定默认值,则使用类型特定的默认值:对于字符串,默认值是空字符串。对于布尔值,默认值为false。对于数值类型,默认值为零。还要注意的是,如果您添加了一个新的重复字段,您的新代码将无法判断它是空的(根据新代码)还是根本没有设置(根据旧代码),因为它没有has_标志。

4、高级用法

协议缓冲区的用途不仅限于简单的访问器和序列化。一定要研究Java API引用( Java API reference),看看还可以用它们做什么。

协议消息类提供的一个关键特性是反射。您可以迭代消息的字段并操作它们的值,而无需针对任何特定的消息类型编写代码。使用反射的一个非常有用的方法是将协议消息与其他编码(如XML或JSON)进行转换。反射的一个更高级的用途可能是发现相同类型的两个消息之间的差异,或者开发一种“协议消息的正则表达式”,在这种表达式中可以编写与特定消息内容匹配的表达式。如果您发挥您的想象力,可能会将协议缓冲区应用到比您最初预期的范围更广的问题上!

发布了192 篇原创文章 · 获赞 254 · 访问量 76万+

猜你喜欢

转载自blog.csdn.net/yulei_qq/article/details/103168937