Protobuf Language Guide(proto3)-2

本文介绍了Protobuf中消息类型的定义、复用方法及更新规则,包括如何导入其他.proto文件、定义嵌套类型、使用Any类型及Oneof特性等,并讨论了向后兼容性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Using Other Message

We also can use other Message type in an Message type, like this:

message SearchResponse {
	repeated Result results = 1;
}

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

Also, if we want to use another Message type from other .proto file, how to do that? We can use import to solve this problem:

// new.proto
// Some definitions...
// 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

The protocol compiler searches for imported files in a set of directories specified on the protocol compiler command line using the -I/--proto_path flag. In general you should set the --proto_path flag to the root of your project and use fully qualified names for all imports.

Nested Type

We can also define another Message type in an Message type, like this:

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

And we can reuse the Result Message type towrad . :

message SomeOtherMessage {
	SearchResponse.Result result = 1;
}

Updating a Message Type

In protobuf, there is a way that can help you update the message types without breaking any of your existing code, to achieve this, you need to obey these rules:

  • Don’t change the field numbers for any existing fields.
  • If you add new fields, any messages serialized by code using your “old” message format can still be parsed by your new generated code. You should keep in mind the default values for these elements so that new code can properly interact with messages generated by old code.
  • Fields can be removed, as long as the field number is not used again in your updated message type.
  • int32, uint32, int64, uint64, and bool are all compatible – this means you can change a field from one of these types to another without breaking forwards- or backwards-compatibility.
  • sint32 and sint64 are compatible with each other but are not compatible with the other integer types.
  • string and bytes are compatible as long as the bytes are valid UTF-8.
  • Embedded messages are compatible with bytes if the bytes contain an encoded version of the message.
  • fixed32 is compatible with sfixed32, and fixed64 with sfixed64.
  • enum is compatible with int32, uint32, int64, and uint64 in terms of wire format (note that values will be truncated if they don’t fit).
  • Changing a single value into a member of a new oneof is safe and binary compatible. But moving any fields into an existing oneof is not safe.

Any

The Any message type lets you use messages as embedded types without having their .proto definition. But you need to import google/protobuf/any.proto to use Any type:

import "google/protobuf/any.proto";

message ErrorStatus {
	string message = 1;
	repeated google.protobuf.Any details = 2;
}

Different language implementations will support runtime library helpers to pack and unpack Any values in a typesafe manner, for example, in C++ there are PackFrom() and UnpackTo() methods:

// 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 ...
  }
}

Oneof

If you have a message with many fields and where at most one field will be set at the same time, you can enforce this behavior and save memory by using the oneof feature.

You can use a special case() or WhichOneof() method to check which value in a oneof is set.

How to use Oneof?

To define Oneof, we need to use the oneof keyword:

message SampleMessage {
	oneof test_oneof {
		string name = 4;
		SubMessage sub_message = 9;
	}
}

You then add your oneof fields to the oneof definition. You can add fields of any type, but cannot use repeated fields.

Oneof features?
  • Setting a oneof field will automatically clear all other members of the oneof.

  • If the parser encounters multiple members of the same oneof on the wire, only the last member seen is used in the parsed message.

  • A oneof cannot be repeated.

  • Reflection APIs work for oneof fields.

  • If you set a oneof field to the default value (such as setting an int32 oneof field to 0), the “case” of that oneof field will be set, and the value will be serialized on the wire.

  • If you’re using C++, make sure your code doesn’t cause memory crashes. The following sample code will crash because sub_message was already deleted by calling the set_name() method.

  • SampleMessage message;
    SubMessage* sub_message = message.mutable_sub_message();
    message.set_name("name");      // Will delete sub_message
    sub_message->set_...            // Crashes here
    
  • Again in C++, if you Swap() two messages with oneofs, each message will end up with the other’s oneof case: in the example below, msg1 will have a sub_message and msg2 will have a name.

  • SampleMessage msg1;
    msg1.set_name("name");
    SampleMessage msg2;
    msg2.mutable_sub_message();
    msg1.swap(&msg2);
    CHECK(msg1.has_sub_message());
    CHECK(msg2.has_name());
    
Backwards-compatibility issues

Be careful when adding or removing oneof fields. If checking the value of a oneof returns None/NOT_SET, it could mean that the oneof has not been set or it has been set to a field in a different version of the oneof.

Tag reuse issues
  • Move fields into or out of a oneof: You may lose some of your information (some fields will be cleared) after the message is serialized and parsed.
  • Delete a oneof field and add it back: This may clear your currently set oneof field after the message is serialized and parsed.
  • Split or merge oneof: This has similar issues to moving regular fields.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值