Protobuf Tutorial
Protobuf Tutorial
Audience ............................................................................................................................. 3
Prerequisite ......................................................................................................................... 3
1
11. Protobuf – Nested Class .............................................................................................. 44
OneOf ............................................................................................................................... 63
Any .................................................................................................................................... 68
2
Overview
Protocol Buffers is a library from Google. It provides efficient and
language-independent ways to serialize the data. It supports serialization and
deserialization from languages like Java, Python, Go, Dart, etc. It is one of the
most popular serialization libraries used across industries by various companies.
The major use-case for Google Protocol Buffers is the serialization and
deserialization of data which is simple and fast. Serialization and Deserialization
becomes a very important piece in microservices/distributed environment where
lot of data is transferred across services. That is why, it becomes a very useful
library in developing applications which require high scalability and performance.
Audience
This tutorial deep dives into various components that make Google Protocol
Buffers a very useful library. It is directed towards software professionals who want
to develop highly scalable and performant applications. Post this tutorial, you
would have intermediate knowledge of Protocol Buffers and its usage.
Prerequisite
To learn from this tutorial, you need to have a good hold over Java or Python and
a basic knowledge of data structure is preferable.
3
1. Protobuf – Introduction
In microservice architecture, the application is broken down into small services and
these services communicate with each other via messaging queue and APIs. And
all of this communication happens over a network which requires frequent
conversion of object to bytes and back to objects. So, serialization and
deserialization becomes very critical aspects when it comes to distributed
environment.
So, what makes Google Protobuf special? Here are some of its important features:
4
Language independent: Multiple languages have protobuf library, few
famous ones being Java, Python, Go, etc. So, a Java object can be
serialized into bytes from a Java program and can be deserialized to a a
Python object.
Language
Yes Yes Yes
independent
Support for
Yes No No
evolving schema
5
2. Protobuf – Basic App
Let us now use Google Protocol Buffer and see how it works with a simple Greeting
app. In this example, we will create a simple application which would do the
following:
Greeting Reader:
o Reads the same file which we stored in the above file
o Convert that data into an object and print the data
Let us store the following data in "greeting.proto" and we will use this in our first
application.
syntax = "proto3";
package tutorial;
message Greet {
string greeting = 1;
string username = 2;
}
6
Now, let us take a closer look at the data and see what each line of code does in
the above code block.
syntax = "proto3";
The "syntax" here represents what version of Protobuf we are using. So, we are
using the latest version 3 and the schema thus can use all the syntax which is valid
for version 3.
package tutorial;
The package here is used for conflict resolution if, say, we have multiple
classes/members with same name.
This argument is specific to Java, i.e., the package where the code from the
".proto" file will be auto-generated.
message Greet
Name of the base class for the object which would be created/recreated.
string greeting = 1;
string username = 2;
These are the attributes of the Greet class along with the data type and the position
of the tag in the schema. If a new tag is to be added, it should have "3" as the
position. Note that this position integer is important to ensure that the actual data
is compact and there is scope of schema evolution.
Choose the correct binary based on the OS. We will install proto binary on
Windows but the steps are not very different for Linux.
Once installed, ensure that you are able to access it via command line:
protoc --version
libprotoc 3.15.6
7
It confirms that Protobuf is correctly installed. Now let us move to creating the
Greeting app described above for Java.
Project Structure
Here is the overall project structure that we would have:
And here is the project structure that we would be having for Java:
8
Greeting App in Java
Now that we have installed protoc, we can auto-generate the code from the proto
files using protoc. Let us first create a Java project though.
Following is the Maven configuration that we will use for our Java project. Note
that it contains the required library for Protobuf as well.
9
<modelVersion>4.0.0</modelVersion>
<groupId>com.tutorials.point</groupId>
<artifactId>protobuf-tutorial</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--
https://mvnrepository.com/artifact/com.google.protobuf/protobuf-
java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.15.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<configuration>
10
<!--Put your configurations here-->
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
With the project structure out of the way, let us generate the code for the Greet
class:
Post execution of the command, you will notice two auto-generated classes.
Greeting.java
GreetOrBuilder.java
These two classes would help us with serialization and deserialization of the Greet
object.
Now, let us write the writer of the data, which will take the username and the
greeting as its inputs:
package com.tutorialspoint.greeting;
import java.io.FileOutputStream;
import java.io.IOException;
11
import com.tutorialspoint.greeting.Greeting.Greet;
The writer simply takes CLI arguments, creates the Greet object, serializes it and
then dumps it to a file.
package com.tutorialspoint.greeting;
import java.io.FileInputStream;
import java.io.IOException;
12
import com.tutorialspoint.greeting.Greeting.Greet;
The reader simply reads from the same file, deserializes it, and prints the data
about the greeting.
Now that we have set up the reader and the writer, let us compile the project.
13
Saved greeting with following data to disk:
greeting: Hello
username: John
So, as we see the data that was serialized by the writer and saved to the file, that
exact data is correctly deserialized by the reader and printed accordingly.
With the project structure out of the way, let us generate the code for Greet class:
Now, let us write the writer of the data, which will take the username and the
greeting as its input:
import sys
14
greet = greeting_pb2.Greet()
greet.username = sys.argv[1]
greet.greeting = sys.argv[2]
filename = "greeting_protobuf_output";
print("Saving to file: " + filename)
f = open(filename, "wb")
f.write(greet.SerializeToString())
f.close()
The writer simply takes CLI arguments, creates the Greet object, serializes it, and
then dumps it to a file.
greet = greeting_pb2.Greet()
filename = "greeting_protobuf_output";
print("Reading from file: " + filename)
f = open(filename, "rb")
greet.ParseFromString(f.read())
f.close()
The reader simply reads from the same file, deserializes it, and prints the data
about the greeting.
15
Now, let us first execute the writer.
python greetReader.py
So, as we see, the data that was serialized by the writer and saved to a file. Next,
the same data is correctly deserialized by the reader and printed accordingly.
16
3. Protobuf – Constructs
Let us now look at a few basic data structures and data types which Google
Protobuf provides. We will look at these data structures using an example of a
Movie theater.
Note that for this structure while we will be using Java code, using them in Python
code should also be equally simple and possible.
In the next few chapters, we will discuss the following Protobuf data types one by
one:
Protobuf Class/Member
Protobuf Strings
Protobuf Numbers
Protobuf Boolean
Protobuf Enum
Protobuf List/Repeated
Protobuf Map
Protobuf Nested Class
17
4. Protobuf – Class/Member
The very basic building block of Protobuf is the member attribute. This translates
to a class in the languages that we use, for example, Java, Python, etc.
Following is the syntax that we need to have to instruct Protobuf that we will be
creating instances of a given class:
syntax = "proto3";
package theater;
message Theater {
We will save the above in "theater.proto" and we will use this when we explore
other data structures.
The "syntax" here represents what version of Protobuf are we using. So, we are
using the latest version 3 and the schema thus can use all the syntax which is valid
for version 3.
syntax = "proto3";
The package here is used for conflict resolution, if, say, we have multiple
class/message with the same name.
package tutorial;
This argument is specific to Java, i.e., the package where the code from the
".proto" file will be auto-generated.
18
Now that we are done with the prerequisites, the last item here is:
message Theater
This is nothing but the class name of the base class for the object which would be
created/recreated. Note that it is useless in its current shape, as it does not have
any other attributes. But we will be more adding attributes as we move along.
A single proto file can also have multiple classes/messages. For example, if we
want, we can add a Visitors message/class as well in the same file. Protobuf
would ensure to create two separate and independent classes for the same. For
example:
syntax = "proto3";
package theater;
message Theater {
}
message Visitor {
To use Protobuf, we will now have to use protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
Well, that is it! The above command should create the required files and now we
can use it in our Java code:
At this stage, it is not very useful, as we have not added any attributes to the
members/classed. Let us do that when we look at strings.
19
5. Protobuf – Strings
Protobuf strings translate to a string in the languages that we use, for example,
Java, Python, etc. Continuing on the theater example, following is the syntax that
we need to have to instruct Protobuf that we will be creating a string:
syntax = "proto3";
package theater;
message Theater {
string name = 1;
string address = 2;
}
Now our class/message contains two string attributes. Each of them also has a
position which is what Protobuf uses while serialization and deserialization. Each
attribute of a member needs to have a unique position attribute.
To use Protobuf, we will now have to use protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command should create the required files and now we can use it in our
Java code. First let's create a writer to write the theater information:
package com.tutorialspoint.theater;
import java.io.FileOutputStream;
import java.io.IOException;
20
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.greeting.Greeting.Greet;
21
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
System.out.println(theater);
}
}
}
Now, let us execute the reader to read from the same file:
22
Reading from file theater_protobuf_output
name: "Silver Screener"
address: "212, Maple Street, LA, California"
So, as we see, we are able to read the serialized strings by deserializing the binary
data to the Theater object. Let us now look at numbers in the next chapter.
23
6. Protobuf – Numbers
Numbers include protobuf types like int32, int64, float, double, which are basic
building blocks of Protobuf. It translates to int, long float, double, respectively, in
the languages that we use, for example, Java, Python, etc.
Continuing with our theater example, following is the syntax that we need to have
to instruct Protobuf that we will be creating numbers:
syntax = "proto3";
package theater;
message Theater {
int32 total_capcity = 3;
int64 mobile = 4;
float base_ticket_price = 5;
}
Now our class/message contains numerical attributes. Each of them also has a
position which is what Protobuf uses while serialization and deserialization. Each
attribute of a member needs to have a unique number assigned.
To use Protobuf, we will now have to use protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command will create the required files and now we can use it in our
Java code. First, let's create a writer to write the theater information:
package com.tutorialspoint.theater;
24
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
package com.tutorialspoint.theater;
import java.io.FileInputStream;
25
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
26
total_capcity: 320
mobile: 98234567189
base_ticket_price: 22.45
Now, let us execute the reader to read from the same file:
So, as we see, we are able to read the serialized int, float, and long by
deserializing the binary data to Theater object. In the next chapter, we will look at
the Boolean type.
27
7. Protobuf – Boolean
The "bool" data type is one of the basic building blocks of Protobuf. It translates to
Boolean in the languages that we use, for example, Java, Python, etc.
Continuing with the theater example, following is the syntax that we need to have
to instruct Protobuf that we will be creating a Boolean attribute:
syntax = "proto3";
package theater;
message Theater {
bool drive_in = 6;
}
Now our message class contains a Boolean attribute. It also has a position which
is what Protobuf uses while serialization and deserialization. Each attribute of a
member needs to have a unique number assigned.
To use Protobuf, we will now have to use protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command will create the required files and now we can use it in our
Java code. First let's create a writer to write the theater information:
package com.tutorialspoint.theater;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
28
public class TheaterWriter{
public static void main(String[] args) throws IOException {
Theater theater = Theater.newBuilder()
.setTotalCapcity(320)
.setMobile(98234567189L)
.setBaseTicketPrice(22.45f)
.build();
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
29
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
Now, let us execute the reader to read from the same file:
30
Reading from file theater_protobuf_output
drive_in: true
So, as we see, we are able to read the serialized Boolean by deserializing the
binary data to Theater object.
31
8. Protobuf – Enum
Continuing with our theater example, following is the syntax that we need to have
to instruct Protobuf that we will be creating an enum:
syntax = "proto3";
package theater;
message Theater {
enum PAYMENT_SYSTEM{
CASH = 0;
CREDIT_CARD = 1;
DEBIT_CARD = 2;
APP = 3;
}
PAYMENT_SYSTEM payment = 7;
}
Now our message class contains an Enum for payment. Each of them also has a
position which is what Protobuf uses while serialization and deserialization. Each
attribute of a member needs to have a unique number assigned.
We define the enum and use it below as the data type along with "payment"
attribute. Note that although we have defined enum inside the message class, it
can also reside outside of it.
32
To use Protobuf, we will now have to use protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command should create the required files and now we can use it in our
Java code. First, we will create a writer to write the theater information:
package com.tutorialspoint.theater;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.PAYMENT_SYSTEM;
33
Next, we have a reader to read the theater information:
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
34
Now, post compilation, let us execute the writer first:
Now, let us execute the reader to read from the same file:
So, as we see, we are able to read the serialized enum by deserializing the binary
data to Theater object. In the next chapter, we will take a look at Protobuf lists.
35
9. Protobuf – List/Repeated
Lists are one of the composite datatypes of Protobuf. Protobuf translates this to a
java.util.list interface in Java.
Continuing with our theater example, following is the syntax that we need to have
to instruct Protobuf that we will be creating a list:
syntax = "proto3";
package theater;
message Theater {
repeated string snacks = 8;
}
Now our message class contains a list for snacks. Note that although we have a
string list, we can as well have number, Boolean, custom data type list.
To use Protobuf, we will now have to use protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command should create the required files and now we can use it in our
Java code. First, we will have a writer to write the theater information:
package com.tutorialspoint.theater;
import java.util.List;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
36
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
}
}
package com.tutorialspoint.theater;
37
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
System.out.println(theater);
}
}
}
38
snacks: "Popcorn"
snacks: "Coke"
snacks: "Chips"
snacks: "Soda"
Now, let us execute the reader to read from the same file:
So, as we see, we are able to read the serialized list by deserializing the binary
data to Theater object. In the next chapter, we will look at the map data type of
Protobuf.
39
10. Protobuf – Map
Continuing with our theater example, following is the syntax that we need to have
to instruct Protobuf that we will be creating a map:
syntax = "proto3";
package theater;
message Theater {
map<string, int32> movieTicketPrice = 9;
}
Now our class/message contains a map of movie and their ticket price. Note that
although we have "string -> int" map, we can as well have number, Boolean, and
custom data types. However, note that we cannot have a nested map.
To use Protobuf, we will now have to use the protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command will create the required files and now we can use it in our
Java code. First, we will create a writer to write the theater information:
package com.tutorialspoint.theater;
import java.util.List;
import java.util.Map;
import java.io.FileOutputStream;
import java.io.IOException;
40
import java.util.ArrayList;
import java.util.HashMap;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
}
}
41
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
42
movieTicketPrice {
key: "Avengers Endgame"
value: 700
}
movieTicketPrice {
key: "Captain America"
value: 200
}
movieTicketPrice {
key: "Wonder Woman 1984"
value: 400
}
Now, let us execute the reader to read from the same file:
So, as we see, we are able to read the serialized map by deserializing the binary
data to Theater object. In the next chapter, we will see how to create a nested
class in Protobuf.
43
11. Protobuf – Nested Class
Here, we will see how to create a nested class. Protobuf translates this to a nested
Java class.
Continuing with the theater example, following is the syntax that we need to have
to instruct Protobuf that we will be creating a nested class:
syntax = "proto3";
package theater;
message Theater {
TheaterOwner owner = 10;
}
message TheaterOwner{
string name = 1;
string address = 2;
}
Now our class/message contains a nested class, i.e., information about the owner
of the theater.
To use Protobuf, we will now have to use the protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command should create the required files and now we can use it in our
Java code. First, let's create a writer to write the theater information:
package com.tutorialspoint.theater;
44
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.TheaterOwner;
.build();
45
Next, we have a reader to read the theater information:
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
46
owner {
name: "Anthony Gonsalves"
address: "513, St Paul Street, West Coast, California"
}
Now, let us execute the reader to read from the same file:
So, as we see, we are able to read the serialized nested class data by deserializing
the binary data to Theater object.
47
12. Protobuf – Optionality and Defaults
While we looked at various data types and how to use them. What happens if we
do not specify the values while serialization? Google Protobuf 2 supported
"required" and "optional" tag which helped in figuring out if the
serialization/deserialization should fail if the required parsing logic is unavailable.
But these tags are not available in the latest version. The failing part needs to be
handled by respective code.
So, if one does not specify the data for these data types, then they would take the
above default values. Now, let's continue with our theater example to demonstrate
how it works.
In this example, we will let all the fields default. The only field which would be
specified would be the name of the theater.
syntax = "proto3";
package theater;
message Theater {
48
string name = 1;
string address = 2;
int32 total_capcity = 3;
int64 mobile = 4;
float base_ticket_price = 5;
bool drive_in = 6;
enum PAYMENT_SYSTEM{
CASH = 0;
CREDIT_CARD = 1;
DEBIT_CARD = 2;
APP = 3;
}
PAYMENT_SYSTEM payment = 7;
message TheaterOwner{
string name = 1;
string address = 2;
}
49
Now our class/message contains multiple attributes. To use Protobuf, we will
have to use protoc binary to create the required classes from this ".proto" file. Let
us see how to do that:
The above command should create the required files and now we can use it in our
Java code. First, let's create a writer to write the theater information:
package com.tutorialspoint.theater;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
}
}
50
Next, we will have a reader to read the theater information:
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
51
"Total Capacity:" +
theater.getTotalCapcity() + "\n" +
"Base Ticket Prices: " +
theater.getBaseTicketPrice() + "\n" +
"Owner: " + theater.getOwner() + "\n" +
"Snacks: " + theater.getSnacksList() + "\n" +
"Payment: " + theater.getPayment()
);
Now, let us execute the reader to read from the same file:
52
Base Ticket Prices: 0.0
Owner:
Snacks: []
Payment: CASH
List of fields explicitly specified:
{theater.Theater.name=SilverScreen}
So, as we see, all the values defaulted accordingly apart from name which we
have explicitly specified as seen in the bottommost line.
53
13. Protobuf – Language Independence
Till now, we have been using Java to serialize and deserialize the Movie Theater
data. However, one of the key features that Google Protobuf provides is "language
independence". In this chapter, we will see how to serialize using Java and
deserialize using Python.
syntax = "proto3";
package theater;
message Theater {
string name = 1;
string address = 2;
int32 total_capcity = 3;
int64 mobile = 4;
float base_ticket_price = 5;
bool drive_in = 6;
enum PAYMENT_SYSTEM{
CASH = 0;
CREDIT_CARD = 1;
DEBIT_CARD = 2;
APP = 3;
}
54
PAYMENT_SYSTEM payment = 7;
message TheaterOwner{
string name = 1;
string address = 2;
}
The above command should create the required files and now we can use it in our
Java code. First, we will create a writer to write the theater information:
package com.tutorialspoint.theater;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
55
import
com.tutorialspoint.theater.TheaterOuterClass.TheaterOwner;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.PAYMENT_SYS
TEM;
56
.setBaseTicketPrice(22.45f)
.setPayment(PAYMENT_SYSTEM.CREDIT_CARD)
.putAllMovieTicketPrice(ticketPrice)
.addAllSnacks(snacks)
.setOwner(owner)
.build();
57
snacks: "Popcorn"
snacks: "Coke"
snacks: "Chips"
snacks: "Soda"
movieTicketPrice {
key: "Avengers Endgame"
value: 700
}
movieTicketPrice {
key: "Captain America"
value: 200
}
movieTicketPrice {
key: "Wonder Woman 1984"
value: 400
}
owner {
name: "Anthony Gonsalves"
address: "513, St Paul Street, West Coast, California"
}
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.Descriptors.FieldDescriptor;
58
import com.google.protobuf.Descriptors.FileDescriptor;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
59
+ theater.getAllFields());
}
}
}
Output:
Saving theater information to file: theater_protobuf_output
Saved theater information with following data to disk:
name: "SilverScreen"
To use Protobuf with Python, we will now have to use protoc binary to create the
required classes from this ".proto" file. Let us see how to do that:
The above command should create the required files and now we can use it in our
Python code. Now, let us write a Python reader:
path = Path()
filename =
str(path.parent.absolute().parent.joinpath("java").joinpath("the
ater_protobuf_output"));
print("Reading from file: " + filename)
theater = theater_pb2.Theater()
f = open(filename, "rb")
theater.ParseFromString(f.read())
f.close()
60
We read the theater_protobuf_output file which is generated in the Java
directory. Now, let us execute the code:
python theaterReader.py
61
}
So, as we see, all the values which were written by the Java client were correctly
deserialized and read by our Python client which effectively means Protobuf is
language independent.
62
14. Protobuf – Compound Data Types
There are two more compound data types which may be useful for complicated
use cases. They are "OneOf" and "Any". In this chapter, we will see how to use
these two data types of Protobuf.
OneOf
We pass a few parameters to this OneOf data type and Protobuf ensures that only
one of them is set. If we set one of them and try to set the other one, the first
attribute gets reset. Let's us understand this via an example.
Continuing with our theater example, say, we have an API which is used to fetch
the count of available employees. The value returned from this API is then set to
'count' tag in the following file. But if that API errors out, we can't really 'count',
instead we attach the error log.
Ideally, we will always have one of them set, i.e., either the call is successful and
we get the count OR the count calculation fails and we get the error message.
Following is the syntax that we need to have to instruct Protobuf that we will be
creating an OneOf attribute:
syntax = "proto3";
package theater;
message Theater {
string name = 1;
string address = 2;
oneof availableEmployees{
63
int32 count = 4;
string errorLog = 5;
}
}
Now our class/message contains an OneOf attribute, i.e., information about the
available employees.
To use Protobuf, we will have to use protoc binary to create the required classes
from this ".proto" file. Let us see how to do that:
protoc --java_out=java/src/main/java
proto_files\theater_advanced.proto
The above command should create the required files and now we can use it in our
Java code. First, we will create a writer to write the theater information:
package com.tutorialspoint.theater;
import java.util.List;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import com.google.protobuf.Any;
import com.tutorialspoint.theater.TheaterAdvanced.Employee;
import com.tutorialspoint.theater.TheaterAdvanced.Viewer;
import com.tutorialspoint.theater.TheaterAdvanced.Theater;
people.add(Any.pack(Employee.newBuilder().setName("John").build(
)));
64
people.add(Any.pack(Viewer.newBuilder().setName("Jane").setAge(3
0).build()));
people.add(Any.pack(Employee.newBuilder().setName("Simon").build
()));
people.add(Any.pack(Viewer.newBuilder().setName("Janice").setAge
(25).build()));
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.IOException;
import com.google.protobuf.Any;
import com.tutorialspoint.theater.TheaterAdvanced.Theater;
65
import
com.tutorialspoint.theater.TheaterAdvanced.Theater.AvailableEmpl
oyeesCase;
import
com.tutorialspoint.theater.TheaterAdvanced.Theater.Builder;
import com.tutorialspoint.theater.TheaterAdvanced.Viewer;
import com.tutorialspoint.theater.TheaterAdvanced.Employee;
if(anyPeople.is(Viewer.class)) {
Viewer viewer =
anyPeople.unpack(Viewer.class);
System.out.println("Viewer:" + viewer + "\n");
}
}
}
}
66
}
Now, let us execute the reader to read from the same file:
67
Employee:name: "John"
Viewer:name: "Jane"
age: 30
Employee:name: "Simon"
Viewer:name: "Janice"
age: 25
So, as we see, in the list, we are able to figure out the Any type and find the
respective underlying datatype employee/viewer. Let us now look at defaults and
AnyOf.
Any
The next data type that can be of use for complicated uses cases is Any. We can
pass any type/message/class to this data type and Protobuf would not complain.
Let us understand this via an example.
Continuing with the theater example, say, we want to track people inside the
theater. Some of them could be employees and others could be viewers. But
ultimately they are people, so we will pass them in a single list which would contain
both the types.
Following is the syntax that we need to have to instruct Protobuf that we will be
creating a list:
syntax = "proto3";
package theater;
import "google/protobuf/any.proto";
message Theater {
68
string name = 1;
string address = 2;
message Employee{
string name = 1;
string address = 2;
}
message Viewer{
string name = 1;
int32 age = 2;
string sex = 3;
}
Now our class/message contains an Any attribute 'peopleInside' list along with
Viewer and Employee class, i.e., information about the people inside theater. Let
us see this in action.
To use Protobuf, we will now have to use protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
protoc --java_out=java/src/main/java
proto_files\theater_advanced.proto
The above command should create the required files and now we can use it in our
Java code. First, we will create a writer to write the theater information:
package com.tutorialspoint.theater;
import java.util.List;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
69
import com.google.protobuf.Any;
import com.tutorialspoint.theater.TheaterAdvanced.Employee;
import com.tutorialspoint.theater.TheaterAdvanced.Viewer;
import com.tutorialspoint.theater.TheaterAdvanced.Theater;
people.add(Any.pack(Employee.newBuilder().setName("John").build(
)));
people.add(Any.pack(Viewer.newBuilder().setName("Jane").setAg
e(30).build()));
people.add(Any.pack(Employee.newBuilder().setName("Simon").build
()));
people.add(Any.pack(Viewer.newBuilder().setName("Janice").setAge
(25).build()));
70
System.out.println("Saved theater information with
following data to disk: \n" + theater);
}
}
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.IOException;
import com.google.protobuf.Any;
import com.tutorialspoint.theater.TheaterAdvanced.Theater;
import
com.tutorialspoint.theater.TheaterAdvanced.Theater.AvailableEmpl
oyeesCase;
import
com.tutorialspoint.theater.TheaterAdvanced.Theater.Builder;
import com.tutorialspoint.theater.TheaterAdvanced.Viewer;
import com.tutorialspoint.theater.TheaterAdvanced.Employee;
71
Theater theater =
theaterBuilder.mergeFrom(input).build();
if(anyPeople.is(Viewer.class)) {
Viewer viewer = anyPeople.unpack(Viewer.class);
}
}
}
}
}
72
}
peopleInside {
type_url: "type.googleapis.com/theater.Viewer"
value: "\n\004Jane\020\036"
}
peopleInside {
type_url: "type.googleapis.com/theater.Employee"
value: "\n\005Simon"
}
peopleInside {
type_url: "type.googleapis.com/theater.Viewer"
value: "\n\006Janice\020\031"
}
Now let us execute the reader to read from the same file:
Employee:name: "John"
Viewer:name: "Jane"
age: 30
73
Employee:name: "Simon"
Viewer:name: "Janice"
age: 25
74
15. Protobuf – Command Line Usage
Protobuf serializes the data and stores it in a binary format. While this may not be
a problem if we are dealing simply with strings, because ultimately Protobuf uses
UTF-8. So, any text that it stores would be human readable if you are using a
UTF-8 enabled reader. However, things like int32, Boolean, list, maps are
encoded using specific techniques to reduce space consumption.
syntax = "proto3";
package tutorial;
message Greet {
string greeting = 1;
string username = 2;
int32 age = 3;
}
greeting: "Yo"
username : "John"
age : 50
75
If we look at what is inside this file or cat this file:
cat .\encoded_greeting
☻Yo↕♦John↑2
You will notice some weird characters apart from "Yo" and "John". That is because
these encoding may not be a valid unicode/UTF-8 encoding. UTF-8 is what is
used, generally speaking, at most of the places. And this is used for string in case
of Protobuf, but ints, maps, Boolean, list have separate formats. Plus, this file
also contains a metadata of the data.
That is why, we need a decoder/deserializer to read this data. Let us use that.
greeting: "Yo"
username : "John"
age : 50
So, as we see, we are able to get the data back which was serialized and looked
weird in the file.
76
16. Protobuf – Rules to Update Definition
Assume you came out with the definition of the proto file that you will use in the
production environment. There will obviously be times in future when this definition
would have to change. In that case, it is essential that the changes we make
adhere to certain rules so that the changes are backwards compatible. Let us see
this in action with a few do's and dont's.
Add a new field in the writer, while the reader retains the older version of code.
Suppose, you decide to add a new field. Ideally, to have the new field to be added,
we will have to update the writer and the reader simultaneously. However, in a
large-scale deployment, this is not possible. There will be cases where the writer
has been updated, but the reader is yet to be updated with the new field. This is
where the above situation occurs. Let us see that in action.
Continuing with our theater example, say, we just have a single tag which is
'name' in our proto file. Following is the syntax that we need to have to instruct
Protobuf:
syntax = "proto3";
package theater;
message Theater {
string name = 1;
}
To use Protobuf, we will now have to use the protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command should create the required files and now we can use it in our
Java code. First, we will create a writer to write the theater information:
77
package com.tutorialspoint.theater;
package com.tutorialspoint.theater;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
78
import java.io.IOException;
import com.google.protobuf.ProtocolStringList;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
79
Now let us execute the reader to read from the same file:
Unknown Fields
We just wrote a simple string as per our Protobuf definition and the reader was
able to read the string. And we also saw that there were no unknown fields that the
reader was not aware of.
But now, let us suppose we want add a new string 'address' to our Protobuf
definition. Now, it will look like this:
syntax = "proto3";
package theater;
message Theater {
string name = 1;
string address = 2;
}
Before compiling, rename the JAR from the previous compilation to protobuf-
tutorial-old-1.0.jar. And then compile.
80
> java -cp .\target\protobuf-tutorial-1.0.jar
com.tutorialspoint.theater.TheaterWriter
Now let us execute the reader to read from the same file but from the older JAR:
As you can see from the last line of the output, the old reader is unaware of the
address field which was added by the new writer. It just shows how a combination
of "new writer - old reader" functions.
Deleting a Field
Suppose, you decide to delete an existing field. Ideally, for the deleted field to have
an effect immediately, we will have to update the writer and the reader
simultaneously. However, in a large-scale deployment, this is not possible. There
will be cases where the writer has been updated, but the reader is yet to be
updated. In such a case, the reader will still attempt to read the deleted field. Let
us see that in action.
Continuing with the theater example, say, we just have two tags in our proto file.
Following is the syntax that we need to have to instruct Protobuf:
syntax = "proto3";
81
package theater;
message Theater {
string name = 1;
string address = 2;
}
To use Protobuf we will now have to use the protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command should create the required files and now we can use it in our
Java code. First, we will create a writer to write the theater information:
package com.tutorialspoint.theater;
package com.tutorialspoint.theater;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
82
try(FileOutputStream output = new
FileOutputStream(filename)){
theater.writeTo(output);
}
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.google.protobuf.ProtocolStringList;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
83
Theater theater =
theaterBuilder.mergeFrom(input).build();
System.out.println(theater);
System.out.println("Unknwon fields: " +
theater.getUnknownFields());
}
}
}
Now let us execute the reader to read from the same file:
So, nothing new here, we just wrote a simple string as per our Protobuf definition
and the reader was able to read the string.
But now, let us suppose we want to delete the string 'address' from our Protobuf
definition. So, the definition would look like this:
syntax = "proto3";
84
package theater;
message Theater {
string name = 1;
}
Before compiling, rename the JAR from the previous compilation to protobuf-
tutorial-old-1.0.jar. And then compile.
Now let us execute the reader to read from the same file but from the older JAR:
As you can see from the last line of the output, the old reader defaults to the value
of "address". It shows how a combination of "new writer - old reader" functions.
85
Avoid Reusing Serial Number of the Field
There may be cases where, by mistake, we update the "serial number" of a field.
This can be problematic, as the serial number is very critical for Protobuf to
understand and deserialize the data. And some old reader may be relying on this
serial number to deserialize the data. So, it is recommended that you:
Continuing with the theater example, let's assume we just have two tags in our
proto file. Following is the syntax that we need to have to instruct Protobuf:
syntax = "proto3";
package theater;
message Theater {
string name = 1;
string address = 2;
}
To use Protobuf, we will now have to use the protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command should create the required files and now we can use it in our
Java code. First, we will create a writer to write the theater information:
package com.tutorialspoint.theater;
package com.tutorialspoint.theater;
import java.io.FileOutputStream;
86
import java.io.IOException;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.google.protobuf.ProtocolStringList;
import com.tutorialspoint.greeting.Greeting.Greet;
87
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
Next, let us execute the reader to read from the same file:
88
name: "Silver Screener"
address: "212, Maple Street, LA, California"
Here, we just wrote simple strings as per our Protobuf definition and the reader
was able to read the string. But now, let us interchange the serial number in our
Protobuf definition and to make it like this:
syntax = "proto3";
package theater;
message Theater {
string name = 2;
string address = 1;
}
Now let us execute the reader to read from the same file but from the older JAR:
89
address: "Silver Screener"
As you can see from the output, the old reader interchanged the address and the
name. It shows that updating the serial number along with a combination of "new
writer-old reader" does not function as expected.
More importantly, here we had two strings, which is why we get to see the data. If
we had used different data types, for example, int32, Boolean, map, etc., Protobuf
would have given up and treated that as an unknown field.
So, it is imperative to not change the serial number of a field or reuse the serial
number of a deleted field.
string and bytes are compatible if the bytes are UTF-8. This is because,
strings are anyways encoded/decoded as UTF-8 by Protobuf.
enum is compatible with int32 and int64 in terms of the value, however,
the client may not deserialize this as expected.
int32, int64 (unsigned also) along with bool are compatible and thus can
be interchanged. Excessive characters may get truncated similar to how
casting works in languages.
But we need to be very careful when changing types. Let us see that in action with
an incorrect example of converting int64 to int32.
Continuing with the theater example, suppose we just have two tags in our proto
file. Following is the syntax that we need to have to instruct Protobuf:
syntax = "proto3";
package theater;
message Theater {
90
string name = 1;
int64 total_capacity = 2;
}
To use Protobuf, we will now have to use the protoc binary to create the required
classes from this ".proto" file. Let us see how to do that:
The above command should create the required files and now we can use it in our
Java code. First, we will create a writer to write the theater information:
package com.tutorialspoint.theater;
import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
91
}
}
Let us suppose, we use a different version of proto file for the reader:
syntax = "proto3";
package theater;
message Theater {
string name = 1;
int64 total_capacity = 2;
}
package com.tutorialspoint.theater;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.google.protobuf.ProtocolStringList;
import com.tutorialspoint.greeting.Greeting.Greet;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
92
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder;
Now let us execute the reader to read from the same file:
So, nothing new here, we just wrote simple strings as per our Protobuf definition
and the reader was able to read the string. But now, let us interchange the serial
number in our Protobuf definition and make it like this:
syntax = "proto3";
93
package theater;
message Theater {
string name = 2;
int32 total_capacity = 2;
}
As you can see from the output, the old reader converted the number from int64,
however, the given int32 does not have enough space to contain the data, it
wrapped around to negative number. This wrapping is Java specific and is not
related to Protobuf.
So, we need to upgrade to int64 from int32 instead of other way around. If we still
want to convert from int64 to int32, we need to ensure that the values can be
actually held in 31 bits (1 bit for sign bit).
94
17. Protobuf – Integration with Kafka
We have covered quite a lot of examples of Protobuf and its data types. In this
chapter, let us take another example and see how Protobuf integrates with a
Schema Registry used by Kafka. Let us first understand what a "schema registry"
is.
Schema Registry
Kafka is one of the widely used messaging queues. It is used to apply the
publisher-subscriber model at scale. More information about Kafka can be found
here: https://www.tutorialspoint.com/apache_kafka/index.htm
Efficient encoding: Sending in a field name, its type with every message
is space and compute inefficient. With schemas in place, we do not need
to send this information with each message.
The schema registry supports Avro, Google Protobuf and JSON Schema as the
schema language. The schema in these languages can be stored in the schema
registry. For this tutorial, we would require Kafka setup and Schema registry setup.
95
https://docs.confluent.io/platform/current/installation/installing_cp/deb-
ubuntu.html#systemd-ubuntu-debian-install
Once you have Kafka installed, you can then setup the Schema Registry by
updating the /etc/schema-registry/schema-registry.properties file.
With the setup out of the way, let us start using Google Protobuf along with the
Schema Registry.
syntax = "proto3";
package theater;
message Theater {
string name = 1;
string address = 2;
int32 total_capcity = 3;
int64 mobile = 4;
float base_ticket_price = 5;
96
bool drive_in = 6;
enum PAYMENT_SYSTEM{
CASH = 0;
CREDIT_CARD = 1;
DEBIT_CARD = 2;
APP = 3;
}
PAYMENT_SYSTEM payment = 7;
Now, let us create a simple Kafka writer which would write the message encoded
in this format to the Kafka topic. But for doing that, first, we need to add a few
dependencies to our Maven POM:
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.5.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.confluent/kafka-
protobuf-serializer -->
<dependency>
97
<groupId>io.confluent</groupId>
<artifactId>kafka-protobuf-serializer</artifactId>
<version>5.5.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-
simple -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
</dependency>
Once this is done, let us now create a Kafka producer. This producer will create
and send a message which will contain the theater object.
package com.tutorialspoint.kafka;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.PAYMENT_SYS
TEM;
98
String topicName = "testy1";
props.put("bootstrap.servers", "localhost:9092");
props.put("clientid", "foo");
props.put("key.serializer",
"org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer",
"io.confluent.kafka.serializers.protobuf.KafkaProtobufSerializer
");
props.put("schema.registry.url", "http://localhost:8081");
props.put("auto.register.schemas", "true");
99
snacks.add("Chips");
snacks.add("Soda");
return theater;
}
}
100
Let us now compile and execute the code:
Sent to Kafka:
101
value: 200
}
movieTicketPrice {
key: "Wonder Woman 1984"
value: 400
}
Now, let us confirm that the schema has been stored in the Schema Registry.
[
"testy1-value"
]
{
"schemaType": "PROTOBUF",
"schema": "syntax = \"proto3\";\npackage theater;\n\noption
java_package = \"com.tutorialspoint.theater\";\n\nmessage
Theater {\n string name = 1;\n string address = 2;\n int64
total_capacity = 3;\n int64 mobile = 4;\n float
base_ticket_price = 5;\n bool drive_in = 6;\n
.theater.Theater.PAYMENT_SYSTEM payment = 7;\n repeated string
snacks = 8;\n repeated .theater.Theater.MovieTicketPriceEntry
movieTicketPrice = 9;\n\n message MovieTicketPriceEntry {\n
option map_entry = true;\n \n string key = 1;\n int32
value = 2;\n }\n enum PAYMENT_SYSTEM {\n CASH = 0;\n
CREDIT_CARD = 1;\n DEBIT_CARD = 2;\n APP = 3;\n }\n}\n"
}
102
Kafka Consumer with Protobuf Schema
Let us now create a Kafka consumer. This consumer will consume the message
which contains the theater object.
package com.tutorialspoint.kafka;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import com.tutorialspoint.theater.TheaterOuterClass.Theater;
import
com.tutorialspoint.theater.TheaterOuterClass.Theater.PAYMENT_SYS
TEM;
props.put("bootstrap.servers", "localhost:9092");
props.put("clientid", "foo");
props.put("key.serializer",
"org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer",
103
"io.confluent.kafka.serializers.protobuf.KafkaProtobufSerializer
");
props.put("schema.registry.url", "http://localhost:8081");
props.put("auto.register.schemas", "true");
104
.setAddress("212, Maple Street, LA, California")
.setDriveIn(true)
.setTotalCapacity(320)
.setMobile(98234567189L)
.setBaseTicketPrice(22.45f)
.setPayment(PAYMENT_SYSTEM.CREDIT_CARD)
.putAllMovieTicketPrice(ticketPrice)
.addAllSnacks(snacks)
.build();
return theater;
}
}
The Schema Registry would automatically read the stored schema of the
theater object when we are done consuming.
105
address: "212, Maple Street, LA, California"
total_capacity: 320
mobile: 98234567189
base_ticket_price: 22.45
drive_in: true
payment: CREDIT_CARD
snacks: "Popcorn"
snacks: "Coke"
snacks: "Chips"
snacks: "Soda"
movieTicketPrice {
key: "Captain America"
value: 200
}
movieTicketPrice {
key: "Wonder Woman 1984"
value: 400
}
movieTicketPrice {
key: "Avengers Endgame"
value: 700
}
So, as we can see, the message which was written into Kafka was correctly
consumed by the Consumer. Plus, the Registry stored the schema which can also
be accessed by a REST API.
106
18. Protobuf – In Other Languages
We have been using Protobuf in Java and Python. But there are multiple
languages it supports including C++, C#, Kotlin, Dart, Go, etc. The basic stuff
mostly remains the same, i.e., writing a proto schema, generating the source code
via protoc binary which our code can use. Let us write a basic example for Go
and Dart as part of this section.
syntax = "proto3";
package tutorial;
message Greet {
string greeting = 1;
string username = 2;
}
go install google.golang.org/protobuf/cmd/protoc-gen-go
Then, run the protoc with the provided ".proto" file and we will instruct it to
generate the code under the "go" directory.
Post execution of the above command, you will notice an auto-generated class:
"greeting.pb.go". This class would help us with the serialization and
deserialization of the Greet object.
107
Now, let us create the writer of the data, which will take the username and
greeting as its input:
import "fmt"
import "io/ioutil"
func main() {
greet := Greeting{}
greet.username = "John"
greet.greeting = "Hello"
Now let us create the reader which will read the file:
import "fmt"
import "io/ioutil"
func main() {
in, err := ioutil.ReadFile("greeting_go_out")
greet := &pb.Greet{}
proto.Unmarshal(in, greet)
108
The reader simply reads from the same file, deserializes it, and prints the data
about the greeting.
Now that we have setup the reader and the writer, let us compile the project.
go run greeting_writer.go
go run greeting_reader.go
So, as we can see, the data that was serialized by the writer and saved to the file,
that exact data is correctly deserialized by the reader and printed accordingly.
Install the Dart Protobuf plugin (protoc-gen-go) which is the prerequisite for the
protoc file which we have been using.
https://github.com/dart-
lang/protobuf/tree/master/protoc_plugin#how-to-build-and-use
Then, run the protoc with the provided ".proto" file and we will instruct it to
generate the code under the "dart" directory.
Post execution of the above command, you will notice an auto-generated class:
"greeting.pb.dart". This class would help us with the serialization and
deserialization of the Greet object.
109
Now, let us create the writer of the data, which will take the username and
greeting as its input:
import 'dart:io';
import 'dart/greeting.pb.dart';
main(List arguments) {
import 'dart:io';
import 'dart/greeting.pb.dart';
main(List arguments) {
110
The reader simply reads from the same file, deserializes it, and prints the data
about the greeting.
Now that we have setup the reader and the writer, let us compile the project.
So, as we can see, the data that was serialized by the writer and saved to the file,
that exact data is correctly deserialized by the reader and printed accordingly.
111