Protokołu Google Protocol Buffers nie trzeba przedstawiać, ale przy okazji analizowania gRPC, pomyślałem, że o nim wspomnę.
Proto jest protokołem przechowywania danych w ustrukturyzowany sposób i stanowi lżejszą alternatywę dla XML, czy JSON.
Definicja struktury danych rozpoczyna się od słowa kluczowego message, a dalej posiłkujemy się już dedykowanymi typami danych dla ciągów znaków, liczb, dat, własnych pól wyliczeniowych czy wcześniej utworzonymi własnymi strukturami danych.
Przykład struktury danych:
message Data {
enum DataType {
New = 0;
Deleted = 1;
}
message Content {
string description = 1;
}
int32 id = 1;
string name = 2;
DataType type = 3;
Content content = 4;
google.protobuf.Timestamp UpdateTime = 5;
}
Własne struktury danych możemy swobodnie używać w kolejnych strukturach danych.
Utworzone typy danych przechowujemy w pliku z rozszerzeniem .proto, na początku którego określamy wersję protokołu, nazwę paczki i importujemy dodatkowe przestrzenie, np.:
syntax = "proto3";
package kq.Research.Tests.Proto;
import "google/protobuf/timestamp.proto";
message Data {
// struktura danych opisana powyżej
}
message DataContainer {
repeated Data data = 1;
}
Zapisany plik proto, możemy zamodelować np. w postaci klasy C# i swobodnie używać w dowolnej aplikacji.
protoc.exe --csharp_out=./ data.proto
Przykład użycia w C#:
using System;
using System.IO;
using Google.Protobuf;
namespace Kq.Research.Tests.Proto
{
class Program
{
static void Main(string[] args)
{
Data data = new Data()
{
Id = 12345,
Name = "My data",
Content = new Data.Types.Content
{
Description = "important data"
},
Type = Data.Types.DataType.New
};
using (var file = File.Create("data.dat"))
data.WriteTo(file);
Data newData = null;
using (var file = File.OpenRead("data.dat"))
newData = Data.Parser.ParseFrom(file);
newData.Id += 1;
newData.Name = "My updated data";
newData.Content.Description = "new important data";
DataContainer datas = new DataContainer();
datas.Data.Add(data);
datas.Data.Add(newData);
using (var file = File.Create("container.dat"))
datas.WriteTo(file);
DataContainer readDatas = null;
using (var file = File.OpenRead("container.dat"))
readDatas = DataContainer.Parser.ParseFrom(file);
PrintMessage(readDatas);
}
private static void PrintMessage(IMessage message)
{
var descriptor = message.Descriptor;
foreach (var field in descriptor.Fields.InDeclarationOrder())
{
Console.WriteLine($"Field {field.FieldNumber} ({field.Name}): {field.Accessor.GetValue(message)}");
}
}
}
}
Oczywiście powyższy opis nie wyczerpuje możliwości, ale stanowi szybki start w formie jednostronicowego manuala.