Apache Avro core components for data serialization with rich data structures, compact binary format, and schema evolution support
—
Avro's code generation support provides integration with generated Java classes for type-safe, high-performance data operations. Specific operations work with POJOs generated from Avro schemas, offering compile-time type checking and optimized serialization performance.
Core utilities for working with generated Avro classes and their schemas.
public class SpecificData extends GenericData {
public static SpecificData get();
// Class and schema mapping
public static Class<?> getClassName(Schema schema);
public Schema getSchema(Class<?> c);
public Schema getSchema(Object object);
// Instance creation
public Object newInstance(Class<?> c, Schema schema);
public Object createFixed(Object old, Schema schema);
public Object createRecord(Object old, Schema schema);
public Object createEnum(String symbol, Schema schema);
// Protocol support
public Protocol getProtocol(Class<?> iface);
// Conversion support
public void addLogicalTypeConversion(Conversion<?> conversion);
public Conversion<?> getConversionFor(LogicalType logicalType);
public Collection<Conversion<?>> getConversions();
}Usage Examples:
// Get SpecificData instance
SpecificData specificData = SpecificData.get();
// Get class name from schema
Schema userSchema = new Schema.Parser().parse(userSchemaJson);
Class<?> userClass = SpecificData.getClassName(userSchema);
System.out.println("Generated class: " + userClass.getName());
// Get schema from generated class
Schema schemaFromClass = specificData.getSchema(User.class);
System.out.println("Schema from class: " + schemaFromClass.getName());
// Create instance of generated class
User user = (User) specificData.newInstance(User.class, userSchema);
user.setName("Alice");
user.setAge(25);
// Work with protocols for RPC
Protocol userServiceProtocol = specificData.getProtocol(UserService.class);
System.out.println("Protocol: " + userServiceProtocol.getName());Base interface that all generated record classes implement.
public interface SpecificRecord extends IndexedRecord {
Schema getSchema();
// Specific record methods are typically generated
// For example, for a User schema:
// String getName();
// void setName(String name);
// Integer getAge();
// void setAge(Integer age);
}Usage Examples:
// Using generated record class (example)
public class User implements SpecificRecord {
private static final Schema SCHEMA$ = new Schema.Parser().parse(SCHEMA_JSON);
private String name;
private Integer age;
private String email;
public User() {}
public User(String name, Integer age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
@Override
public Schema getSchema() {
return SCHEMA$;
}
// Generated getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
// IndexedRecord implementation
@Override
public Object get(int field) {
switch (field) {
case 0: return name;
case 1: return age;
case 2: return email;
default: throw new IndexOutOfBoundsException();
}
}
@Override
public void put(int field, Object value) {
switch (field) {
case 0: name = (String) value; break;
case 1: age = (Integer) value; break;
case 2: email = (String) value; break;
default: throw new IndexOutOfBoundsException();
}
}
}
// Use generated class
User user = new User("Bob", 30, "bob@example.com");
Schema userSchema = user.getSchema();
System.out.println("User: " + user.getName() + ", Age: " + user.getAge());DatumReader optimized for generated classes with compile-time type safety.
public class SpecificDatumReader<T> extends GenericDatumReader<T> {
public SpecificDatumReader();
public SpecificDatumReader(Schema schema);
public SpecificDatumReader(Schema writer, Schema reader);
public SpecificDatumReader(Class<T> c);
public SpecificDatumReader(Class<T> c, Schema writer, Schema reader);
// Inherited from GenericDatumReader
public void setSchema(Schema schema);
public void setExpected(Schema reader);
public T read(T reuse, Decoder in) throws IOException;
}Usage Examples:
// Read generated objects from binary data
SpecificDatumReader<User> reader = new SpecificDatumReader<>(User.class);
InputStream inputStream = new FileInputStream("users.avro");
BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(inputStream, null);
// Read specific records with type safety
User user1 = reader.read(null, decoder);
System.out.println("User name: " + user1.getName());
// Reuse objects for better performance
User reusedUser = null;
while (hasMoreData(decoder)) {
reusedUser = reader.read(reusedUser, decoder);
processUser(reusedUser);
}
// Schema evolution with generated classes
Schema writerSchema = getWriterSchema();
Schema readerSchema = User.SCHEMA$;
SpecificDatumReader<User> evolvingReader =
new SpecificDatumReader<>(User.class, writerSchema, readerSchema);
User evolvedUser = evolvingReader.read(null, decoder);DatumWriter optimized for generated classes with compile-time type safety.
public class SpecificDatumWriter<T> extends GenericDatumWriter<T> {
public SpecificDatumWriter();
public SpecificDatumWriter(Schema schema);
public SpecificDatumWriter(Class<T> c);
// Inherited from GenericDatumWriter
public void setSchema(Schema schema);
public void write(T datum, Encoder out) throws IOException;
}Usage Examples:
// Write generated objects to binary format
SpecificDatumWriter<User> writer = new SpecificDatumWriter<>(User.class);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(outputStream, null);
// Write specific records with type safety
User user = new User("Charlie", 35, "charlie@example.com");
writer.write(user, encoder);
encoder.flush();
// Write multiple users
List<User> users = Arrays.asList(
new User("Alice", 25, "alice@example.com"),
new User("Bob", 30, "bob@example.com"),
new User("Carol", 28, "carol@example.com")
);
for (User u : users) {
writer.write(u, encoder);
}
encoder.flush();
byte[] serializedData = outputStream.toByteArray();Annotations used to mark and configure generated Avro classes.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvroGenerated {
// Marks classes as Avro-generated
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FixedSize {
int value();
}Usage Examples:
// Generated record with annotations
@AvroGenerated
public class User implements SpecificRecord {
// Implementation generated by Avro compiler
}
// Generated fixed class with size annotation
@AvroGenerated
@FixedSize(16)
public class MD5Hash implements SpecificFixed {
private static final Schema SCHEMA$ = Schema.createFixed("MD5Hash", null, null, 16);
private byte[] bytes = new byte[16];
@Override
public Schema getSchema() {
return SCHEMA$;
}
@Override
public byte[] bytes() {
return bytes;
}
}
// Check if class is Avro-generated
if (User.class.isAnnotationPresent(AvroGenerated.class)) {
System.out.println("User class is Avro-generated");
}
// Get fixed size from annotation
if (MD5Hash.class.isAnnotationPresent(FixedSize.class)) {
FixedSize annotation = MD5Hash.class.getAnnotation(FixedSize.class);
System.out.println("Fixed size: " + annotation.value());
}Generated classes often include builder patterns for convenient object construction.
// Example of generated builder (varies by schema)
public static class Builder {
// Builder methods generated based on schema fields
public Builder setName(String name);
public Builder setAge(Integer age);
public Builder setEmail(String email);
public User build();
public Builder clear();
// Field checking
public boolean hasName();
public boolean hasAge();
public boolean hasEmail();
}Usage Examples:
// Using generated builder (example pattern)
User user = User.newBuilder()
.setName("David")
.setAge(40)
.setEmail("david@example.com")
.build();
// Build with partial data
User partialUser = User.newBuilder()
.setName("Eve")
.build(); // Age and email will use defaults or be null
// Check what fields are set
User.Builder builder = User.newBuilder();
builder.setName("Frank");
if (builder.hasName()) {
System.out.println("Name is set");
}
if (!builder.hasEmail()) {
System.out.println("Email is not set");
}
User finalUser = builder.build();Error classes generated from schema error types implement SpecificRecord.
// Generated error class example
@AvroGenerated
public class UserNotFoundException extends Exception implements SpecificRecord {
private static final Schema SCHEMA$ = parseSchema(ERROR_SCHEMA_JSON);
private String userId;
private String message;
public UserNotFoundException() {}
public UserNotFoundException(String userId, String message) {
super(message);
this.userId = userId;
this.message = message;
}
@Override
public Schema getSchema() {
return SCHEMA$;
}
// Generated getters/setters
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
@Override
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}Usage Examples:
// Throw generated error
public User findUser(String userId) throws UserNotFoundException {
User user = userRepository.find(userId);
if (user == null) {
throw new UserNotFoundException(userId, "User not found: " + userId);
}
return user;
}
// Catch and handle generated error
try {
User user = findUser("unknown123");
} catch (UserNotFoundException e) {
System.err.println("Failed to find user: " + e.getUserId());
System.err.println("Error message: " + e.getMessage());
// Error also implements SpecificRecord
Schema errorSchema = e.getSchema();
System.out.println("Error schema: " + errorSchema.getName());
}public class SpecificData extends GenericData {
// Utilities for generated classes
}
public interface SpecificRecord extends IndexedRecord {
Schema getSchema();
}
public interface SpecificFixed extends GenericFixed {
// Marker interface for generated fixed classes
}
public class SpecificDatumReader<T> extends GenericDatumReader<T> {
// Type-safe reader for generated classes
}
public class SpecificDatumWriter<T> extends GenericDatumWriter<T> {
// Type-safe writer for generated classes
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvroGenerated {
// Annotation for generated classes
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FixedSize {
int value();
}
// Generated class interfaces (examples based on common patterns)
public interface Builder<T> {
T build();
Builder<T> clear();
}
public abstract class SpecificRecordBase implements SpecificRecord {
// Base class for generated records (implementation detail)
}
public abstract class SpecificExceptionBase extends Exception implements SpecificRecord {
// Base class for generated exceptions
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-avro--avro