0
# JSON Marshaling
1
2
Built-in JSON marshaling capabilities with customizable field names, subclass handling, and integration with streaming JSON APIs. The JSON marshaling feature generates efficient serialization code that works with the Immutables runtime marshaling framework.
3
4
## Capabilities
5
6
### JSON Marshaled Generation
7
8
Generate JSON marshaler for immutable types with streaming API support.
9
10
```java { .api }
11
/**
12
* Generate JSON marshaler for the annotated immutable type.
13
* Creates marshalers that integrate with the Immutables JSON framework
14
* for efficient streaming serialization and deserialization.
15
*/
16
@Target(ElementType.TYPE)
17
@Retention(RetentionPolicy.SOURCE)
18
@interface Json.Marshaled {}
19
```
20
21
**Usage Example:**
22
23
```java
24
import org.immutables.value.Value;
25
import org.immutables.value.Json;
26
import java.util.Optional;
27
import java.util.List;
28
29
@Value.Immutable
30
@Json.Marshaled
31
public interface Person {
32
String name();
33
int age();
34
Optional<String> email();
35
List<String> tags();
36
}
37
38
// JSON marshaling usage
39
Person person = ImmutablePerson.builder()
40
.name("Alice")
41
.age(30)
42
.email("alice@example.com")
43
.addTags("developer", "java")
44
.build();
45
46
// Serialize to JSON
47
String json = PersonMarshaler.instance().toString(person);
48
// {"name":"Alice","age":30,"email":"alice@example.com","tags":["developer","java"]}
49
50
// Deserialize from JSON
51
Person restored = PersonMarshaler.instance().fromString(json);
52
```
53
54
### Custom Field Names
55
56
Customize JSON field names for attributes.
57
58
```java { .api }
59
/**
60
* Specify custom JSON field name for an attribute.
61
* Overrides the default field name derived from the accessor method.
62
*/
63
@Target(ElementType.METHOD)
64
@Retention(RetentionPolicy.SOURCE)
65
@interface Json.Named {
66
/** Custom JSON field name */
67
String value();
68
}
69
```
70
71
**Usage Example:**
72
73
```java
74
import org.immutables.value.Value;
75
import org.immutables.value.Json;
76
77
@Value.Immutable
78
@Json.Marshaled
79
public interface ApiResponse {
80
@Json.Named("response_code")
81
int responseCode();
82
83
@Json.Named("data")
84
String responseData();
85
86
@Json.Named("timestamp_ms")
87
long timestampMillis();
88
}
89
90
// Generated JSON uses custom field names
91
ApiResponse response = ImmutableApiResponse.builder()
92
.responseCode(200)
93
.responseData("success")
94
.timestampMillis(System.currentTimeMillis())
95
.build();
96
97
String json = ApiResponseMarshaler.instance().toString(response);
98
// {"response_code":200,"data":"success","timestamp_ms":1640995200000}
99
```
100
101
### Field Exclusion
102
103
Exclude specific attributes from JSON serialization.
104
105
```java { .api }
106
/**
107
* Exclude an attribute from JSON marshaling.
108
* The attribute will not appear in serialized JSON output
109
* and will not be expected during deserialization.
110
*/
111
@Target(ElementType.METHOD)
112
@Retention(RetentionPolicy.SOURCE)
113
@interface Json.Ignore {}
114
```
115
116
**Usage Example:**
117
118
```java
119
import org.immutables.value.Value;
120
import org.immutables.value.Json;
121
import java.time.Instant;
122
123
@Value.Immutable
124
@Json.Marshaled
125
public interface UserAccount {
126
String username();
127
String email();
128
129
@Json.Ignore // Sensitive data not serialized
130
String passwordHash();
131
132
@Json.Ignore // Internal field not exposed via JSON
133
Instant lastModified();
134
135
boolean active();
136
}
137
138
// passwordHash and lastModified are excluded from JSON
139
UserAccount account = ImmutableUserAccount.builder()
140
.username("alice")
141
.email("alice@example.com")
142
.passwordHash("$2a$10$...")
143
.lastModified(Instant.now())
144
.active(true)
145
.build();
146
147
String json = UserAccountMarshaler.instance().toString(account);
148
// {"username":"alice","email":"alice@example.com","active":true}
149
```
150
151
### Force Empty Values
152
153
Force output of empty collections and null optional values.
154
155
```java { .api }
156
/**
157
* Force output of empty collections or null optional values in JSON.
158
* By default, empty collections and absent optionals are omitted
159
* from JSON output for compactness.
160
*/
161
@Target(ElementType.METHOD)
162
@Retention(RetentionPolicy.SOURCE)
163
@interface Json.ForceEmpty {}
164
```
165
166
**Usage Example:**
167
168
```java
169
import org.immutables.value.Value;
170
import org.immutables.value.Json;
171
import java.util.List;
172
import java.util.Optional;
173
import java.util.Map;
174
175
@Value.Immutable
176
@Json.Marshaled
177
public interface DataContainer {
178
String name();
179
180
@Json.ForceEmpty
181
List<String> items(); // Always include, even if empty
182
183
@Json.ForceEmpty
184
Optional<String> description(); // Always include, even if absent
185
186
Map<String, Object> metadata(); // Omitted if empty (default behavior)
187
}
188
189
// Empty collections and absent optionals are included when @Json.ForceEmpty
190
DataContainer container = ImmutableDataContainer.builder()
191
.name("test")
192
.build();
193
194
String json = DataContainerMarshaler.instance().toString(container);
195
// {"name":"test","items":[],"description":null}
196
// Note: metadata is omitted since it's empty and not marked @Json.ForceEmpty
197
```
198
199
### Import External Marshalers
200
201
Import static marshalers for use in generated marshaling code.
202
203
```java { .api }
204
/**
205
* Import static classes containing marshalers for use in generated code.
206
* Allows reuse of existing marshalers for complex nested types.
207
*/
208
@Target(ElementType.TYPE)
209
@Retention(RetentionPolicy.SOURCE)
210
@interface Json.Import {
211
/** Classes containing static marshalers to import */
212
Class<?>[] value();
213
}
214
```
215
216
**Usage Example:**
217
218
```java
219
import org.immutables.value.Value;
220
import org.immutables.value.Json;
221
import org.immutables.common.marshaling.Marshaler;
222
import org.immutables.common.marshaling.Marshaling;
223
import java.time.LocalDateTime;
224
225
// External marshaler class
226
public class CustomMarshalers {
227
public static final Marshaler<LocalDateTime> LOCAL_DATE_TIME =
228
Marshaling.fromString(LocalDateTime::toString, LocalDateTime::parse);
229
}
230
231
@Value.Immutable
232
@Json.Marshaled
233
@Json.Import(CustomMarshalers.class)
234
public interface Event {
235
String name();
236
LocalDateTime timestamp(); // Uses imported marshaler
237
String description();
238
}
239
240
// The generated marshaler uses CustomMarshalers.LOCAL_DATE_TIME
241
Event event = ImmutableEvent.builder()
242
.name("deployment")
243
.timestamp(LocalDateTime.now())
244
.description("Production deployment")
245
.build();
246
247
String json = EventMarshaler.instance().toString(event);
248
// {"name":"deployment","timestamp":"2023-01-01T10:30:00","description":"Production deployment"}
249
```
250
251
### Subclass Marshaling
252
253
Handle polymorphic types with expected subclasses.
254
255
```java { .api }
256
/**
257
* Specify expected subclasses for polymorphic marshaling.
258
* Generates marshaling code that can handle multiple implementations
259
* of an abstract type or interface.
260
*/
261
@Target(ElementType.TYPE)
262
@Retention(RetentionPolicy.SOURCE)
263
@interface Json.Subclasses {
264
/** Expected subclass types for polymorphic marshaling */
265
Class<?>[] value();
266
}
267
```
268
269
**Usage Example:**
270
271
```java
272
import org.immutables.value.Value;
273
import org.immutables.value.Json;
274
import java.util.List;
275
import java.util.Arrays;
276
import java.util.stream.Collectors;
277
278
// Base type
279
@Value.Immutable
280
@Json.Marshaled
281
@Json.Subclasses({Cat.class, Dog.class})
282
public interface Animal {
283
String name();
284
String type();
285
}
286
287
// Subclasses
288
@Value.Immutable
289
@Json.Marshaled
290
public interface Cat extends Animal {
291
@Value.Default
292
default String type() { return "cat"; }
293
boolean indoor();
294
}
295
296
@Value.Immutable
297
@Json.Marshaled
298
public interface Dog extends Animal {
299
@Value.Default
300
default String type() { return "dog"; }
301
String breed();
302
}
303
304
// Polymorphic marshaling
305
List<Animal> animals = Arrays.asList(
306
ImmutableCat.builder().name("Whiskers").indoor(true).build(),
307
ImmutableDog.builder().name("Rex").breed("Golden Retriever").build()
308
);
309
310
// AnimalMarshaler can handle both Cat and Dog instances
311
String json = animals.stream()
312
.map(animal -> AnimalMarshaler.instance().toString(animal))
313
.collect(Collectors.joining(",", "[", "]"));
314
```
315
316
## Generated Marshaler Classes
317
318
When `@Json.Marshaled` is applied, the annotation processor generates:
319
320
### Marshaler Class
321
322
- **Naming**: `{TypeName}Marshaler`
323
- **Singleton**: Static `instance()` method for singleton access
324
- **Thread-Safe**: Generated marshalers are thread-safe
325
326
### Serialization Methods
327
328
```java { .api }
329
// Generated marshaler methods
330
public final class PersonMarshaler extends Marshaler<Person> {
331
public static PersonMarshaler instance() { ... }
332
333
// String serialization
334
public String toString(Person instance) { ... }
335
336
// Writer serialization
337
public void toWriter(Person instance, Writer writer) throws IOException { ... }
338
339
// JsonGenerator serialization (Jackson integration)
340
public void toGenerator(Person instance, JsonGenerator generator) throws IOException { ... }
341
}
342
```
343
344
### Deserialization Methods
345
346
```java { .api }
347
// Generated unmarshaler methods
348
public final class PersonMarshaler extends Marshaler<Person> {
349
// String deserialization
350
public Person fromString(String json) throws IOException { ... }
351
352
// Reader deserialization
353
public Person fromReader(Reader reader) throws IOException { ... }
354
355
// JsonParser deserialization (Jackson integration)
356
public Person fromParser(JsonParser parser) throws IOException { ... }
357
}
358
```
359
360
## Integration with Marshaling Framework
361
362
The generated marshalers integrate with the `org.immutables.common.marshaling` framework:
363
364
```java
365
import org.immutables.common.marshaling.Marshaling;
366
367
// Create custom marshalers
368
Marshaler<LocalDate> dateMarshaler = Marshaling.fromString(
369
LocalDate::toString,
370
LocalDate::parse
371
);
372
373
// Combine with generated marshalers
374
Person person = PersonMarshaler.instance().fromString(jsonString);
375
```