or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdconfiguration.mdindex.mdjson-tree-model.mdobject-serialization.mdstreaming-api.mdtype-adapters.md

annotations.mddocs/

0

# Annotations

1

2

Gson provides several annotations to control serialization and deserialization behavior directly on fields and classes without requiring configuration changes.

3

4

## @SerializedName

5

6

Specifies alternative names for fields during serialization and deserialization.

7

8

```java { .api }

9

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

10

@Retention(RetentionPolicy.RUNTIME)

11

public @interface SerializedName {

12

String value();

13

String[] alternate() default {};

14

}

15

```

16

17

**Basic usage:**

18

```java

19

public class Person {

20

@SerializedName("full_name")

21

private String name;

22

23

@SerializedName("years_old")

24

private int age;

25

}

26

27

// JSON: {"full_name":"Alice","years_old":30}

28

```

29

30

**Multiple alternative names for deserialization:**

31

```java

32

public class Person {

33

@SerializedName(value = "name", alternate = {"full_name", "firstName", "first_name"})

34

private String name;

35

}

36

37

// Can deserialize from any of these JSON formats:

38

// {"name":"Alice"}

39

// {"full_name":"Alice"}

40

// {"firstName":"Alice"}

41

// {"first_name":"Alice"}

42

```

43

44

## @Expose

45

46

Controls which fields are included in serialization and deserialization when using `excludeFieldsWithoutExposeAnnotation()`.

47

48

```java { .api }

49

@Target(ElementType.FIELD)

50

@Retention(RetentionPolicy.RUNTIME)

51

public @interface Expose {

52

boolean serialize() default true;

53

boolean deserialize() default true;

54

}

55

```

56

57

**Usage:**

58

```java

59

public class User {

60

@Expose

61

private String name;

62

63

@Expose(serialize = false) // Only for deserialization

64

private String password;

65

66

@Expose(deserialize = false) // Only for serialization

67

private String token;

68

69

private String internalId; // Not exposed

70

}

71

72

Gson gson = new GsonBuilder()

73

.excludeFieldsWithoutExposeAnnotation()

74

.create();

75

76

// Only fields marked with @Expose will be processed

77

```

78

79

## @Since

80

81

Marks fields as available since a specific API version.

82

83

```java { .api }

84

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

85

@Retention(RetentionPolicy.RUNTIME)

86

public @interface Since {

87

double value();

88

}

89

```

90

91

**Usage:**

92

```java

93

public class ApiResponse {

94

private String status;

95

96

@Since(1.1)

97

private String message;

98

99

@Since(2.0)

100

private List<String> errors;

101

}

102

103

// Only include fields from version 1.1 and later

104

Gson gson = new GsonBuilder()

105

.setVersion(1.1)

106

.create();

107

108

// 'status' and 'message' will be included, but 'errors' will be excluded

109

```

110

111

## @Until

112

113

Marks fields as available until a specific API version.

114

115

```java { .api }

116

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

117

@Retention(RetentionPolicy.RUNTIME)

118

public @interface Until {

119

double value();

120

}

121

```

122

123

**Usage:**

124

```java

125

public class LegacyResponse {

126

private String data;

127

128

@Until(2.0)

129

private String oldFormat; // Deprecated in version 2.0

130

131

@Since(2.0)

132

private String newFormat; // Added in version 2.0

133

}

134

135

// Version 1.5: includes 'data' and 'oldFormat'

136

Gson gson15 = new GsonBuilder().setVersion(1.5).create();

137

138

// Version 2.1: includes 'data' and 'newFormat'

139

Gson gson21 = new GsonBuilder().setVersion(2.1).create();

140

```

141

142

## @JsonAdapter

143

144

Specifies a custom TypeAdapter, JsonSerializer, JsonDeserializer, or TypeAdapterFactory for a field or class.

145

146

```java { .api }

147

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

148

@Retention(RetentionPolicy.RUNTIME)

149

public @interface JsonAdapter {

150

Class<?> value();

151

boolean nullSafe() default true;

152

}

153

```

154

155

**Field-level adapter:**

156

```java

157

public class Event {

158

private String name;

159

160

@JsonAdapter(DateAdapter.class)

161

private Date timestamp;

162

}

163

164

public class DateAdapter extends TypeAdapter<Date> {

165

private final DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

166

167

@Override

168

public void write(JsonWriter out, Date date) throws IOException {

169

if (date == null) {

170

out.nullValue();

171

} else {

172

out.value(format.format(date));

173

}

174

}

175

176

@Override

177

public Date read(JsonReader in) throws IOException {

178

if (in.peek() == JsonToken.NULL) {

179

in.nextNull();

180

return null;

181

}

182

try {

183

return format.parse(in.nextString());

184

} catch (ParseException e) {

185

throw new IOException(e);

186

}

187

}

188

}

189

```

190

191

**Class-level adapter:**

192

```java

193

@JsonAdapter(ColorAdapter.class)

194

public class Color {

195

private int red, green, blue;

196

197

public Color(int red, int green, int blue) {

198

this.red = red;

199

this.green = green;

200

this.blue = blue;

201

}

202

}

203

204

public class ColorAdapter extends TypeAdapter<Color> {

205

@Override

206

public void write(JsonWriter out, Color color) throws IOException {

207

if (color == null) {

208

out.nullValue();

209

} else {

210

String hex = String.format("#%02x%02x%02x", color.red, color.green, color.blue);

211

out.value(hex);

212

}

213

}

214

215

@Override

216

public Color read(JsonReader in) throws IOException {

217

if (in.peek() == JsonToken.NULL) {

218

in.nextNull();

219

return null;

220

}

221

222

String hex = in.nextString();

223

if (hex.startsWith("#")) {

224

hex = hex.substring(1);

225

}

226

227

int rgb = Integer.parseInt(hex, 16);

228

return new Color((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF);

229

}

230

}

231

232

// Usage: Color serializes as "#ff0000" instead of {"red":255,"green":0,"blue":0}

233

```

234

235

**Using with JsonSerializer/JsonDeserializer:**

236

```java

237

public class User {

238

private String name;

239

240

@JsonAdapter(PasswordSerializer.class)

241

private String password;

242

}

243

244

public class PasswordSerializer implements JsonSerializer<String>, JsonDeserializer<String> {

245

@Override

246

public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) {

247

return new JsonPrimitive("***"); // Always serialize as stars

248

}

249

250

@Override

251

public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {

252

return json.getAsString(); // Deserialize normally

253

}

254

}

255

```

256

257

**Using with TypeAdapterFactory:**

258

```java

259

@JsonAdapter(CaseInsensitiveEnumAdapterFactory.class)

260

public enum Status {

261

ACTIVE, INACTIVE, PENDING

262

}

263

264

public class CaseInsensitiveEnumAdapterFactory implements TypeAdapterFactory {

265

@Override

266

@SuppressWarnings("unchecked")

267

public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

268

Class<T> rawType = (Class<T>) type.getRawType();

269

if (!rawType.isEnum()) {

270

return null;

271

}

272

273

return new TypeAdapter<T>() {

274

@Override

275

public void write(JsonWriter out, T value) throws IOException {

276

if (value == null) {

277

out.nullValue();

278

} else {

279

out.value(value.toString());

280

}

281

}

282

283

@Override

284

@SuppressWarnings("unchecked")

285

public T read(JsonReader in) throws IOException {

286

if (in.peek() == JsonToken.NULL) {

287

in.nextNull();

288

return null;

289

}

290

291

String value = in.nextString().toUpperCase();

292

return (T) Enum.valueOf((Class<Enum>) rawType, value);

293

}

294

};

295

}

296

}

297

```

298

299

## Combining Annotations

300

301

Annotations can be combined for comprehensive control:

302

303

```java

304

public class Product {

305

@SerializedName("product_id")

306

@Since(1.0)

307

private String id;

308

309

@SerializedName("product_name")

310

@Expose

311

private String name;

312

313

@SerializedName("price_cents")

314

@JsonAdapter(MoneyAdapter.class)

315

private Money price;

316

317

@Until(2.0)

318

private String oldCategory;

319

320

@Since(2.0)

321

@SerializedName("category_info")

322

private Category category;

323

324

@Expose(serialize = false)

325

private String internalNotes;

326

}

327

```

328

329

## Annotation Processing Order

330

331

Gson processes annotations in the following priority:

332

333

1. **@JsonAdapter** - Takes highest precedence

334

2. **@SerializedName** - Controls field naming

335

3. **@Expose** - Controls field inclusion (when enabled)

336

4. **@Since/@Until** - Controls version-based inclusion

337

5. **Field modifiers** - Processed based on GsonBuilder configuration

338

339

**Example:**

340

```java

341

public class Example {

342

@JsonAdapter(CustomAdapter.class) // 1. Custom adapter used

343

@SerializedName("custom_field") // 2. Field name in JSON

344

@Expose(serialize = false) // 3. Only deserialize (if expose filtering enabled)

345

@Since(1.1) // 4. Only if version >= 1.1

346

private String field;

347

}

348

```

349

350

## Best Practices

351

352

**Use @SerializedName for API compatibility:**

353

```java

354

public class ApiModel {

355

@SerializedName("user_id")

356

private String userId; // Java convention: camelCase

357

358

@SerializedName("created_at")

359

private Date createdAt; // API uses snake_case

360

}

361

```

362

363

**Use @Expose for security:**

364

```java

365

public class User {

366

@Expose

367

private String username;

368

369

@Expose

370

private String email;

371

372

private String passwordHash; // Never expose sensitive data

373

374

@Expose(serialize = false)

375

private String password; // Accept for input, never output

376

}

377

```

378

379

**Use versioning for API evolution:**

380

```java

381

public class ApiResponse {

382

private String status;

383

384

@Until(1.9)

385

private String oldErrorMessage;

386

387

@Since(2.0)

388

private ErrorDetails errorDetails;

389

}

390

```

391

392

**Use @JsonAdapter for domain-specific serialization:**

393

```java

394

public class Order {

395

@JsonAdapter(CurrencyAdapter.class)

396

private BigDecimal totalAmount;

397

398

@JsonAdapter(TimestampAdapter.class)

399

private Instant orderTime;

400

401

@JsonAdapter(Base64Adapter.class)

402

private byte[] signature;

403

}

404

```