or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdconfiguration.mdcore-operations.mdcustom-serializers.mddsl-builders.mdindex.mdjson-elements.mdnaming-strategies.mdplatform-extensions.md

annotations.mddocs/

0

# Annotations

1

2

Annotations for controlling JSON serialization behavior including alternative property names and polymorphic serialization.

3

4

## Capabilities

5

6

### JsonNames

7

8

Annotation for specifying alternative property names during deserialization.

9

10

```kotlin { .api }

11

/**

12

* Indicates that the field can be represented in JSON with multiple possible alternative names

13

* Json format recognizes this annotation and can decode data using any of the alternative names

14

* Does not affect JSON encoding in any way

15

* @param names Alternative names for the property during deserialization

16

*/

17

@Target(AnnotationTarget.PROPERTY)

18

annotation class JsonNames(vararg val names: String)

19

```

20

21

**Usage Examples:**

22

23

```kotlin

24

@Serializable

25

data class User(

26

val id: Int,

27

@JsonNames("user_name", "username", "login")

28

val name: String,

29

@JsonNames("user_email", "email_address")

30

val email: String,

31

@JsonNames("is_active", "enabled")

32

val active: Boolean = true

33

)

34

35

// All of these JSON inputs will deserialize successfully

36

val json1 = """{"id":1,"name":"Alice","email":"alice@example.com","active":true}"""

37

val json2 = """{"id":1,"user_name":"Alice","user_email":"alice@example.com","is_active":true}"""

38

val json3 = """{"id":1,"username":"Alice","email_address":"alice@example.com","enabled":true}"""

39

val json4 = """{"id":1,"login":"Alice","user_email":"alice@example.com","is_active":true}"""

40

41

val user1 = Json.decodeFromString<User>(json1)

42

val user2 = Json.decodeFromString<User>(json2)

43

val user3 = Json.decodeFromString<User>(json3)

44

val user4 = Json.decodeFromString<User>(json4)

45

// All create the same User object

46

47

// But encoding always uses the original property name

48

val encoded = Json.encodeToString(user1)

49

// {"id":1,"name":"Alice","email":"alice@example.com","active":true}

50

51

// Legacy API compatibility

52

@Serializable

53

data class ApiResponse(

54

@JsonNames("success", "ok", "status")

55

val isSuccess: Boolean,

56

@JsonNames("msg", "message", "error_message")

57

val responseMessage: String,

58

@JsonNames("payload", "result", "response_data")

59

val data: JsonElement?

60

)

61

62

// Handles multiple API versions

63

val v1Response = """{"success":true,"msg":"OK","payload":{"user_id":123}}"""

64

val v2Response = """{"ok":true,"message":"OK","result":{"user_id":123}}"""

65

val v3Response = """{"status":true,"error_message":"OK","response_data":{"user_id":123}}"""

66

67

// All deserialize to same object

68

val response1 = Json.decodeFromString<ApiResponse>(v1Response)

69

val response2 = Json.decodeFromString<ApiResponse>(v2Response)

70

val response3 = Json.decodeFromString<ApiResponse>(v3Response)

71

```

72

73

### JsonClassDiscriminator

74

75

Annotation for specifying custom class discriminator key for polymorphic serialization.

76

77

```kotlin { .api }

78

/**

79

* Specifies key for class discriminator value used during polymorphic serialization

80

* Provided key is used only for annotated class and its subclasses

81

* This annotation is inheritable, so placing it on base class affects all subclasses

82

* @param discriminator Custom discriminator property name

83

*/

84

@Target(AnnotationTarget.CLASS)

85

annotation class JsonClassDiscriminator(val discriminator: String)

86

```

87

88

**Usage Examples:**

89

90

```kotlin

91

@Serializable

92

@JsonClassDiscriminator("message_type")

93

abstract class BaseMessage {

94

abstract val timestamp: Long

95

}

96

97

@Serializable

98

data class TextMessage(

99

override val timestamp: Long,

100

val content: String

101

) : BaseMessage()

102

103

@Serializable

104

data class ImageMessage(

105

override val timestamp: Long,

106

val imageUrl: String,

107

val caption: String?

108

) : BaseMessage()

109

110

@Serializable

111

data class SystemMessage(

112

override val timestamp: Long,

113

val systemCode: String

114

) : BaseMessage()

115

116

// Configure polymorphic serialization

117

val json = Json {

118

serializersModule = SerializersModule {

119

polymorphic(BaseMessage::class) {

120

subclass(TextMessage::class)

121

subclass(ImageMessage::class)

122

subclass(SystemMessage::class)

123

}

124

}

125

}

126

127

// Serialization uses custom discriminator

128

val textMessage: BaseMessage = TextMessage(System.currentTimeMillis(), "Hello World")

129

val encoded = json.encodeToString(textMessage)

130

// {"message_type":"TextMessage","timestamp":1634567890123,"content":"Hello World"}

131

132

val decoded = json.decodeFromString<BaseMessage>(encoded)

133

134

// Different discriminator for different hierarchies

135

@Serializable

136

@JsonClassDiscriminator("event_type")

137

sealed class Event {

138

@Serializable

139

data class UserLogin(val userId: String, val timestamp: Long) : Event()

140

141

@Serializable

142

data class UserLogout(val userId: String, val sessionDuration: Long) : Event()

143

}

144

145

@Serializable

146

@JsonClassDiscriminator("shape_kind")

147

sealed class Shape {

148

@Serializable

149

data class Circle(val radius: Double) : Shape()

150

151

@Serializable

152

data class Rectangle(val width: Double, val height: Double) : Shape()

153

}

154

155

// Each hierarchy uses its own discriminator

156

val eventJson = Json {

157

serializersModule = SerializersModule {

158

polymorphic(Event::class) {

159

subclass(Event.UserLogin::class)

160

subclass(Event.UserLogout::class)

161

}

162

polymorphic(Shape::class) {

163

subclass(Shape.Circle::class)

164

subclass(Shape.Rectangle::class)

165

}

166

}

167

}

168

169

val login: Event = Event.UserLogin("user123", System.currentTimeMillis())

170

val circle: Shape = Shape.Circle(5.0)

171

172

val loginJson = eventJson.encodeToString(login)

173

// {"event_type":"UserLogin","userId":"user123","timestamp":1634567890123}

174

175

val circleJson = eventJson.encodeToString(circle)

176

// {"shape_kind":"Circle","radius":5.0}

177

```

178

179

### JsonIgnoreUnknownKeys

180

181

Annotation for allowing unknown properties in specific classes during deserialization.

182

183

```kotlin { .api }

184

/**

185

* Specifies that encounters of unknown properties in input JSON should be ignored

186

* instead of throwing SerializationException for this specific class

187

* Allows selective ignoring of unknown keys without affecting global Json configuration

188

*/

189

@Target(AnnotationTarget.CLASS)

190

annotation class JsonIgnoreUnknownKeys

191

```

192

193

**Usage Examples:**

194

195

```kotlin

196

@Serializable

197

@JsonIgnoreUnknownKeys

198

data class FlexibleConfig(

199

val host: String,

200

val port: Int,

201

val ssl: Boolean = false

202

)

203

204

@Serializable

205

data class StrictConfig(

206

val apiKey: String,

207

val endpoint: String

208

)

209

210

// FlexibleConfig ignores unknown properties

211

val flexibleJson = """{"host":"localhost","port":8080,"ssl":true,"unknown_field":"ignored","extra":123}"""

212

val flexibleConfig = Json.decodeFromString<FlexibleConfig>(flexibleJson)

213

// Success: FlexibleConfig(host="localhost", port=8080, ssl=true)

214

215

// StrictConfig throws exception for unknown properties

216

val strictJson = """{"apiKey":"secret","endpoint":"https://api.example.com","unknown":"field"}"""

217

// Json.decodeFromString<StrictConfig>(strictJson)

218

// Would throw SerializationException: Encountered an unknown key 'unknown'

219

220

// Selective flexibility in complex structures

221

@Serializable

222

data class ApiRequest(

223

val method: String,

224

val path: String,

225

@JsonIgnoreUnknownKeys

226

val headers: Headers,

227

val body: RequestBody

228

)

229

230

@Serializable

231

@JsonIgnoreUnknownKeys

232

data class Headers(

233

val contentType: String,

234

val authorization: String? = null

235

)

236

237

@Serializable

238

data class RequestBody(

239

val data: JsonElement

240

)

241

242

val requestJson = """

243

{

244

"method": "POST",

245

"path": "/api/users",

246

"headers": {

247

"contentType": "application/json",

248

"authorization": "Bearer token123",

249

"x-custom-header": "ignored",

250

"user-agent": "also ignored"

251

},

252

"body": {

253

"data": {"name": "Alice", "email": "alice@example.com"}

254

}

255

}

256

"""

257

258

val request = Json.decodeFromString<ApiRequest>(requestJson)

259

// Success: Headers ignores unknown properties, but RequestBody must match exactly

260

261

// Evolution-friendly data classes

262

@Serializable

263

@JsonIgnoreUnknownKeys

264

data class UserProfile(

265

val id: String,

266

val name: String,

267

val email: String,

268

val createdAt: Long,

269

// Future versions might add more fields, but this version ignores them

270

)

271

272

// Can handle JSON from newer API versions

273

val futureJson = """

274

{

275

"id": "user123",

276

"name": "Alice",

277

"email": "alice@example.com",

278

"createdAt": 1634567890123,

279

"profilePicture": "https://example.com/pic.jpg",

280

"preferences": {"theme": "dark"},

281

"newFeature": true

282

}

283

"""

284

285

val profile = Json.decodeFromString<UserProfile>(futureJson)

286

// Success: Unknown fields are ignored

287

```

288

289

### Combining Annotations

290

291

Using multiple annotations together for flexible JSON handling.

292

293

**Usage Examples:**

294

295

```kotlin

296

@Serializable

297

@JsonIgnoreUnknownKeys

298

@JsonClassDiscriminator("notification_type")

299

sealed class Notification {

300

abstract val id: String

301

abstract val timestamp: Long

302

}

303

304

@Serializable

305

data class EmailNotification(

306

override val id: String,

307

override val timestamp: Long,

308

@JsonNames("email_address", "recipient", "to")

309

val email: String,

310

@JsonNames("email_subject", "title")

311

val subject: String,

312

@JsonNames("email_body", "content", "message")

313

val body: String

314

) : Notification()

315

316

@Serializable

317

data class PushNotification(

318

override val id: String,

319

override val timestamp: Long,

320

@JsonNames("device_token", "token", "device_id")

321

val deviceToken: String,

322

@JsonNames("push_title", "notification_title")

323

val title: String,

324

@JsonNames("push_body", "notification_body", "text")

325

val body: String

326

) : Notification()

327

328

val notificationJson = Json {

329

serializersModule = SerializersModule {

330

polymorphic(Notification::class) {

331

subclass(EmailNotification::class)

332

subclass(PushNotification::class)

333

}

334

}

335

}

336

337

// Handles various input formats with unknown fields

338

val legacyEmailJson = """

339

{

340

"notification_type": "EmailNotification",

341

"id": "email-001",

342

"timestamp": 1634567890123,

343

"email_address": "user@example.com",

344

"email_subject": "Welcome!",

345

"email_body": "Welcome to our service",

346

"legacy_field": "ignored",

347

"internal_id": 12345

348

}

349

"""

350

351

val modernPushJson = """

352

{

353

"notification_type": "PushNotification",

354

"id": "push-001",

355

"timestamp": 1634567890123,

356

"device_token": "abc123def456",

357

"push_title": "New Message",

358

"push_body": "You have a new message",

359

"priority": "high",

360

"ttl": 3600

361

}

362

"""

363

364

val emailNotification = notificationJson.decodeFromString<Notification>(legacyEmailJson)

365

val pushNotification = notificationJson.decodeFromString<Notification>(modernPushJson)

366

// Both deserialize successfully despite unknown fields and alternative names

367

```