Using the generated code

This guide shows how the generated JVM API enforces constraints at runtime.

Validation runs automatically when a message is created. It is also possible to validate an existing instance.

Validation on creation 

When a message violates declared constraints, build() throws ValidationException.

Kotlin
Java

shouldThrow<ValidationException> {
    // Kotlin proto DSL delegates to a Java builder.
    bankCard {
        digits = "invalid"
        owner = "ALEX SMITH"
    }
}

assertThrows(ValidationException.class, () ->
    BankCard.newBuilder()
        .setDigits("invalid")
        .setOwner("ALEX SMITH")
        .build()
);

Validating existing messages 

To get a ValidationError instead of an exception, build the message using the buildPartial() and call validate():

Kotlin
Java

// There is no Kotlin DSL which allows building a non-valid message.
// So we use a builder from Java.
val card = BankCard.newBuilder()
    .setOwner("ALEX SMITH")
    .setDigits("wrong number")
    .buildPartial()
val error = card.validate()
error.shouldBePresent()

val violation = error.get().constraintViolationList[0]
val formatted = violation.message.format()

formatted shouldContain "digits"
formatted shouldContain "wrong number"

var card = BankCard.newBuilder()
        .setOwner("ALEX SMITH")
        .setDigits("wrong number")
        .buildPartial();
var error = card.validate();
assertThat(error).isPresent();

var violation = error.get().getConstraintViolation(0);
var formatted = TemplateStrings.format(violation.getMessage());

assertThat(formatted).contains("digits");
assertThat(formatted).contains("wrong number");

You are not likely to use buildPartial() for building invalid messages in production code. But you may want to use validate() for checking messages received from external sources, such as API requests or deserialized data, for example, when building an anticorruption layer.

What’s next