美文网首页
protocol buffers(二)

protocol buffers(二)

作者: 带马界的神秘人 | 来源:发表于2017-12-25 11:02 被阅读0次

    这篇文章翻译自https://developers.google.com/protocol-buffers/

    默认值

    当一个Message被解析,如果对应字段没有值,解析器会对字段赋予默认值。下面是各个类型的默认值:

    • 对于string,默认值是空字符串。
    • 对于byte数组,默认值是空的byte数组。
    • 对于bool,默认值是false。
    • 对于数值类型,默认值是0。
    • 对于枚举,默认值是枚举中第一个字段,且值必须是0。
    • 对于Message字段,没有默认值,不同语言有不同的处理。

    对于repeated字段的默认值是空list。
    注意对于字段是Message类型的时候,一旦message被解析就无法赋值默认值,或者压根没有赋值。你需要小心的定义一个Message字段。例如,当你不希望默认行为发生的时候,将默认行为开关设置成false。
    另外注意如果message指定了默认值,默认值并不参与序列化传递。

    枚举

    当想定义一个Message类型是,可能需要一个字段只有预定义的指定值。例如添加一个corpus字段在SearchRequest上,corpus只能是UNIVERSAL, WEB, IMAGES, LOCAL, NEWS, PRODUCTS 或 VIDEO,其中一个值。在Message上定义一个enum可以很简单实现。
    在下面的例子中,我们添加了一个enum Corpus和一个Corpus字段。

    message SearchRequest {
      string query = 1;
      int32 page_number = 2;
      int32 result_per_page = 3;
      enum Corpus {
        UNIVERSAL = 0;
        WEB = 1;
        IMAGES = 2;
        LOCAL = 3;
        NEWS = 4;
        PRODUCTS = 5;
        VIDEO = 6;
      }
      Corpus corpus = 4;
    }
    

    正如你所看到,Corpus枚举的第一个字段值等0,对于每个enum来说第一个字段值必须是0,因为:

    • 这样可以用0表示默认值。
    • 枚举的第一个值一般都是默认值。

    你可以定义一个别名(相同的值,不同的字段名)。这样做需要开启别名开关(options allow_alias=true),如果没有开启,生成器发现别名时会报错。

    enum EnumAllowingAlias {
      option allow_alias = true;
      UNKNOWN = 0;
      STARTED = 1;
      RUNNING = 1;
    }
    enum EnumNotAllowingAlias {
      UNKNOWN = 0;
      STARTED = 1;
      // RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
    }
    

    枚举的字段值必须在int32范围内。当enum编码时,负值是低效的因此不推荐。定义enum可以在Message里,也可以在Message外。Message外的enum可以被多个Message使用。Message也可以使用别的Message定义的enum,句法是MessageType.EnumType

    当你运行protocol buffer编译器的时候,enum会被生成为对应的C++或者java的enum类型,Python中会生成EnumDescriptor 类。

    在反序列化时,未识别的enum值会被保留,不同语言有不同的处理。
    在可以动态扩展enum的语言中,例如c++和go,这个未知的enum值会用int存储。在不可以动态扩展enum的语言中,例如java,可以通过特殊的方法获取。任何情况下,无法识别的enum值都会被序列化。

    枚举中的保留值

    如果你移除了enum类型中的字段,可能会被新的字段使用了移除字段的值。这样将会导致使用旧enum生成的服务器出现问题。可以通过 reserved防止这种情况发生。编译器会警告如果使用了保留值。你可以通过max表示一个值到最大值的区间。

    enum Foo {
      reserved 2, 15, 9 to 11, 40 to max;
      reserved "FOO", "BAR";
    }
    

    注意不能在一个reserved中同时包含字段名称和字段值。

    使用另一个Message类型

    可以使用另一个Message类型作为字段类型。例如包含Result的SearchResponse。在同一个proto文件中定义一个Result Message,然后指定SearchResponse字段类型为Result:

    message SearchResponse {
      repeated Result results = 1;
    }
    
    message Result {
      string url = 1;
      string title = 2;
      repeated string snippets = 3;
    }
    

    导入定义 Importing Definitions

    在上面的例子中Result和SearchResponse在同一个文件中,当想使用在其他proto文件中定义的Message时?可以通过导入方式使用别的文件中定义的Message。添加import语句在文件开头:

    import "myproject/other_protos.proto";
    

    默认情况下通过import proto文件可以使用定义好的Message。然而当你需要移动一个proto文件到其它地方时,不需要修改所有引用它的proto文件,只需要放入一个替身proto文件在原位置,替身文件中import public原proto文件。import public会传递定义,例如:

    // new.proto
    // All definitions are moved here
    
    // old.proto
    // This is the proto that all clients are importing.
    import public "new.proto";
    import "other.proto";
    
    // client.proto
    import "old.proto";
    // You use definitions from old.proto and new.proto, but not other.proto
    

    protocol编译器会搜索通过命令行参数-I/--proto_path指定的一组文件夹。如果没有指定文件夹,编译器会搜索当前目录。通常你需要设定--proto_path指向你的项目目录,然后import时使用全路径。

    使用proto2的Message类型

    在proto3中import proto2 文件是可以的。然而proto2中的enum不能直接在proto3中使用。

    嵌套类型

    可以使用和定义Message在其他Message中。下面的例子中Result定义在SearchRespose中:

    message SearchResponse {
      message Result {
        string url = 1;
        string title = 2;
        repeated string snippets = 3;
      }
      repeated Result results = 1;
    }
    

    如果你想使用这个Message在父Message外面,可以通过Parent.Type:

    message SomeOtherMessage {
      SearchResponse.Result result = 1;
    }
    

    可以嵌套任意多层

    message Outer {                  // Level 0
      message MiddleAA {  // Level 1
        message Inner {   // Level 2
          int64 ival = 1;
          bool  booly = 2;
        }
      }
      message MiddleBB {  // Level 1
        message Inner {   // Level 2
          int32 ival = 1;
          bool  booly = 2;
        }
      }
    }
    

    更新Message类型

    如果一个Message类型不符合你的现在的需求,例如你想扩展一个字段。但是你还在用旧的Message类型,不用担心!更新一个Message类型不会影响已经存在的代码,只要符合下面的规则:

    • 不要修改任何已经存在的字段的标签值。
    • 如果你扩展了新字段,新生成的代码对旧的Message依然可以解析成功。只要指定好新增字段的默认值,旧的Message就可以很好的被解析到新的Message类型。同样新的Message可以被旧的Message解析器很好的解析,新增的字段会被就Message解析器忽略。
    • 字段可以被移除,只要保证标签值在以后不被使用。你可以重命名要移除的字段,例如加入前缀OBSOLETE_,或者通过reserved标签值,这样就可以保证这个标签值以后不会被使用。
    • int32,uint32,int64,uint64和bool都是兼容的 - 这意味着你可以修改这些的类型变成这里的其他类型(例如int32修改从bool),而不会破坏向前兼容性和向后兼容性。如果一个类型反序列化不符合对应类型,将会出现精度丢失,例如int64 转int32。
    • sint32和sint64 是相互兼容的,但是与其他整数类型不兼容。
    • string 和byte数组是相互兼容,如果编码是UTF8。
    • 嵌套Message和byte数组兼容,如果byte数组中包含Message版本信息。
    • fixed32 和 sfixed32 相互兼容, fixed64 和 sfixed64 相互兼容。
    • enum和int32,uint32,int64,uint64兼容。然而客户端解析会稍有不同:例如未识别的enum值会被保留到Message中,但是反序列化不同语言有不同的处理方式。

    未知字段

    未知字段是protocol buffer序列化数据中不能被解析器识别的字段。例如,当一个旧的解析器解析新的Messag类型时,新添加的字段都会变成位置字段。

    Proto3 实现的解析器可以对包含未知字段的Message解析。然后解析器实现可能保留未知字段也可能不保留未知字段。你不能确定未知字段是被保留或者删除。对于大多数protocol buffer实现中,未知字段不能被proto运行时访问,因为在反序列化的时候已经被删除。这个与proto2的行为不一致。

    Any

    Any 类型可以让你使用没有定义的类型。 Any会被序列化成byte数组,同时会拥有一个URL作为全局唯一标识符,用于解析。使用Any类型,你需要import google/protobuf/any.proto

    import "google/protobuf/any.proto";
    
    message ErrorStatus {
      string message = 1;
      repeated google.protobuf.Any details = 2;
    }
    

    默认的URL是type.googleapis.com/packagename.messagename

    不同语言对于Any运行时获取会通过安全的方式:例如java会对Any通过pack()和unpack()方法,在C++中是PackFrom()和UnpackTo()

    // Storing an arbitrary message type in Any.
    NetworkErrorDetails details = ...;
    ErrorStatus status;
    status.add_details()->PackFrom(details);
    
    // Reading an arbitrary message from Any.
    ErrorStatus status = ...;
    for (const Any& detail : status.details()) {
      if (detail.Is<NetworkErrorDetails>()) {
        NetworkErrorDetails network_error;
        detail.UnpackTo(&network_error);
        ... processing network_error ...
      }
    }
    

    目前Any处于开发模式下
    Currently the runtime libraries for working with Any types are under development.

    相关文章

      网友评论

          本文标题:protocol buffers(二)

          本文链接:https://www.haomeiwen.com/subject/hjikgxtx.html