Skip to main content

Jprotobuf Generate .proto

Introduction

Jprotobuf provides a simplified way to use Google Protocol Buffers with equivalent performance. With jprotobuf, you do not need to manually write proto file syntax. You can define fields directly with Java annotations.

This module enhances jprotobuf in the following ways:

  1. Multiple objects can be generated into the same .proto file.
  2. You can omit jprotobuf field annotations for further simplification.

How to Install

This module is optional. Add it to pom.xml when needed.

see https://central.sonatype.com/artifact/com.iohao.net/extension-jprotobuf

pom.xml
<dependency>
<groupId>com.iohao.net</groupId>
<artifactId>extension-jprotobuf</artifactId>
<version>${ionet.version}</version>
</dependency>

How to Generate .proto

Via Utility Class

Add the package names to scan. The default output directory for .proto is target/proto.

void test1() {
GenerateFileKit.generate("com.iohao.example.sdk.logic.data");
}

Via ProtoGenerateFile

  • code 3: specify the proto output directory. If omitted, default is target/proto.
  • code 4: add package names to scan.
  • code 6: generate .proto files.
void test2() {
var protoGenerateFile = new ProtoGenerateFile()
.setGenerateFolder(path)
.addProtoPackage("com.iohao.example.sdk.logic.data");

protoGenerateFile.generate();
}

Multi-Module On-Demand Generation

  • code 6: specify the proto output directory. If omitted, default is target/proto.
  • code 7~9: add package names to scan.
  • code 10: generate .proto files.
void test2() {
String generateFolder = "/Users/join/gitme/game/MyGames/proto";
var protoPackageList = List.of("com.iohao.happy.robot", "com.iohao.happy.email");

var protoGenerateFile = new ProtoGenerateFile()
.setGenerateFolder(generateFolder)
.addProtoPackage(protoPackageList)
.addProtoPackage("com.iohao.happy.common.provide.proto");

protoGenerateFile.generate();
}

ProtoFileMerge Annotation

ProtoFileMerge has two attributes:

  1. fileName: specifies the .proto file name.
  2. filePackage: specifies the .proto package name.
warning

You must add this annotation on a class to generate it into a .proto file.

Usage Example

Now we define 3 classes (protocol data classes) used for communication between client and server.

Data Protocol Classes

Two required annotations for protocol data classes:

  • ProtobufClass: marks a class as a protocol data class.
  • ProtoFileMerge: provides metadata required for .proto generation.
public interface TempProtoFile {
String fileName = "common.proto";
String filePackage = "pb.common";
}

/**
* MyFood
*/
@ProtobufClass
@FieldDefaults(level = AccessLevel.PUBLIC)
@ProtoFileMerge(fileName = TempProtoFile.fileName, filePackage = TempProtoFile.filePackage)
public class Food {
/** id */
int id;
/** food name */
String foodName;
}

/**
* My Cat
*/
@ProtobufClass
@FieldDefaults(level = AccessLevel.PUBLIC)
@ProtoFileMerge(fileName = TempProtoFile.fileName, filePackage = TempProtoFile.filePackage)
public class Cat {
/** id */
int id;
/** cat name */
String catName;
/** the food */
Food food;
/** the animalType */
AnimalTypeEnum animalType;
}

@ProtobufClass
@ProtoFileMerge(fileName = TempProtoFile.fileName, filePackage = TempProtoFile.filePackage)
public enum AnimalTypeEnum implements EnumReadable {
/** the cat */
cat(0),
/** the tiger */
tiger(10),
;

final int value;

AnimalTypeEnum(int value) {
this.value = value;
}

@Override
public int value() {
return this.value;
}
}

Generated .proto File

Protocol data will be generated into common.proto as shown below.

You can see that structures in common.proto contain comments. These comments come from the Java classes above, so code and documentation stay aligned.

syntax = "proto3";
package pb.common;

// TestAnimalTypeEnum
enum AnimalTypeEnum {
// the cat
cat = 0;
// the tiger
tiger = 10;
}

// MyCat
message Cat {
// id
int32 id = 1;
// cat name
string cat_name = 2;
// the food
Food food = 3;
// the animalType
AnimalTypeEnum animal_type = 4;
}

// MyFood
message Food {
// id
int32 id = 1;
// food name
string food_name = 2;
}

Enum

Default Enum Values

When using enums, values start from 0 by default.

/**
* The AnimalType
*/
@ProtobufClass
@ProtoFileMerge(fileName = MyProtoFile.fileName, filePackage = MyProtoFile.filePackage)
public enum AnimalType {
/** the bird */
BIRD,
/** the cat */
CAT;
}

/**
* The Animal
*/
@ProtobufClass
@FieldDefaults(level = AccessLevel.PUBLIC)
@ProtoFileMerge(fileName = MyProtoFile.fileName, filePackage = MyProtoFile.filePackage)
public class Animal {
/** id */
int id;
/** AnimalType */
AnimalType animalType;
}
common.proto
syntax = "proto3";
package common;
// The Animal
message Animal {
// id
int32 id = 1;
// AnimalType
AnimalType animal_type = 2;
}

// The AnimalType
enum AnimalType {
// the bird
BIRD = 0;
// the cat
CAT = 1;
}

Custom Enum Values

When using enums, custom values require implementing the EnumReadable interface.

@ProtobufClass
@ProtoFileMerge(fileName = TempProtoFile.fileName, filePackage = TempProtoFile.filePackage)
public enum AnimalTypeEnum implements EnumReadable {
/** the cat */
cat(0),
/** the tiger */
tiger(10),
;

final int value;

AnimalTypeEnum(int value) {
this.value = value;
}

@Override
public int value() {
return this.value;
}
}
common.proto
syntax = "proto3";
package common;
// TestAnimalTypeEnum
enum AnimalTypeEnum {
// the cat
cat = 0;
// the tiger
tiger = 10;
}

Field Naming Style

Generated .proto field names use UnderScoreCase by default.

syntax = "proto3";
package common;
// food
message Food {
// id
sint32 id = 1;
// food name
string food_name = 2;
}

Developers can also customize the naming style, for example to keep the same names as Java fields:

ProtoGenerateSetting.setFieldNameFunction(FieldNameGenerate::getFieldName);
syntax = "proto3";

// MyFood
message Food {
// id
int32 id = 1;
// food name
string foodName = 2;
}

Notes and Considerations

Comments support single-line style only, so describe each field clearly in one sentence.

public class Animal {
/** id */
int id;
/** the animalType */
AnimalType animalType;
}

Required annotations for protocol data classes:

  • ProtobufClass: marks a class as a protocol data class.
  • ProtoFileMerge: provides metadata required for .proto generation.
@ProtobufClass
@ProtoFileMerge(fileName = TempProtoFile.fileName, filePackage = TempProtoFile.filePackage)
public class Food {
...
}

Supported types: see https://github.com/jhunters/jprotobuf/blob/master/Document.md