Summary
Protocol Buffers is a method for serializing structured data developed by Google. The primary usage is to facilitate structured data exchange between different applications and systems.
Several data formats are used to exchange data, such as XML, CSV or JSON. They are still commonly used in web development, but each of them has disadvantages in some aspects. Protocol Buffers were developed to solve some of these drawbacks.
This article will mention the newest version, proto3.
Advantages
There are several reasons why using Protocol Buffers is a better choice rather than other options:
- Smaller data size
- Fast encoding and decoding
- Automatically generate code
- Support a variety of programming languages
Smaller data size
Protocol Buffers in a binary format, meaning that it uses only 0s and 1s to represent the data and does not include any extra metadata or formatting like XML or JSON. Additionally, Protocol Buffers' compact encoding method allows it to compress data more efficiently, resulting in smaller message sizes.
Fast encoding and decoding
Another benefit of Protocol Buffers is their speed. Because it is a binary format, it can be encoded and decoded much more quickly than text-based formats like XML and JSON, which require additional processing to parse the text and convert it into a usable format. This makes Protocol Buffers a good choice for systems that require fast and efficient data exchange.
Automatically generate code
Protocol Buffers are designed to be language- and platform-independent, meaning that they can be used with any programming language and on any platform that has a code generator for the format. This allows systems and applications written in different languages to communicate with each other using a common data exchange format, regardless of the programming language or platform they were developed on.
Support a variety of programming languages
The Protocol Buffers compiler, protoc
, supports several types of programming languages: C++, C♯, Java, Kotlin, Objective-C, PHP, Python, and Ruby.
Workflow
The workflow of Protocol Buffers is straightforward and easy to use. Firstly, you define the schema with a .proto
file, and the protocol compiler generates code to serialize and deserialize data in your application. Your application uses the generated code to send or receive the data.
Basic syntax
The schema definition file is called .proto
file, and it defines data schema.
syntax="proto3";
message Person {
int32 id = 1;
string name = 2
}
In this example, syntax="proto3"
specifies that the .proto
file is written in version 3 of Protocol Buffers. The message
keyword is used to define a new message type called Person
. Inside the Person
message type, there are two fields: id
and name
. int32
and string are the field types, and 1
and 2
are field tags. Field tags are used to identify each field in the binary data stream and are required for proper decoding of the data.
Common usage
Protocol Buffers is widely used in various applications and systems. One of its most popular use cases is in the gRPC framework, which is a modern, high-performance, open-source RPC (Remote Procedure Call) framework that uses Protocol Buffers as its default data format. gRPC allows developers to build distributed systems that communicate with each other using a simple and efficient RPC interface, making it a great choice for building microservices and other distributed applications.
Code Example
Finally, quickly check what the generated code looks like.
When the protocol buffer compiler generates a Java class code for this example .proto
file,
syntax="proto3";
message Person {
int32 id = 1;
string name = 2
}
The generated code will be here, click the collapse mark.
generated Java file
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: person.proto
public final class PersonOuterClass {
private PersonOuterClass() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
public interface PersonOrBuilder extends
// @@protoc_insertion_point(interface_extends:Person)
com.google.protobuf.MessageOrBuilder {
/**
* <code>int32 id = 1;</code>
* @return The id.
*/
int getId();
/**
* <code>string name = 2;</code>
* @return The name.
*/
java.lang.String getName();
/**
* <code>string name = 2;</code>
* @return The bytes for name.
*/
com.google.protobuf.ByteString
getNameBytes();
}
/**
* Protobuf type {@code Person}
*/
public static final class Person extends
com.google.protobuf.GeneratedMessageV3 implements
// @@protoc_insertion_point(message_implements:Person)
PersonOrBuilder {
private static final long serialVersionUID = 0L;
// Use Person.newBuilder() to construct.
private Person(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
super(builder);
}
private Person() {
name_ = "";
}
@java.lang.Override
@SuppressWarnings({"unused"})
protected java.lang.Object newInstance(
UnusedPrivateParameter unused) {
return new Person();
}
@java.lang.Override
public final com.google.protobuf.UnknownFieldSet
getUnknownFields() {
return this.unknownFields;
}
private Person(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
this();
if (extensionRegistry == null) {
throw new java.lang.NullPointerException();
}
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
com.google.protobuf.UnknownFieldSet.newBuilder();
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
case 8: {
id_ = input.readInt32();
break;
}
case 18: {
java.lang.String s = input.readStringRequireUtf8();
name_ = s;
break;
}
default: {
if (!parseUnknownField(
input, unknownFields, extensionRegistry, tag)) {
done = true;
}
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (com.google.protobuf.UninitializedMessageException e) {
throw e.asInvalidProtocolBufferException().setUnfinishedMessage(this);
} catch (java.io.IOException e) {
throw new com.google.protobuf.InvalidProtocolBufferException(
e).setUnfinishedMessage(this);
} finally {
this.unknownFields = unknownFields.build();
makeExtensionsImmutable();
}
}
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return PersonOuterClass.internal_static_Person_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internalGetFieldAccessorTable() {
return PersonOuterClass.internal_static_Person_fieldAccessorTable
.ensureFieldAccessorsInitialized(
PersonOuterClass.Person.class, PersonOuterClass.Person.Builder.class);
}
public static final int ID_FIELD_NUMBER = 1;
private int id_;
/**
* <code>int32 id = 1;</code>
* @return The id.
*/
@java.lang.Override
public int getId() {
return id_;
}
public static final int NAME_FIELD_NUMBER = 2;
private volatile java.lang.Object name_;
/**
* <code>string name = 2;</code>
* @return The name.
*/
@java.lang.Override
public java.lang.String getName() {
java.lang.Object ref = name_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
} else {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
name_ = s;
return s;
}
}
/**
* <code>string name = 2;</code>
* @return The bytes for name.
*/
@java.lang.Override
public com.google.protobuf.ByteString
getNameBytes() {
java.lang.Object ref = name_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
name_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
private byte memoizedIsInitialized = -1;
@java.lang.Override
public final boolean isInitialized() {
byte isInitialized = memoizedIsInitialized;
if (isInitialized == 1) return true;
if (isInitialized == 0) return false;
memoizedIsInitialized = 1;
return true;
}
@java.lang.Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
if (id_ != 0) {
output.writeInt32(1, id_);
}
if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(name_)) {
com.google.protobuf.GeneratedMessageV3.writeString(output, 2, name_);
}
unknownFields.writeTo(output);
}
@java.lang.Override
public int getSerializedSize() {
int size = memoizedSize;
if (size != -1) return size;
size = 0;
if (id_ != 0) {
size += com.google.protobuf.CodedOutputStream
.computeInt32Size(1, id_);
}
if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(name_)) {
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, name_);
}
size += unknownFields.getSerializedSize();
memoizedSize = size;
return size;
}
@java.lang.Override
public boolean equals(final java.lang.Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof PersonOuterClass.Person)) {
return super.equals(obj);
}
PersonOuterClass.Person other = (PersonOuterClass.Person) obj;
if (getId()
!= other.getId()) return false;
if (!getName()
.equals(other.getName())) return false;
if (!unknownFields.equals(other.unknownFields)) return false;
return true;
}
@java.lang.Override
public int hashCode() {
if (memoizedHashCode != 0) {
return memoizedHashCode;
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
hash = (37 * hash) + ID_FIELD_NUMBER;
hash = (53 * hash) + getId();
hash = (37 * hash) + NAME_FIELD_NUMBER;
hash = (53 * hash) + getName().hashCode();
hash = (29 * hash) + unknownFields.hashCode();
memoizedHashCode = hash;
return hash;
}
public static PersonOuterClass.Person parseFrom(
java.nio.ByteBuffer data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static PersonOuterClass.Person parseFrom(
java.nio.ByteBuffer data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static PersonOuterClass.Person parseFrom(
com.google.protobuf.ByteString data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static PersonOuterClass.Person parseFrom(
com.google.protobuf.ByteString data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static PersonOuterClass.Person parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static PersonOuterClass.Person parseFrom(
byte[] data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static PersonOuterClass.Person parseFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input);
}
public static PersonOuterClass.Person parseFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input, extensionRegistry);
}
public static PersonOuterClass.Person parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input);
}
public static PersonOuterClass.Person parseDelimitedFrom(
java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
}
public static PersonOuterClass.Person parseFrom(
com.google.protobuf.CodedInputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input);
}
public static PersonOuterClass.Person parseFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseWithIOException(PARSER, input, extensionRegistry);
}
@java.lang.Override
public Builder newBuilderForType() { return newBuilder(); }
public static Builder newBuilder() {
return DEFAULT_INSTANCE.toBuilder();
}
public static Builder newBuilder(PersonOuterClass.Person prototype) {
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
}
@java.lang.Override
public Builder toBuilder() {
return this == DEFAULT_INSTANCE
? new Builder() : new Builder().mergeFrom(this);
}
@java.lang.Override
protected Builder newBuilderForType(
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
Builder builder = new Builder(parent);
return builder;
}
/**
* Protobuf type {@code Person}
*/
public static final class Builder extends
com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
// @@protoc_insertion_point(builder_implements:Person)
PersonOuterClass.PersonOrBuilder {
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return PersonOuterClass.internal_static_Person_descriptor;
}
@java.lang.Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internalGetFieldAccessorTable() {
return PersonOuterClass.internal_static_Person_fieldAccessorTable
.ensureFieldAccessorsInitialized(
PersonOuterClass.Person.class, PersonOuterClass.Person.Builder.class);
}
// Construct using PersonOuterClass.Person.newBuilder()
private Builder() {
maybeForceBuilderInitialization();
}
private Builder(
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
super(parent);
maybeForceBuilderInitialization();
}
private void maybeForceBuilderInitialization() {
if (com.google.protobuf.GeneratedMessageV3
.alwaysUseFieldBuilders) {
}
}
@java.lang.Override
public Builder clear() {
super.clear();
id_ = 0;
name_ = "";
return this;
}
@java.lang.Override
public com.google.protobuf.Descriptors.Descriptor
getDescriptorForType() {
return PersonOuterClass.internal_static_Person_descriptor;
}
@java.lang.Override
public PersonOuterClass.Person getDefaultInstanceForType() {
return PersonOuterClass.Person.getDefaultInstance();
}
@java.lang.Override
public PersonOuterClass.Person build() {
PersonOuterClass.Person result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
@java.lang.Override
public PersonOuterClass.Person buildPartial() {
PersonOuterClass.Person result = new PersonOuterClass.Person(this);
result.id_ = id_;
result.name_ = name_;
onBuilt();
return result;
}
@java.lang.Override
public Builder clone() {
return super.clone();
}
@java.lang.Override
public Builder setField(
com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return super.setField(field, value);
}
@java.lang.Override
public Builder clearField(
com.google.protobuf.Descriptors.FieldDescriptor field) {
return super.clearField(field);
}
@java.lang.Override
public Builder clearOneof(
com.google.protobuf.Descriptors.OneofDescriptor oneof) {
return super.clearOneof(oneof);
}
@java.lang.Override
public Builder setRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field,
int index, java.lang.Object value) {
return super.setRepeatedField(field, index, value);
}
@java.lang.Override
public Builder addRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return super.addRepeatedField(field, value);
}
@java.lang.Override
public Builder mergeFrom(com.google.protobuf.Message other) {
if (other instanceof PersonOuterClass.Person) {
return mergeFrom((PersonOuterClass.Person)other);
} else {
super.mergeFrom(other);
return this;
}
}
public Builder mergeFrom(PersonOuterClass.Person other) {
if (other == PersonOuterClass.Person.getDefaultInstance()) return this;
if (other.getId() != 0) {
setId(other.getId());
}
if (!other.getName().isEmpty()) {
name_ = other.name_;
onChanged();
}
this.mergeUnknownFields(other.unknownFields);
onChanged();
return this;
}
@java.lang.Override
public final boolean isInitialized() {
return true;
}
@java.lang.Override
public Builder mergeFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
PersonOuterClass.Person parsedMessage = null;
try {
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
parsedMessage = (PersonOuterClass.Person) e.getUnfinishedMessage();
throw e.unwrapIOException();
} finally {
if (parsedMessage != null) {
mergeFrom(parsedMessage);
}
}
return this;
}
private int id_ ;
/**
* <code>int32 id = 1;</code>
* @return The id.
*/
@java.lang.Override
public int getId() {
return id_;
}
/**
* <code>int32 id = 1;</code>
* @param value The id to set.
* @return This builder for chaining.
*/
public Builder setId(int value) {
id_ = value;
onChanged();
return this;
}
/**
* <code>int32 id = 1;</code>
* @return This builder for chaining.
*/
public Builder clearId() {
id_ = 0;
onChanged();
return this;
}
private java.lang.Object name_ = "";
/**
* <code>string name = 2;</code>
* @return The name.
*/
public java.lang.String getName() {
java.lang.Object ref = name_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs =
(com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
name_ = s;
return s;
} else {
return (java.lang.String) ref;
}
}
/**
* <code>string name = 2;</code>
* @return The bytes for name.
*/
public com.google.protobuf.ByteString
getNameBytes() {
java.lang.Object ref = name_;
if (ref instanceof String) {
com.google.protobuf.ByteString b =
com.google.protobuf.ByteString.copyFromUtf8(
(java.lang.String) ref);
name_ = b;
return b;
} else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>string name = 2;</code>
* @param value The name to set.
* @return This builder for chaining.
*/
public Builder setName(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
name_ = value;
onChanged();
return this;
}
/**
* <code>string name = 2;</code>
* @return This builder for chaining.
*/
public Builder clearName() {
name_ = getDefaultInstance().getName();
onChanged();
return this;
}
/**
* <code>string name = 2;</code>
* @param value The bytes for name to set.
* @return This builder for chaining.
*/
public Builder setNameBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
checkByteStringIsUtf8(value);
name_ = value;
onChanged();
return this;
}
@java.lang.Override
public final Builder setUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.setUnknownFields(unknownFields);
}
@java.lang.Override
public final Builder mergeUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.mergeUnknownFields(unknownFields);
}
// @@protoc_insertion_point(builder_scope:Person)
}
// @@protoc_insertion_point(class_scope:Person)
private static final PersonOuterClass.Person DEFAULT_INSTANCE;
static {
DEFAULT_INSTANCE = new PersonOuterClass.Person();
}
public static PersonOuterClass.Person getDefaultInstance() {
return DEFAULT_INSTANCE;
}
private static final com.google.protobuf.Parser<Person>
PARSER = new com.google.protobuf.AbstractParser<Person>() {
@java.lang.Override
public Person parsePartialFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return new Person(input, extensionRegistry);
}
};
public static com.google.protobuf.Parser<Person> parser() {
return PARSER;
}
@java.lang.Override
public com.google.protobuf.Parser<Person> getParserForType() {
return PARSER;
}
@java.lang.Override
public PersonOuterClass.Person getDefaultInstanceForType() {
return DEFAULT_INSTANCE;
}
}
private static final com.google.protobuf.Descriptors.Descriptor
internal_static_Person_descriptor;
private static final
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internal_static_Person_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\014person.proto\"\"\n\006Person\022\n\n\002id\030\001 \001(\005\022\014\n\004" +
"name\030\002 \001(\tb\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
});
internal_static_Person_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_Person_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_Person_descriptor,
new java.lang.String[] { "Id", "Name", });
}
// @@protoc_insertion_point(outer_class_scope)
}
Conclusion
In conclusion, Protocol Buffers are a powerful method for serializing structured data to facilitate data exchange between applications and systems. Its advantages include smaller data size, fast encoding and decoding, automatic code generation, and support for various programming languages. The basic syntax is straightforward and widely used in multiple applications, especially in the gRPC framework. Using Protocol Buffers, developers can quickly build distributed systems that communicate with each other using a simple and efficient RPC interface, making it an excellent choice for building microservices and other distributed applications.
Top comments (0)