protovalidate-java
is the Java language implementation of protovalidate
designed to validate Protobuf messages at runtime based on user-defined validation constraints. Powered by Google's Common Expression Language (CEL), it provides a flexible and efficient foundation for defining and evaluating custom validation rules. The primary goal of protovalidate
is to help developers ensure data consistency and integrity across the network without requiring generated code.
Head over to the core protovalidate
repository for:
- The API definition: used to describe validation constraints
- Documentation: how to apply
protovalidate
effectively - Migration tooling: incrementally migrate from
protoc-gen-validate
- Conformance testing utilities: for acceptance testing of
protovalidate
implementations
Other protovalidate
runtime implementations include:
- C++:
protovalidate-cc
- Go:
protovalidate-go
- Python:
protovalidate-python
And others coming soon:
- TypeScript:
protovalidate-ts
To include protovalidate-java
in your project, add the following to your build file:
dependencies {
implementation 'build.buf:protovalidate:<version>'
}
Remember to always check for the latest version of protovalidate-java
on the project's GitHub releases page to ensure you're using the most up-to-date version.
Validation constraints are defined directly within .proto
files. Documentation for adding constraints can be found in the protovalidate
project README and its comprehensive docs.
syntax = "proto3";
package my.package;
import "google/protobuf/timestamp.proto";
import "buf/validate/validate.proto";
message Transaction {
uint64 id = 1 [(buf.validate.field).uint64.gt = 999];
google.protobuf.Timestamp purchase_date = 2;
google.protobuf.Timestamp delivery_date = 3;
string price = 4 [(buf.validate.field).cel = {
id: "transaction.price",
message: "price must be positive and include a valid currency symbol ($ or £)",
expression: "(this.startsWith('$') || this.startsWith('£')) && double(this.substring(1)) > 0"
}];
option (buf.validate.message).cel = {
id: "transaction.delivery_date",
message: "delivery date must be after purchase date",
expression: "this.delivery_date > this.purchase_date"
};
}
In your Java code, create an instance of the Validator
class and use the validate
method to validate your messages.
// Import the required packages
package build.buf;
import build.buf.protovalidate.results.ValidationException;
import build.buf.protovalidate.results.ValidationResult;
import com.my.package.Transaction;
import com.google.protobuf.Timestamp;
import build.buf.protovalidate.Validator;
import build.buf.protovalidate.Config;
public class Main {
// Create timestamps for purchase and delivery date
Timestamp purchaseDate = Timestamp.newBuilder().build();
Timestamp deliveryDate = Timestamp.newBuilder().build();
// Create a transaction object using the Builder pattern
Transaction transaction =
Transaction.newBuilder()
.setId(1234)
.setPrice("$5.67")
.setPurchaseDate(purchaseDate)
.setDeliveryDate(deliveryDate)
.build();
// Create a validator object with the default Configuration
Validator validator = new Validator();
// Validate the transaction object using the validator
try {
ValidationResult result = validator.validate(transaction);
// Check if there are any validation violations
if (result.getViolations().isEmpty()) {
// No violations, validation successful
System.out.println("Validation succeeded");
} else {
// Print the violations if any found
System.out.println(result.toString());
}
} catch (ValidationException e) {
// Catch and print any ValidationExceptions thrown during the validation process
System.out.println("Validation failed: " + e.getMessage());
}
}
protovalidate
core repository- Buf
- CEL Spec
Offered under the Apache 2 license.