Protocol Buffers (protobuf) e' una libreria di Google per la serializzazione di dati. Con il termine serializzazione si intende il processo di tradurre una "data structure" (array, struttura, albero binario.....) in un file che conservi oltre che i dati anche la struttura. Una forma tipica di serializzazione sono i file in formato JSON
Per utilizzare protobuf con GoLang si parte dalla definizione della struttura in file .proto. Un semplice modello puo' essere il seguente
syntax = "proto3";
package main;
import "google/protobuf/timestamp.proto";
message Person {
string name = 1;
int32 id = 2;
string email = 3;
google.protobuf.Timestamp last_updated = 5;
}
da notare che il file .proto deve avere lo stesso package del codice che lo utilizzera'
i numeri di fianco ai vari campi sono univoci
il file .proto deve essere compilato tramite il compilatore protoc. Su Debian l'installazione del compilatore avviene mediante
apt install protobuf-compiler
apt install golang-goprotobuf-dev
si crea il file .proto nella stessa directory del sorgente Go e si compila con
protoc --go_out=. *.proto
questo genera un nuovo file con estensione .go. Se si apre il file con editor di testo si osserva che oltre alla struttura dei dati sono state create delle funzioni tipo helper che facilitano l'immissione e gestione della struttura dati
per gestire i dati all'interno del progetto Go, nel caso si usino tipo dati di uso comune tipo datetime gia' formalizzati in protobuf si deve scaricare la loro definizione per esempio
go get github.com/golang/protobuf/ptypes/timestamp
i dati di un protobuf possono essere salvati su un file ma questo sara' binario e non immediatamente human readable
package main
import (
"fmt"
"io/ioutil"
"log"
proto "github.com/golang/protobuf/proto"
)
// apt install protobuf-compiler
// per compilare il file proto
// protoc --go_out=. *.proto
// go get github.com/golang/protobuf/ptypes/timestamp
func main() {
dati_persona := &Person{
Name: "Luca",
Id: 51, //per poco ancora
}
data, err := proto.Marshal(dati_persona)
if err != nil {
log.Fatal("marshaling error: ", err)
}
fmt.Println(data)
out, err := proto.Marshal(dati_persona)
if err != nil {
log.Fatalf("Serialization error: %s", err.Error())
}
if err := ioutil.WriteFile("dati.bin", out, 0644); err != nil {
log.Fatalf("Write File Error: %s ", err.Error())
}
fmt.Println("Write Success")
//Read from file
in, err := ioutil.ReadFile("dati.bin")
if err != nil {
log.Fatalf("Read File Error: %s ", err.Error())
}
dati_persona2 := &Person{}
err2 := proto.Unmarshal(in, dati_persona2)
if err2 != nil {
log.Fatalf("DeSerialization error: %s", err.Error())
}
fmt.Println("Read Success")
fmt.Printf("Nome %s\n", dati_persona2.GetName())
}