• Protocol Buffers 学习笔记


    Protocol Buffers

    Java 示例

    目的

    1. 使用proto文件定义消息格式
    2. 使用protocol buffer编译器
    3. 使用protocol buffer API读写消息

    示例说明

    • 一个简单的通讯录例子,可以从文件读写联系人信息。通讯录的每个人包含一个名字,一个ID,一个邮件地址和一个联系电话。
    • 对于这个需求,你有什么序列化的想法?下面列出一些解决方案:
      • 使用Java序列化。问题很多(可以看Effective Java这本书)且不支持多语言
      • 自己写序列化方式。需要自己解析和编码,适合简单数据
      • 序列化成XML。性能低,空间利用率低

    定义protpcol文件

    syntax = "proto2";
    
    package tutorial;
    
    option java_multiple_files = true;
    option java_package = "com.example.tutorial.protos";
    option java_outer_classname = "AddressBookProtos";
    
    message Person {
      optional string name = 1;
      optional int32 id = 2;
      optional string email = 3;
    
      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }
    
      message PhoneNumber {
        optional string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }
    
      repeated PhoneNumber phones = 4;
    }
    
    message AddressBook {
      repeated Person people = 1;
    }
    
    • 从java来看,就是定义一个AddressBook对象,AddressBook包含了一个集合类型的Person。

    • Person包含了4个字段:name,id,email,phones

    • phones是PhoneNumber引用类型的,PhoneNumber包含两个字段,PhoneType为枚举

    • 对应示例

    package com.example.tutorial.protos;
    public class AddressBookProtos{
      public static class Person{
        private String name;
        private int id;
        private String email;
        private PhoneNumber phones;
        public static class PhoneNumber{
          private String number;
          private PhoneType type=PhoneType.HOME;
          public static enum PhoneType{
            MOBILE,HOME,WORK;
          }
        }
      }
      public static class AddressBook{
        private List<Person> people;
      } 
    }
    // 省略了getter、setter等
    

    proto文件说明

    • package用于声明包,可以作为java的包名,【必须有】多语言使用,解决定义冲突,可能不是java的反转域名规范;如果定义了java_package,使用java_package生成java包。
    • java_outer_classname用于定义外部类名称,不写会使用文件名驼峰形式。my_proto.proto会使用外部名MyProto。
    • java_multiple_files = true用于指示生成多个文件还是单个文件。
    • 基本类型 bool, int32, float, double, and string ,enum,嵌套messge
    • =1,=2这些是用于定义二进制编解码的字段顺序,必须唯一,1-15只占用一个字节,最好用来定义常用的字段和可重复字段
    • 每个字段有一个修饰符:required(必须)、optional(可选)、repeated(可重复)。

    编译proto文件

    protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
    
    • 示例:protoc --java_out=. data.proto

    protocol buffer API

    • 每个类都会生成POJO类和Buidler,Builder类有getter和setter,POJO类只有getter

    写数据

    import com.example.tutorial.protos.AddressBook;
    import com.example.tutorial.protos.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.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 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();
      }
    }
    

    读数据

    import com.example.tutorial.protos.AddressBook;
    import com.example.tutorial.protos.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.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);
      }
    }
    

    Guides

  • 相关阅读:
    【design pattern】代理模式
    Java IO(十七)FIleReader 和 FileWriter
    Java IO(十六)InputStreamReader 和 InputStreamWriter
    Java IO(十五)FilterReader 和 FilterWriter、FilterReader 子类 PushBackReader
    Java IO(十四) CharArrayReader 和 CharArrayWriter
    Java IO(十三)PipedReader 和 PipedWriter
    Java IO(十二) 字符流 Writer 和 Reader
    Java IO(十一) DataInputStream 和 DataOutputStream
    Java IO(十) BufferedInputStream 和 BufferedOutputStream
    Java IO(九)FilterInputStream 和 FilterOutputStream
  • 原文地址:https://www.cnblogs.com/zby9527/p/15338692.html
Copyright © 2020-2023  润新知