or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdformatting.mdinclusion-exclusion.mdindex.mdobject-creation.mdobject-identity.mdobject-structure.mdpolymorphic-types.mdproperty-control.md

object-structure.mddocs/

0

# Object Structure

1

2

Annotations that modify how objects are structured in JSON, including unwrapping nested objects and controlling value representation.

3

4

## Capabilities

5

6

### JsonUnwrapped

7

8

Unwrap nested object properties into the parent object.

9

10

```java { .api }

11

/**

12

* Unwrap nested object properties into parent object

13

* @param enabled Whether to enable unwrapping (default: true)

14

* @param prefix Prefix to add to unwrapped property names

15

* @param suffix Suffix to add to unwrapped property names

16

*/

17

@JsonUnwrapped(boolean enabled = true,

18

String prefix = "",

19

String suffix = "")

20

public @interface JsonUnwrapped;

21

```

22

23

**Usage Examples:**

24

25

```java

26

public class Person {

27

private String firstName;

28

private String lastName;

29

30

@JsonUnwrapped

31

private Address address;

32

33

@JsonUnwrapped(prefix = "work_")

34

private Address workAddress;

35

}

36

37

public class Address {

38

private String street;

39

private String city;

40

private String zipCode;

41

}

42

43

// Without @JsonUnwrapped:

44

// {"firstName": "John", "lastName": "Doe", "address": {"street": "123 Main St", "city": "NYC", "zipCode": "10001"}}

45

46

// With @JsonUnwrapped:

47

// {"firstName": "John", "lastName": "Doe", "street": "123 Main St", "city": "NYC", "zipCode": "10001", "work_street": "456 Office Blvd", "work_city": "NYC", "work_zipCode": "10002"}

48

```

49

50

### JsonValue

51

52

Use a single method or field value as the JSON representation of the entire object.

53

54

```java { .api }

55

/**

56

* Use single value as JSON representation of entire object

57

* @param value Whether to use this as the JSON value (default: true)

58

*/

59

@JsonValue(boolean value = true)

60

public @interface JsonValue;

61

```

62

63

**Usage Examples:**

64

65

```java

66

public enum Status {

67

ACTIVE("active"),

68

INACTIVE("inactive"),

69

PENDING("pending");

70

71

private final String code;

72

73

Status(String code) {

74

this.code = code;

75

}

76

77

@JsonValue

78

public String getCode() {

79

return code;

80

}

81

}

82

83

// Serializes as: "active" instead of {"code": "active"}

84

85

public class Money {

86

private final BigDecimal amount;

87

private final String currency;

88

89

public Money(BigDecimal amount, String currency) {

90

this.amount = amount;

91

this.currency = currency;

92

}

93

94

@JsonValue

95

public String toString() {

96

return amount + " " + currency;

97

}

98

}

99

100

// Serializes as: "100.50 USD" instead of {"amount": 100.50, "currency": "USD"}

101

```

102

103

### JsonRawValue

104

105

Serialize string value as raw JSON without quotes or escaping.

106

107

```java { .api }

108

/**

109

* Serialize string value as raw JSON without quotes/escaping

110

* @param value Whether to use raw value serialization (default: true)

111

*/

112

@JsonRawValue(boolean value = true)

113

public @interface JsonRawValue;

114

```

115

116

**Usage Examples:**

117

118

```java

119

public class JsonContainer {

120

private String name;

121

122

@JsonRawValue

123

private String jsonData;

124

125

@JsonRawValue

126

private String configJson;

127

}

128

129

// If jsonData contains: {"settings": {"theme": "dark"}, "count": 42}

130

// Result: {"name": "container", "jsonData": {"settings": {"theme": "dark"}, "count": 42}, "configJson": {...}}

131

// Instead of: {"name": "container", "jsonData": "{\"settings\": {\"theme\": \"dark\"}, \"count\": 42}"}

132

```

133

134

### JsonKey

135

136

Use property value as Map key during serialization.

137

138

```java { .api }

139

/**

140

* Use property value as Map key during serialization

141

* @param value Whether to use this property as key (default: true)

142

*/

143

@JsonKey(boolean value = true)

144

public @interface JsonKey;

145

```

146

147

**Usage Examples:**

148

149

```java

150

public class KeyedItem {

151

@JsonKey

152

private String identifier;

153

154

private String name;

155

private String description;

156

}

157

158

// When used in a collection context, the identifier becomes the map key:

159

// List<KeyedItem> items -> Map<String, KeyedItem> structure

160

// [{"identifier": "item1", "name": "First"}, {"identifier": "item2", "name": "Second"}]

161

// becomes: {"item1": {"name": "First"}, "item2": {"name": "Second"}}

162

```

163

164

### JsonAnyGetter and JsonAnySetter

165

166

Handle dynamic properties with Map-based storage.

167

168

```java { .api }

169

/**

170

* Mark method returning Map to serialize as additional properties

171

* @param enabled Whether to enable any-getter behavior (default: true)

172

*/

173

@JsonAnyGetter(boolean enabled = true)

174

public @interface JsonAnyGetter;

175

176

/**

177

* Mark method/field as fallback for unrecognized properties during deserialization

178

* @param enabled Whether to enable any-setter behavior (default: true)

179

*/

180

@JsonAnySetter(boolean enabled = true)

181

public @interface JsonAnySetter;

182

```

183

184

**Usage Examples:**

185

186

```java

187

public class FlexibleObject {

188

private String name;

189

private String type;

190

191

private Map<String, Object> additionalProperties = new HashMap<>();

192

193

@JsonAnyGetter

194

public Map<String, Object> getAdditionalProperties() {

195

return additionalProperties;

196

}

197

198

@JsonAnySetter

199

public void setAdditionalProperty(String key, Object value) {

200

additionalProperties.put(key, value);

201

}

202

}

203

204

// JSON: {"name": "test", "type": "sample", "customField": "value", "count": 42}

205

// additionalProperties will contain: {"customField": "value", "count": 42}

206

// Serialization includes all additional properties at the top level

207

```

208

209

## Advanced Structure Patterns

210

211

### Nested Unwrapping

212

213

```java

214

public class Employee {

215

private String employeeId;

216

217

@JsonUnwrapped

218

private PersonalInfo personal;

219

220

@JsonUnwrapped(prefix = "contact_")

221

private ContactInfo contact;

222

223

@JsonUnwrapped(prefix = "work_", suffix = "_info")

224

private WorkInfo work;

225

}

226

227

public class PersonalInfo {

228

private String firstName;

229

private String lastName;

230

private LocalDate birthDate;

231

}

232

233

public class ContactInfo {

234

private String email;

235

private String phone;

236

}

237

238

public class WorkInfo {

239

private String department;

240

private String position;

241

}

242

243

// Result: {

244

// "employeeId": "E123",

245

// "firstName": "John", "lastName": "Doe", "birthDate": "1990-01-15",

246

// "contact_email": "john@company.com", "contact_phone": "555-0123",

247

// "work_department_info": "Engineering", "work_position_info": "Senior Developer"

248

// }

249

```

250

251

### Conditional Raw Values

252

253

```java

254

public class ConfigurableResponse {

255

private String status;

256

private String message;

257

258

@JsonRawValue

259

@JsonInclude(JsonInclude.Include.NON_NULL)

260

private String rawData; // Only included if not null, and as raw JSON

261

262

@JsonRawValue

263

private String getFormattedConfig() {

264

// Method can return formatted JSON string

265

return configService.getJsonConfig();

266

}

267

}

268

```

269

270

### Complex Value Objects

271

272

```java

273

public class Coordinate {

274

private final double x;

275

private final double y;

276

277

@JsonCreator

278

public Coordinate(@JsonProperty("x") double x, @JsonProperty("y") double y) {

279

this.x = x;

280

this.y = y;

281

}

282

283

@JsonValue

284

public String toWktString() {

285

return String.format("POINT(%f %f)", x, y);

286

}

287

288

// Custom deserializer would be needed to parse the WKT string back

289

}

290

291

// Serializes as: "POINT(10.500000 20.300000)"

292

```

293

294

### Dynamic Property Handling

295

296

```java

297

public class ApiResource {

298

private String id;

299

private String type;

300

301

// Store metadata separately

302

@JsonIgnore

303

private Map<String, Object> metadata = new HashMap<>();

304

305

// Store dynamic fields separately

306

@JsonIgnore

307

private Map<String, Object> dynamicFields = new HashMap<>();

308

309

@JsonAnyGetter

310

public Map<String, Object> getDynamicFields() {

311

Map<String, Object> all = new HashMap<>(metadata);

312

all.putAll(dynamicFields);

313

return all;

314

}

315

316

@JsonAnySetter

317

public void setDynamicField(String key, Object value) {

318

if (key.startsWith("meta_")) {

319

metadata.put(key.substring(5), value);

320

} else {

321

dynamicFields.put(key, value);

322

}

323

}

324

}

325

```

326

327

### Wrapper and Container Patterns

328

329

```java

330

public class ApiResponse<T> {

331

private String status;

332

private String message;

333

334

@JsonUnwrapped

335

private T data; // Unwrap the actual data into the response

336

337

private Map<String, Object> meta;

338

}

339

340

// Usage:

341

ApiResponse<User> response = new ApiResponse<>();

342

response.setData(new User("john", "john@email.com"));

343

344

// Result: {"status": "success", "message": "OK", "username": "john", "email": "john@email.com", "meta": {...}}

345

```