proto-language
Language constructs
A message is just an aggregate containing a set of typed fields. Many standard simple data types are available as field types, including bool, int32, float, double, and string. You can also add further structure to your messages by using other message types as field types.
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
google.protobuf.Timestamp last_updated = 5;
}
message AddressBook {
repeated Person people = 1;
}
Working with different constructs
Lists/repeated fields
To specify lists or arrays, we need to use a special keyword for those fields : repeated
The Google.Protobuf.Collections.RepeatedField<T> type is used in the produced code to represent recurring fields. This type is always a read-only property and is different from the standard .NET collection types. Despite this difference, RepeatedField<T> implements all the typical .NET collection interfaces, including IList and IEnumerable. Hence, we can efficiently perform LINQ queries or transform it into an array or a list. Additionally, RepeatedField<T> comprises the necessary code to serialize and deserialize the list to the binary wire format.
repeated string snippets = 3;
message Person {
repeated string Aliases = 8;
}
Composed types
Just like C# types, we can compose messages from other messages, as long as these respect the basic rules.
message SearchResponse {
Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
We can also define nested messages, but in the end we will have to work with long namespaces. I advise you to use nested only if you really have to.
Nested types
message Outer { // Level 0
message MiddleAA { // Level 1
message InnerA { // Level 2
int64 ival = 1;
bool booly = 2;
}
}
message MiddleBB { // Level 1
message InnerB { // Level 2
int32 ival = 1;
bool booly = 2;
}
}
}
Reserved fields
If you update a message type by entirely removing a field, or commenting it out, future users can reuse the field number when making their own updates to the type. This can cause severe issues if they later load old versions of the same .proto, including data corruption, privacy bugs, and so on. One way to make sure this doesn’t happen is to specify that the field numbers (and/or names, which can also cause issues for JSON serialization) of your deleted fields are reserved. The protocol buffer compiler will complain if any future users try to use these field identifiers. You can specify that your reserved numeric value range goes up to the maximum possible value using the max keyword
The reserved fields allocate a 'place' in the binary string. This way if a field is removed from a message in a new version, it prevents it from being reused.
syntax "proto3";
message Stock {
string display_name = 3;
int32 market_id = 4;
int32 id = 1;
string symbol = 2;
}
syntax "proto3";
message Stock {
reserved 3, 4;
int32 id = 1; s
string symbol = 2;
}
You can mark a field by order or by name
message Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "foo", "bar";
}