Protobuf - Optionality & 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.

Let us look at the default values of the data types −

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.

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 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 −

protoc  --java_out=java/src/main/java proto_files\theater.proto

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;

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 −

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;

public class TheaterReaderExplicit{
   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());
      }
   }
}

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"

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.

Advertisements