Compilers for Avro IDL and Avro Specific Java API - provides code generation tools for converting Avro schemas and IDL definitions into Java classes
—
Utility functions for schema traversal, cloning, manipulation operations, and the visitor pattern for custom schema processing.
Static utility methods for common schema operations.
/**
* Avro Schema utilities, to traverse and manipulate schemas.
* All methods are static and thread-safe.
*/
public final class Schemas {
/**
* Copy aliases from one schema to another (only for named types: RECORD, ENUM, FIXED)
* @param from Source schema
* @param to Destination schema
*/
public static void copyAliases(Schema from, Schema to);
/**
* Copy aliases from one field to another
* @param from Source field
* @param to Destination field
*/
public static void copyAliases(Schema.Field from, Schema.Field to);
/**
* Copy logical types from one schema to another
* @param from Source schema with logical type
* @param to Destination schema
*/
public static void copyLogicalTypes(Schema from, Schema to);
/**
* Copy properties from one JsonProperties object to another
* @param from Source properties
* @param to Destination properties
*/
public static void copyProperties(JsonProperties from, JsonProperties to);
/**
* Check if schema generates a Java class (ENUM, RECORD, FIXED types)
* @param schema Schema to check
* @return true if schema generates Java class
*/
public static boolean hasGeneratedJavaClass(Schema schema);
/**
* Get the Java class name for a schema
* @param schema Schema to get class name for
* @return Fully qualified Java class name
*/
public static String getJavaClassName(Schema schema);
/**
* Depth-first visit of schema tree using visitor pattern
* @param start Starting schema for traversal
* @param visitor Visitor implementation
* @return Result from visitor.get()
*/
public static <T> T visit(Schema start, SchemaVisitor<T> visitor);
}Visitor pattern interface for custom schema processing and traversal.
/**
* Visitor pattern interface for schema traversal.
* Generic type T represents the result type of the visit operation.
*/
public interface SchemaVisitor<T> {
/**
* Invoked for schemas that do not have "child" schemas (like string, int ...)
* or for a previously encountered schema with children, which will be treated
* as a terminal to avoid circular recursion.
* @param terminal Terminal schema node
* @return Action to control traversal
*/
SchemaVisitorAction visitTerminal(Schema terminal);
/**
* Invoked for schema with children before proceeding to visit the children.
* @param nonTerminal Non-terminal schema node
* @return Action to control traversal
*/
SchemaVisitorAction visitNonTerminal(Schema nonTerminal);
/**
* Invoked for schemas with children after its children have been visited.
* @param nonTerminal Non-terminal schema node after visiting children
* @return Action to control traversal
*/
SchemaVisitorAction afterVisitNonTerminal(Schema nonTerminal);
/**
* Invoked when visiting is complete.
* @return Final result of the visit operation
*/
T get();
}Controls the flow of schema traversal operations.
/**
* Actions for controlling schema traversal behavior
*/
public enum SchemaVisitorAction {
/** Continue normal traversal */
CONTINUE,
/** Skip the current subtree but continue with siblings */
SKIP_SUBTREE,
/** Skip remaining siblings at current level */
SKIP_SIBLINGS,
/** Terminate the entire traversal immediately */
TERMINATE
}Built-in visitor implementation for cloning schemas with customizable property copying.
/**
* Visitor implementation for cloning schemas.
* Creates a clone of the original Schema with docs and other nonessential
* fields stripped by default. What attributes are copied is customizable.
*/
public final class CloningVisitor implements SchemaVisitor<Schema> {
/** Interface for customizing property copying behavior */
public interface PropertyCopier {
void copy(Schema first, Schema second);
void copy(Schema.Field first, Schema.Field second);
}
/**
* Create cloning visitor that copies only serialization necessary fields
* @param root Root schema to clone
*/
public CloningVisitor(Schema root);
/**
* Create cloning visitor with custom property copying behavior
* @param copyProperties Custom property copier
* @param copyDocs Whether to copy documentation
* @param root Root schema to clone
*/
public CloningVisitor(PropertyCopier copyProperties, boolean copyDocs, Schema root);
}Visitor implementation for resolving schema references in IDL contexts.
/**
* Visitor for resolving unresolved schema references.
* Used primarily in IDL processing contexts.
*/
public final class ResolvingVisitor implements SchemaVisitor<Schema> {
/**
* Create resolving visitor
* @param root Root schema
* @param replace Map of schemas to replace
* @param symbolTable Function to resolve symbol names to schemas
*/
public ResolvingVisitor(Schema root, IdentityHashMap<Schema, Schema> replace,
Function<String, Schema> symbolTable);
}Visitor to check if a schema is fully resolved (no unresolved references).
/**
* Visitor that checks if the current schema is fully resolved.
* Returns true if schema has no unresolved parts.
*/
public final class IsResolvedSchemaVisitor implements SchemaVisitor<Boolean> {
/** Create visitor to check schema resolution status */
IsResolvedSchemaVisitor();
}import org.apache.avro.Schema;
import org.apache.avro.compiler.schema.Schemas;
// Check if schema generates Java class
Schema userSchema = new Schema.Parser().parse(new File("user.avsc"));
boolean hasClass = Schemas.hasGeneratedJavaClass(userSchema); // true for RECORD
Schema stringSchema = Schema.create(Schema.Type.STRING);
boolean hasClass2 = Schemas.hasGeneratedJavaClass(stringSchema); // false
// Get Java class name
String className = Schemas.getJavaClassName(userSchema);
// Returns: "com.example.User" (if namespace is com.example)import org.apache.avro.Schema;
import org.apache.avro.compiler.schema.Schemas;
// Copy aliases between schemas
Schema originalSchema = /* ... */;
Schema clonedSchema = /* ... */;
Schemas.copyAliases(originalSchema, clonedSchema);
Schemas.copyLogicalTypes(originalSchema, clonedSchema);
Schemas.copyProperties(originalSchema, clonedSchema);import org.apache.avro.Schema;
import org.apache.avro.compiler.schema.*;
// Count all record schemas in a complex schema
public class RecordCountingVisitor implements SchemaVisitor<Integer> {
private int recordCount = 0;
@Override
public SchemaVisitorAction visitTerminal(Schema terminal) {
if (terminal.getType() == Schema.Type.RECORD) {
recordCount++;
}
return SchemaVisitorAction.CONTINUE;
}
@Override
public SchemaVisitorAction visitNonTerminal(Schema nonTerminal) {
if (nonTerminal.getType() == Schema.Type.RECORD) {
recordCount++;
}
return SchemaVisitorAction.CONTINUE;
}
@Override
public SchemaVisitorAction afterVisitNonTerminal(Schema nonTerminal) {
return SchemaVisitorAction.CONTINUE;
}
@Override
public Integer get() {
return recordCount;
}
}
// Usage
Schema complexSchema = /* ... */;
Integer recordCount = Schemas.visit(complexSchema, new RecordCountingVisitor());
System.out.println("Found " + recordCount + " record schemas");import java.util.*;
// Collect all named schema types
public class NameCollectingVisitor implements SchemaVisitor<Set<String>> {
private Set<String> names = new HashSet<>();
@Override
public SchemaVisitorAction visitTerminal(Schema terminal) {
if (terminal.getName() != null) {
names.add(terminal.getFullName());
}
return SchemaVisitorAction.CONTINUE;
}
@Override
public SchemaVisitorAction visitNonTerminal(Schema nonTerminal) {
if (nonTerminal.getName() != null) {
names.add(nonTerminal.getFullName());
}
return SchemaVisitorAction.CONTINUE;
}
@Override
public SchemaVisitorAction afterVisitNonTerminal(Schema nonTerminal) {
return SchemaVisitorAction.CONTINUE;
}
@Override
public Set<String> get() {
return names;
}
}
// Usage
Set<String> schemaNames = Schemas.visit(schema, new NameCollectingVisitor());// Visitor that stops at first error type encountered
public class ErrorFindingVisitor implements SchemaVisitor<Schema> {
private Schema errorSchema = null;
@Override
public SchemaVisitorAction visitTerminal(Schema terminal) {
if (terminal.getType() == Schema.Type.RECORD &&
terminal.getName().endsWith("Error")) {
errorSchema = terminal;
return SchemaVisitorAction.TERMINATE; // Stop immediately
}
return SchemaVisitorAction.CONTINUE;
}
@Override
public SchemaVisitorAction visitNonTerminal(Schema nonTerminal) {
if (nonTerminal.getType() == Schema.Type.UNION) {
// Skip all union types and their children
return SchemaVisitorAction.SKIP_SUBTREE;
}
return SchemaVisitorAction.CONTINUE;
}
@Override
public SchemaVisitorAction afterVisitNonTerminal(Schema nonTerminal) {
return SchemaVisitorAction.CONTINUE;
}
@Override
public Schema get() {
return errorSchema;
}
}import org.apache.avro.compiler.schema.CloningVisitor;
// Clone a schema structure (default: copy only essential fields)
Schema originalSchema = /* ... */;
Schema clonedSchema = Schemas.visit(originalSchema, new CloningVisitor(originalSchema));
// Clone with custom property copying
CloningVisitor.PropertyCopier customCopier = new CloningVisitor.PropertyCopier() {
@Override
public void copy(Schema first, Schema second) {
Schemas.copyAliases(first, second);
Schemas.copyLogicalTypes(first, second);
// Copy custom properties
first.forEachProperty(second::addProp);
}
@Override
public void copy(Schema.Field first, Schema.Field second) {
Schemas.copyAliases(first, second);
}
};
Schema clonedWithDocs = Schemas.visit(originalSchema,
new CloningVisitor(customCopier, true, originalSchema));import org.apache.avro.compiler.idl.IsResolvedSchemaVisitor;
// Check if schema is fully resolved (no unresolved references)
Schema schema = /* ... */;
Boolean isResolved = Schemas.visit(schema, new IsResolvedSchemaVisitor());
if (isResolved) {
System.out.println("Schema is fully resolved");
} else {
System.out.println("Schema has unresolved references");
}import org.apache.avro.compiler.idl.ResolvingVisitor;
import java.util.*;
import java.util.function.Function;
// Resolve schema references using symbol table
IdentityHashMap<Schema, Schema> replacements = new IdentityHashMap<>();
Function<String, Schema> symbolTable = schemaName -> {
// Look up schema by name in symbol table
return findSchemaByName(schemaName);
};
Schema unresolvedSchema = /* ... */;
Schema resolvedSchema = Schemas.visit(unresolvedSchema,
new ResolvingVisitor(unresolvedSchema, replacements, symbolTable));// Copy aliases between record fields
Schema.Field originalField = /* ... */;
Schema.Field newField = /* ... */;
Schemas.copyAliases(originalField, newField);
// Now newField has all the aliases from originalFieldThe Schemas.visit() method performs depth-first traversal with these characteristics:
TERMINATE action stops traversal immediately and returns current resultSKIP_SUBTREE skips children but continues with siblingsSKIP_SIBLINGS skips remaining siblings at current levelSchemas utility methods are thread-safeSchemaVisitor implementations should be designed for single-threaded useInstall with Tessl CLI
npx tessl i tessl/maven-org-apache-avro--avro-compiler