0
# Annotation-Based Configuration
1
2
Field-level configuration annotations for controlling serialization behavior, field numbers, and exclusions. These annotations provide a declarative way to customize how protostuff-runtime handles individual fields and classes during schema generation and serialization.
3
4
## Capabilities
5
6
### @Tag Annotation
7
8
Primary annotation for configuring field serialization behavior, field numbers, and field-level options.
9
10
```java { .api }
11
/**
12
* Annotation for configuring field serialization behavior
13
* Controls field numbers, aliases, and group filtering
14
*/
15
@Target(ElementType.FIELD)
16
@Retention(RetentionPolicy.RUNTIME)
17
public @interface Tag {
18
19
/**
20
* The field number to use in the protobuf wire format
21
* Must be unique within the message and in range [1, 2^29-1]
22
* @return field number
23
*/
24
int value();
25
26
/**
27
* Optional alias for the field name
28
* If specified, this name will be used instead of the Java field name
29
* @return field alias or empty string for default
30
*/
31
String alias() default "";
32
33
/**
34
* Group filter for conditional field processing
35
* Fields with the same group filter can be processed together
36
* @return group filter value, 0 for no filtering
37
*/
38
int groupFilter() default 0;
39
}
40
```
41
42
### @Exclude Annotation
43
44
Annotation to mark fields that should be ignored during serialization and schema generation.
45
46
```java { .api }
47
/**
48
* Annotation to exclude fields from serialization
49
* Fields marked with @Exclude will not be included in the schema
50
*/
51
@Target(ElementType.FIELD)
52
@Retention(RetentionPolicy.RUNTIME)
53
public @interface Exclude {
54
// Marker annotation - no parameters
55
}
56
```
57
58
### @Morph Annotation
59
60
Annotation for controlling polymorphic behavior of fields and types.
61
62
```java { .api }
63
/**
64
* Annotation for controlling polymorphic morphing behavior
65
* Affects how non-final POJOs and interfaces are handled
66
*/
67
@Target({ElementType.FIELD, ElementType.TYPE})
68
@Retention(RetentionPolicy.RUNTIME)
69
public @interface Morph {
70
71
/**
72
* Whether to enable morphing for this field or type
73
* @return true to enable morphing, false to disable
74
*/
75
boolean value() default true;
76
}
77
```
78
79
## Usage Examples
80
81
### Basic Field Tagging
82
83
```java
84
import io.protostuff.Tag;
85
import io.protostuff.Exclude;
86
87
/**
88
* User class with explicit field numbering
89
*/
90
public class User {
91
92
@Tag(1)
93
public String name;
94
95
@Tag(2)
96
public int age;
97
98
@Tag(3)
99
public String email;
100
101
@Tag(4)
102
public boolean active;
103
104
// This field will not be serialized
105
@Exclude
106
public String password;
107
108
// This field will not be serialized either
109
@Exclude
110
public transient String sessionToken;
111
}
112
```
113
114
### Field Aliases and Naming
115
116
```java
117
import io.protostuff.Tag;
118
119
/**
120
* Class demonstrating field aliases
121
*/
122
public class Product {
123
124
@Tag(value = 1, alias = "product_id")
125
public long id;
126
127
@Tag(value = 2, alias = "product_name")
128
public String name;
129
130
@Tag(value = 3, alias = "unit_price")
131
public double price;
132
133
@Tag(value = 4, alias = "is_available")
134
public boolean available;
135
136
// Alias helps with naming conventions different from Java
137
@Tag(value = 5, alias = "created_timestamp")
138
public long createdAt;
139
}
140
```
141
142
### Group Filtering
143
144
```java
145
import io.protostuff.Tag;
146
147
/**
148
* Class using group filters for conditional serialization
149
*/
150
public class UserProfile {
151
152
// Basic information (group 1)
153
@Tag(value = 1, groupFilter = 1)
154
public String name;
155
156
@Tag(value = 2, groupFilter = 1)
157
public int age;
158
159
// Contact information (group 2)
160
@Tag(value = 3, groupFilter = 2)
161
public String email;
162
163
@Tag(value = 4, groupFilter = 2)
164
public String phone;
165
166
// Sensitive information (group 3)
167
@Tag(value = 5, groupFilter = 3)
168
public String ssn;
169
170
@Tag(value = 6, groupFilter = 3)
171
public String creditCardNumber;
172
173
// Always included (no group filter)
174
@Tag(7)
175
public String publicId;
176
}
177
```
178
179
### Complex Field Configuration
180
181
```java
182
import io.protostuff.Tag;
183
import io.protostuff.Exclude;
184
import java.util.List;
185
import java.util.Map;
186
187
/**
188
* Complex class with various annotation combinations
189
*/
190
public class Order {
191
192
@Tag(1)
193
public String orderId;
194
195
@Tag(2)
196
public long customerId;
197
198
@Tag(value = 3, alias = "order_items")
199
public List<OrderItem> items;
200
201
@Tag(value = 4, alias = "order_total")
202
public double totalAmount;
203
204
@Tag(value = 5, groupFilter = 1) // Audit information
205
public long createdAt;
206
207
@Tag(value = 6, groupFilter = 1) // Audit information
208
public String createdBy;
209
210
@Tag(value = 7, alias = "shipping_address")
211
public Address shippingAddress;
212
213
@Tag(value = 8, alias = "billing_address")
214
public Address billingAddress;
215
216
// Additional metadata that might be filtered
217
@Tag(value = 9, groupFilter = 2)
218
public Map<String, String> metadata;
219
220
// Internal processing fields - excluded
221
@Exclude
222
public boolean processed;
223
224
@Exclude
225
public String internalNotes;
226
227
// Transient fields are also excluded by default
228
public transient Object tempData;
229
}
230
```
231
232
### Polymorphic Configuration with @Morph
233
234
```java
235
import io.protostuff.Tag;
236
import io.protostuff.Morph;
237
238
/**
239
* Base class with morphing enabled
240
*/
241
@Morph(true) // Enable morphing at the class level
242
public abstract class Shape {
243
244
@Tag(1)
245
public String name;
246
247
@Tag(2)
248
public String color;
249
}
250
251
/**
252
* Derived classes automatically inherit morphing behavior
253
*/
254
public class Circle extends Shape {
255
256
@Tag(3)
257
public double radius;
258
}
259
260
public class Rectangle extends Shape {
261
262
@Tag(3)
263
public double width;
264
265
@Tag(4)
266
public double height;
267
}
268
269
/**
270
* Class using polymorphic fields
271
*/
272
public class Drawing {
273
274
@Tag(1)
275
public String title;
276
277
// This field will use polymorphic serialization
278
@Tag(2)
279
@Morph(true) // Explicitly enable morphing for this field
280
public List<Shape> shapes;
281
282
// This field disables morphing (will serialize as base type only)
283
@Tag(3)
284
@Morph(false)
285
public Shape backgroundShape;
286
}
287
```
288
289
### Advanced Annotation Patterns
290
291
```java
292
import io.protostuff.Tag;
293
import io.protostuff.Exclude;
294
295
/**
296
* Advanced usage patterns with annotations
297
*/
298
public class AdvancedEntity {
299
300
// Required fields with low numbers for efficiency
301
@Tag(1)
302
public String id;
303
304
@Tag(2)
305
public String name;
306
307
// Optional fields with higher numbers
308
@Tag(10)
309
public String description;
310
311
@Tag(11)
312
public Map<String, Object> properties;
313
314
// Versioning fields
315
@Tag(20)
316
public int version;
317
318
@Tag(21)
319
public long lastModified;
320
321
// Audit fields with group filtering
322
@Tag(value = 30, groupFilter = 100, alias = "created_timestamp")
323
public long createdAt;
324
325
@Tag(value = 31, groupFilter = 100, alias = "created_by_user")
326
public String createdBy;
327
328
@Tag(value = 32, groupFilter = 100, alias = "updated_timestamp")
329
public long updatedAt;
330
331
@Tag(value = 33, groupFilter = 100, alias = "updated_by_user")
332
public String updatedBy;
333
334
// Internal state - excluded from serialization
335
@Exclude
336
public boolean dirty;
337
338
@Exclude
339
public Object cache;
340
341
// Computed fields - excluded
342
@Exclude
343
public String displayName; // Computed from other fields
344
345
@Exclude
346
public boolean valid; // Computed validation state
347
}
348
```
349
350
## Schema Generation with Annotations
351
352
### Basic Schema Creation
353
354
```java
355
import io.protostuff.runtime.RuntimeSchema;
356
import io.protostuff.Schema;
357
358
// Schema generation respects annotations automatically
359
Schema<User> userSchema = RuntimeSchema.getSchema(User.class);
360
361
// Field numbers and aliases from @Tag annotations are used
362
System.out.println("Field 1 name: " + userSchema.getFieldName(1)); // "name"
363
System.out.println("Field number for 'email': " + userSchema.getFieldNumber("email")); // 3
364
365
// @Exclude fields are not included in the schema
366
List<Field<User>> fields = ((RuntimeSchema<User>) userSchema).getFields();
367
// password field will not be in the list
368
```
369
370
### Group-Based Field Filtering
371
372
```java
373
import java.util.Set;
374
375
// Create schema excluding sensitive fields (group 3)
376
Set<String> exclusions = Set.of("ssn", "creditCardNumber");
377
RuntimeSchema<UserProfile> filteredSchema = RuntimeSchema.createFrom(
378
UserProfile.class, exclusions, RuntimeEnv.ID_STRATEGY
379
);
380
381
// Or use group filters programmatically when processing
382
RuntimeSchema<UserProfile> fullSchema = RuntimeSchema.createFrom(UserProfile.class);
383
for (Field<UserProfile> field : fullSchema.getFields()) {
384
if (field.groupFilter == 3) {
385
System.out.println("Sensitive field: " + field.name);
386
// Handle sensitive fields specially
387
}
388
}
389
```
390
391
### Polymorphic Schema with Morphing
392
393
```java
394
import io.protostuff.runtime.DefaultIdStrategy;
395
import io.protostuff.runtime.IdStrategy;
396
397
// Configure strategy for morphing
398
int flags = IdStrategy.MORPH_NON_FINAL_POJOS | IdStrategy.MORPH_COLLECTION_INTERFACES;
399
DefaultIdStrategy strategy = new DefaultIdStrategy(flags);
400
401
// Register polymorphic types
402
strategy.registerPojo(Shape.class);
403
strategy.registerPojo(Circle.class);
404
strategy.registerPojo(Rectangle.class);
405
strategy.map(Shape.class, Circle.class);
406
strategy.map(Shape.class, Rectangle.class);
407
408
// Create schema with morphing support
409
Schema<Drawing> drawingSchema = RuntimeSchema.getSchema(Drawing.class, strategy);
410
411
// The schema will handle polymorphic Shape objects in the shapes list
412
```
413
414
## Annotation Processing Rules
415
416
### Field Number Assignment
417
418
1. **Explicit @Tag**: Use the specified field number
419
2. **No @Tag**: Assign field numbers automatically in declaration order
420
3. **Mixed Usage**: Explicit tags take precedence, automatic assignment fills gaps
421
4. **Validation**: Field numbers must be unique and in valid range [1, 2^29-1]
422
423
### Field Exclusion Priority
424
425
1. **@Exclude annotation**: Highest priority - field is always excluded
426
2. **transient modifier**: Excluded by default (can be overridden with @Tag)
427
3. **static modifier**: Always excluded (cannot be overridden)
428
4. **Accessibility**: Private fields may need reflection access
429
430
### Alias Handling
431
432
1. **Wire Format**: Aliases are used in the protobuf wire format
433
2. **Field Lookup**: Both original name and alias can be used for field lookup
434
3. **Precedence**: Alias takes precedence over original field name in wire format
435
4. **Uniqueness**: Aliases must be unique within the message
436
437
## Best Practices
438
439
1. **Explicit Field Numbers**: Use @Tag with explicit numbers for stable schemas
440
2. **Reserved Ranges**: Reserve number ranges for different types of fields
441
3. **Group Filtering**: Use group filters for conditional serialization scenarios
442
4. **Meaningful Aliases**: Use aliases that match your wire format naming conventions
443
5. **Exclude Sensitive Data**: Always exclude passwords, tokens, and sensitive information
444
6. **Version Compatibility**: Plan field number assignments for schema evolution
445
7. **Documentation**: Document the purpose of group filters and special annotations
446
447
## Error Handling
448
449
Common issues with annotation usage:
450
451
- `IllegalArgumentException` - Invalid field numbers (out of range or duplicate)
452
- `RuntimeException` - Conflicting field configurations
453
- Field number conflicts when mixing @Tag and automatic assignment
454
- Invalid group filter values in certain contexts
455
- Alias conflicts with existing field names
456
457
## Interaction with IdStrategy
458
459
Annotations work in conjunction with IdStrategy configuration:
460
461
- **@Morph + MORPH_NON_FINAL_POJOS**: Enables polymorphic serialization
462
- **Group filters + custom strategy**: Custom field filtering logic
463
- **@Exclude + field exclusions**: Programmatic exclusions complement annotation exclusions
464
- **@Tag aliases + custom naming**: Strategy can provide additional naming transformations