
- Protocol Buffers - Home
- Protocol Buffers - Introduction
- Protocol Buffers - Environment Setup
- Protocol Buffers - Basic App
- Protocol Buffers - Constructs
- Protocol Buffers - message
- Protocol Buffers - string
- Protocol Buffers - Numbers
- Protocol Buffers - bool
- Protocol Buffers - enum
- Protocol Buffers - repeated
- Protocol Buffers - map
- Protocol Buffers - Nested Class
- Protocol Buffers - Optionality & Defaults
- Protocol Buffers - Language Independence
- Protocol Buffers - Compound Data Types
- Protocol Buffers - Command Line Usage
- Protocol Buffers - Rules to Update Definition
- Protocol Buffers - Integration with Kafka
- Protocol Buffers - In Other Languages
- Protocol Buffers Useful Resources
- Protocol Buffers - Quick Guide
- Protocol Buffers - Useful Resources
- Protocol Buffers - Discussion
Protocol Buffers - Optionality & Defaults
Overview
While we looked at various data types and how to use them. What happens if we do not specify the values while serialization? "proto2" version supported "required" and "optional" tag which helped in figuring out if the serialization/deserialization should fail if the required parsing logic is unavailable. But "required" tag is removed in the "proto3" version. The failing part needs to be handled by respective code. Now every attibute is optional and have default value. Thus use of "optional" is reduntant in "proto3" version onwards.
Protocol Buffers supports default values of its data types as per given table below −
Data Type | Default value |
---|---|
Int32 / Int64 | 0 |
Float/double | 0.0 |
String | Empty string |
Boolean | False |
Enum | First Enum item, that is the one with "index=0" |
Repeated type | Empty list |
Map | Empty Map |
Nested Class | null |
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.
Continuing with our theater example from Protocol Buffers - String chapter, following is the syntax that we need to have to instruct Protobuf that we will be creating different datatypes −
theater.proto
syntax = "proto3"; package theater; option java_package = "com.tutorialspoint.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; } PAYMENT_SYSTEM payment = 7; repeated string snacks = 8; map<string, int32> movieTicketPrice = 9; TheaterOwner owner = 10; } message TheaterOwner{ string name = 1; string address = 2; }
Now our message class contains multiple attributes.
Creating Java Classes from Proto File
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=. theater.proto
This will create a TheaterOuterClass.java class in com > tutorialspoint > theater folder in current directory. We're using this class in our application similar to as done in Protocol Buffers - Basic App chapter.
Using Java Classes created from Proto File
TheaterWriter.java
package com.tutorialspoint.theater; import java.io.FileOutputStream; import java.io.IOException; import com.tutorialspoint.theater.TheaterOuterClass.Theater; public class TheaterWriter { public static void main(String[] args) throws IOException { Theater theater = Theater.newBuilder() .setName("SilverScreen") .build(); String filename = "theater_protobuf_output"; System.out.println("Saving theater information to file: " + filename); try(FileOutputStream output = new FileOutputStream(filename)){ theater.writeTo(output); } System.out.println("Saved theater information with following data to disk: \n" + theater); } }
Next, we will have a reader to read the theater information −
TheaterReader.java
package com.tutorialspoint.theater; import java.io.FileInputStream; import java.io.IOException; import com.tutorialspoint.theater.TheaterOuterClass.Theater; import com.tutorialspoint.theater.TheaterOuterClass.Theater.Builder; public class TheaterReader{ public static void main(String[] args) throws IOException { Builder theaterBuilder = Theater.newBuilder(); String filename = "theater_protobuf_output"; System.out.println("Reading from file " + filename); try(FileInputStream input = new FileInputStream(filename)) { Theater theater = theaterBuilder.mergeFrom(input).build(); System.out.println( "Name:" + theater.getName() + "\n" + "Address:" + theater.getAddress() + "\n" + "Drive_In:" + theater.getDriveIn() + "\n" + "Total Capacity:" + theater.getTotalCapcity() + "\n" + "Base Ticket Prices: " + theater.getBaseTicketPrice() + "\n" + "Owner: " + theater.getOwner() + "\n" + "Snacks: " + theater.getSnacksList() + "\n" + "Payment: " + theater.getPayment() ); //Map<FieldDescriptor, Object> f = theater.getAllFields(); System.out.println("List of fields explicitly specified: " + theater.getAllFields()); } } }
Compile the project
Now that we have set up the reader and the writer, let us compile the project.
mvn clean install
Serialize the Java Object
Now, post compilation, let us execute the writer first −
> java -cp .\target\protobuf-tutorial-1.0.jar com.tutorialspoint.theater.TheaterWriter Saving theater information to file: theater_protobuf_output Saved theater information with following data to disk: name: "SilverScreen"
Deserialize the Serialized Object
Now, let us execute the reader to read from the same file −
java -cp .\target\protobuf-tutorial-1.0.jar com.tutorialspoint.theater.TheaterReader Reading from file theater_protobuf_output Name:SilverScreen Address: Drive_In:false Total Capacity:0 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 bottom most line.