Skip to content

Serialization annotations

The generated schema path describes explicit data, not arbitrary Java object graphs. Application constructors/accessors remain trusted code, and the host admits the actual application schema providers.

@SchemaType

Declares an allowlisted public, non-generic record. Target: type. Required attributes: id, version.

@SchemaType(id = "orders.Payment", version = 1)
public record Payment(
        @WireField(id = 1) String orderId,
        @WireField(id = 2) long amountMinor) { }

The ID follows the public schema identifier grammar and the version is positive. Generated providers perform direct access without reflective opening of implementation packages. A registry's version rules still apply; an annotation is not a data-migration tool.

@WireField

Declares a stable numeric field identity. Target: record component. Attributes: required id; required = true, hasDefault = false, defaultValue = "".

@SchemaType(id = "orders.Note", version = 1)
public record OrderNote(
        @WireField(id = 1) String orderId,
        @WireField(id = 2, required = false,
                   hasDefault = true, defaultValue = "none") String note) { }

Every component needs a field ID. Primitive components must be required. Scalar defaults are parsed and validated at compilation; nested schemas and byte arrays cannot use a nonnull scalar text default. An omitted optional field with no declared default yields null according to the supported schema contract.

Example: exchange a payment value

The Payment record above is an application data contract, not a remote object. A service can accept and return it directly:

public interface PaymentValidation {
    Payment normalize(Payment payment);
}

@J7mService(name = "payment-validation")
public final class PaymentValidationService implements PaymentValidation {
    public PaymentValidationService() { }

    @RemoteMethod
    public Payment normalize(Payment payment) {
        if (payment.amountMinor() < 0) {
            throw new IllegalArgumentException("amount must be non-negative");
        }
        return new Payment(payment.orderId(), payment.amountMinor());
    }
}

Compile the record with the schema processor and package/admit its generated schema provider on the participating application paths. A call with new Payment("42", 1250) returns a decoded payment with the same field values in the receiving application's allowed class identity. It does not copy an arbitrary implementation graph.

For OrderNote, an omitted optional wire field 2 decodes to the declared "none" default. Java construction still requires its normal constructor arguments; wire defaults do not rewrite new OrderNote("42", null). Stable numeric IDs identify fields independently of source parameter names. See schemas and JPMS.