or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

code-generation.mdencoding-decoding.mdfile-operations.mdgeneric-data.mdindex.mdmessage-operations.mdreflection-operations.mdschema-evolution.mdschema-system.md

reflection-operations.mddocs/

0

# Reflection Operations

1

2

Avro's reflection support enables automatic schema generation and data handling for existing Java classes using reflection and annotations. This approach allows seamless integration with existing POJOs and domain objects without requiring code generation.

3

4

## Capabilities

5

6

### Reflect Data Utilities

7

8

Core utilities for reflection-based schema generation and data operations on existing Java classes.

9

10

```java { .api }

11

public class ReflectData extends SpecificData {

12

public static ReflectData get();

13

public static ReflectData get(ClassLoader classLoader);

14

15

// Schema generation from classes

16

public Schema getSchema(Type type);

17

public Schema getSchema(Class<?> type);

18

19

// Allow null values (makes fields nullable by default)

20

public ReflectData allowNull();

21

public boolean getAllowNull();

22

23

// String type configuration

24

public ReflectData setStringType(StringType stringType);

25

26

// Instance creation

27

public Object newInstance(Class<?> c, Schema schema);

28

public Object newRecord(Object old, Schema schema);

29

30

// Field access

31

public Object getField(Object record, String name, int position);

32

public void setField(Object record, String name, int position, Object o);

33

34

// Validation

35

public boolean validate(Schema schema, Object datum);

36

}

37

```

38

39

**Usage Examples:**

40

41

```java

42

// Basic reflection usage

43

ReflectData reflectData = ReflectData.get();

44

45

// Generate schema from existing POJO

46

public class Person {

47

private String name;

48

private int age;

49

private List<String> hobbies;

50

51

// Constructor, getters, setters

52

public Person() {}

53

public Person(String name, int age) {

54

this.name = name;

55

this.age = age;

56

this.hobbies = new ArrayList<>();

57

}

58

59

public String getName() { return name; }

60

public void setName(String name) { this.name = name; }

61

public int getAge() { return age; }

62

public void setAge(int age) { this.age = age; }

63

public List<String> getHobbies() { return hobbies; }

64

public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }

65

}

66

67

// Generate schema automatically

68

Schema personSchema = reflectData.getSchema(Person.class);

69

System.out.println("Generated schema: " + personSchema.toString(true));

70

71

// Allow nullable fields

72

ReflectData nullableReflect = ReflectData.get().allowNull();

73

Schema nullableSchema = nullableReflect.getSchema(Person.class);

74

75

// Create instance from schema

76

Person person = (Person) reflectData.newInstance(Person.class, personSchema);

77

person.setName("Alice");

78

person.setAge(30);

79

80

// Validate instance against schema

81

boolean isValid = reflectData.validate(personSchema, person);

82

System.out.println("Person is valid: " + isValid);

83

```

84

85

### Reflect Datum Reader

86

87

DatumReader that uses reflection to deserialize data into existing Java objects.

88

89

```java { .api }

90

public class ReflectDatumReader<T> extends SpecificDatumReader<T> {

91

public ReflectDatumReader();

92

public ReflectDatumReader(Schema schema);

93

public ReflectDatumReader(Schema writer, Schema reader);

94

public ReflectDatumReader(Class<T> c);

95

public ReflectDatumReader(Class<T> c, Schema writer, Schema reader);

96

97

// Inherited methods

98

public void setSchema(Schema schema);

99

public void setExpected(Schema reader);

100

public T read(T reuse, Decoder in) throws IOException;

101

}

102

```

103

104

**Usage Examples:**

105

106

```java

107

// Read data into existing POJOs

108

ReflectDatumReader<Person> reader = new ReflectDatumReader<>(Person.class);

109

110

// Read from binary data

111

InputStream inputStream = new FileInputStream("persons.avro");

112

BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(inputStream, null);

113

114

Person person = reader.read(null, decoder);

115

System.out.println("Name: " + person.getName());

116

System.out.println("Age: " + person.getAge());

117

118

// Schema evolution with reflection

119

Schema writerSchema = getOldPersonSchema();

120

Schema readerSchema = ReflectData.get().getSchema(Person.class);

121

122

ReflectDatumReader<Person> evolvingReader =

123

new ReflectDatumReader<>(Person.class, writerSchema, readerSchema);

124

125

Person evolvedPerson = evolvingReader.read(null, decoder);

126

127

// Read collection of objects

128

List<Person> persons = new ArrayList<>();

129

ReflectDatumReader<Person> personReader = new ReflectDatumReader<>(Person.class);

130

131

while (hasMoreData(decoder)) {

132

Person p = personReader.read(null, decoder);

133

persons.add(p);

134

}

135

```

136

137

### Reflect Datum Writer

138

139

DatumWriter that uses reflection to serialize existing Java objects.

140

141

```java { .api }

142

public class ReflectDatumWriter<T> extends SpecificDatumWriter<T> {

143

public ReflectDatumWriter();

144

public ReflectDatumWriter(Schema schema);

145

public ReflectDatumWriter(Class<T> c);

146

147

// Inherited methods

148

public void setSchema(Schema schema);

149

public void write(T datum, Encoder out) throws IOException;

150

}

151

```

152

153

**Usage Examples:**

154

155

```java

156

// Write existing POJOs using reflection

157

Person person = new Person("Bob", 35);

158

person.getHobbies().addAll(Arrays.asList("reading", "hiking", "cooking"));

159

160

Schema personSchema = ReflectData.get().getSchema(Person.class);

161

ReflectDatumWriter<Person> writer = new ReflectDatumWriter<>(Person.class);

162

163

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

164

BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(outputStream, null);

165

166

writer.write(person, encoder);

167

encoder.flush();

168

169

// Write collection of objects

170

List<Person> persons = Arrays.asList(

171

new Person("Alice", 25),

172

new Person("Charlie", 40),

173

new Person("Diana", 28)

174

);

175

176

for (Person p : persons) {

177

writer.write(p, encoder);

178

}

179

encoder.flush();

180

181

byte[] serializedData = outputStream.toByteArray();

182

```

183

184

### Reflection Annotations

185

186

Annotations for customizing reflection-based schema generation and field handling.

187

188

```java { .api }

189

@Target({ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})

190

@Retention(RetentionPolicy.RUNTIME)

191

public @interface AvroName {

192

String value();

193

}

194

195

@Target({ElementType.FIELD, ElementType.TYPE})

196

@Retention(RetentionPolicy.RUNTIME)

197

public @interface AvroDoc {

198

String value();

199

}

200

201

@Target(ElementType.FIELD)

202

@Retention(RetentionPolicy.RUNTIME)

203

public @interface AvroDefault {

204

String value();

205

}

206

207

@Target({ElementType.FIELD, ElementType.PARAMETER})

208

@Retention(RetentionPolicy.RUNTIME)

209

public @interface Nullable {

210

// Makes field nullable in generated schema

211

}

212

213

@Target({ElementType.FIELD, ElementType.PARAMETER})

214

@Retention(RetentionPolicy.RUNTIME)

215

public @interface Union {

216

Class<?>[] value();

217

}

218

219

@Target(ElementType.FIELD)

220

@Retention(RetentionPolicy.RUNTIME)

221

public @interface AvroIgnore {

222

// Excludes field from schema generation

223

}

224

225

@Target(ElementType.FIELD)

226

@Retention(RetentionPolicy.RUNTIME)

227

public @interface AvroAlias {

228

String[] value();

229

}

230

231

@Target(ElementType.FIELD)

232

@Retention(RetentionPolicy.RUNTIME)

233

public @interface AvroMeta {

234

String key();

235

String value();

236

}

237

```

238

239

**Usage Examples:**

240

241

```java

242

// POJO with reflection annotations

243

public class AnnotatedPerson {

244

@AvroName("full_name")

245

@AvroDoc("The person's full name")

246

private String name;

247

248

@AvroDoc("Age in years")

249

@AvroDefault("0")

250

private int age;

251

252

@Nullable

253

@AvroDoc("Email address if available")

254

private String email;

255

256

@AvroIgnore

257

private String internalId; // This field will be ignored

258

259

@Union({String.class, Integer.class})

260

private Object flexibleField;

261

262

@AvroAlias({"old_phone", "phone_number"})

263

private String phone;

264

265

@AvroMeta(key = "format", value = "ISO-8601")

266

private String timestamp;

267

268

// Constructors, getters, setters...

269

public AnnotatedPerson() {}

270

271

public String getName() { return name; }

272

public void setName(String name) { this.name = name; }

273

274

public int getAge() { return age; }

275

public void setAge(int age) { this.age = age; }

276

277

@Nullable

278

public String getEmail() { return email; }

279

public void setEmail(String email) { this.email = email; }

280

281

// Other getters/setters...

282

}

283

284

// Generate schema with annotations

285

Schema annotatedSchema = ReflectData.get().getSchema(AnnotatedPerson.class);

286

System.out.println("Schema with annotations:\n" + annotatedSchema.toString(true));

287

288

// The generated schema will include:

289

// - "full_name" instead of "name"

290

// - Documentation strings

291

// - Default values

292

// - Nullable email field

293

// - No internalId field

294

// - Union type for flexibleField

295

// - Aliases for phone field

296

// - Custom metadata

297

```

298

299

### Enum Support

300

301

Reflection support for Java enums with custom naming and documentation.

302

303

```java { .api }

304

// Enum annotations

305

@Target(ElementType.TYPE)

306

@Retention(RetentionPolicy.RUNTIME)

307

public @interface AvroEnumDefault {

308

String value();

309

}

310

```

311

312

**Usage Examples:**

313

314

```java

315

// Enum with reflection support

316

@AvroDoc("Status enumeration for users")

317

public enum UserStatus {

318

@AvroName("ACTIVE") ACTIVE,

319

@AvroName("INACTIVE") INACTIVE,

320

@AvroName("SUSPENDED") SUSPENDED,

321

@AvroName("PENDING") PENDING

322

}

323

324

// POJO using enum

325

public class UserWithStatus {

326

private String name;

327

328

@AvroDefault("\"PENDING\"")

329

private UserStatus status;

330

331

public UserWithStatus() {}

332

333

public String getName() { return name; }

334

public void setName(String name) { this.name = name; }

335

336

public UserStatus getStatus() { return status; }

337

public void setStatus(UserStatus status) { this.status = status; }

338

}

339

340

// Generate schema including enum

341

Schema userWithStatusSchema = ReflectData.get().getSchema(UserWithStatus.class);

342

343

// Use with reflection I/O

344

UserWithStatus user = new UserWithStatus();

345

user.setName("Alice");

346

user.setStatus(UserStatus.ACTIVE);

347

348

ReflectDatumWriter<UserWithStatus> writer = new ReflectDatumWriter<>(UserWithStatus.class);

349

ByteArrayOutputStream out = new ByteArrayOutputStream();

350

BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(out, null);

351

writer.write(user, encoder);

352

encoder.flush();

353

```

354

355

### Complex Type Support

356

357

Reflection handles complex nested types, collections, and custom objects.

358

359

```java { .api }

360

// Complex type examples that work with reflection

361

public class ComplexPerson {

362

private String name;

363

private List<String> hobbies;

364

private Map<String, Integer> scores;

365

private Address address; // Nested object

366

private List<Phone> phoneNumbers; // List of objects

367

368

// Constructors, getters, setters...

369

}

370

371

public class Address {

372

private String street;

373

private String city;

374

private String zipCode;

375

376

@Nullable

377

private String country;

378

379

// Constructors, getters, setters...

380

}

381

382

public class Phone {

383

private String type; // "home", "work", "mobile"

384

private String number;

385

386

// Constructors, getters, setters...

387

}

388

```

389

390

**Usage Examples:**

391

392

```java

393

// Work with complex nested objects

394

ComplexPerson person = new ComplexPerson();

395

person.setName("John Doe");

396

person.setHobbies(Arrays.asList("reading", "swimming"));

397

person.setScores(Map.of("math", 95, "science", 88));

398

399

Address address = new Address();

400

address.setStreet("123 Main St");

401

address.setCity("Anytown");

402

address.setZipCode("12345");

403

person.setAddress(address);

404

405

Phone workPhone = new Phone();

406

workPhone.setType("work");

407

workPhone.setNumber("555-1234");

408

person.setPhoneNumbers(Arrays.asList(workPhone));

409

410

// Generate schema for complex type

411

Schema complexSchema = ReflectData.get().getSchema(ComplexPerson.class);

412

System.out.println("Complex schema:\n" + complexSchema.toString(true));

413

414

// Serialize complex object

415

ReflectDatumWriter<ComplexPerson> writer = new ReflectDatumWriter<>(ComplexPerson.class);

416

ByteArrayOutputStream out = new ByteArrayOutputStream();

417

BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(out, null);

418

writer.write(person, encoder);

419

encoder.flush();

420

421

// Deserialize complex object

422

ReflectDatumReader<ComplexPerson> reader = new ReflectDatumReader<>(ComplexPerson.class);

423

BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(out.toByteArray(), null);

424

ComplexPerson deserializedPerson = reader.read(null, decoder);

425

426

System.out.println("Name: " + deserializedPerson.getName());

427

System.out.println("Address: " + deserializedPerson.getAddress().getCity());

428

```

429

430

## Types

431

432

```java { .api }

433

public class ReflectData extends SpecificData {

434

// Reflection-based data operations

435

}

436

437

public class ReflectDatumReader<T> extends SpecificDatumReader<T> {

438

// Reflection-based reader

439

}

440

441

public class ReflectDatumWriter<T> extends SpecificDatumWriter<T> {

442

// Reflection-based writer

443

}

444

445

// Reflection annotations

446

@Target({ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})

447

@Retention(RetentionPolicy.RUNTIME)

448

public @interface AvroName {

449

String value();

450

}

451

452

@Target({ElementType.FIELD, ElementType.TYPE})

453

@Retention(RetentionPolicy.RUNTIME)

454

public @interface AvroDoc {

455

String value();

456

}

457

458

@Target(ElementType.FIELD)

459

@Retention(RetentionPolicy.RUNTIME)

460

public @interface AvroDefault {

461

String value();

462

}

463

464

@Target({ElementType.FIELD, ElementType.PARAMETER})

465

@Retention(RetentionPolicy.RUNTIME)

466

public @interface Nullable {

467

// Marker annotation for nullable fields

468

}

469

470

@Target({ElementType.FIELD, ElementType.PARAMETER})

471

@Retention(RetentionPolicy.RUNTIME)

472

public @interface Union {

473

Class<?>[] value();

474

}

475

476

@Target(ElementType.FIELD)

477

@Retention(RetentionPolicy.RUNTIME)

478

public @interface AvroIgnore {

479

// Marker annotation to ignore fields

480

}

481

482

@Target(ElementType.FIELD)

483

@Retention(RetentionPolicy.RUNTIME)

484

public @interface AvroAlias {

485

String[] value();

486

}

487

488

@Target(ElementType.FIELD)

489

@Retention(RetentionPolicy.RUNTIME)

490

public @interface AvroMeta {

491

String key();

492

String value();

493

}

494

495

@Target(ElementType.TYPE)

496

@Retention(RetentionPolicy.RUNTIME)

497

public @interface AvroEnumDefault {

498

String value();

499

}

500

```