JSON and JSONB
Exposed works together with the JSON serialization library of your choice by allowing column definitions that accept generic serializer and deserializer arguments through the json() and jsonb() functions.
As databases store JSON values either in text or binary format, Exposed provides two types to account for any potential differences, if they exist.
Basic usage
The following example leverages kotlinx.serialization to support @Serializable classes. It uses a simpler form of json() that relies on the library's KSerializer interface:
Here's how the same Project and TeamsTable would be defined using Jackson with the jackson-module-kotlin dependency and the full form of json():
Supported types
The exposed-json extension provides the following additional types:
json()Maps to database
JSON. Used for storing JSON data in text format.Database-specific mappings:
SQLite: Maps to
TEXTSQLServer: Maps to
NVARCHAR(MAX)Oracle: Maps to
VARCHAR2(4000)
jsonb()Maps to database
JSONB. Used for storing JSON data in binary format.Database-specific mappings:
MySQL and H2: Maps to
JSONSQLite: Maps to
BLOBfrom version 3.45.0.0+; otherwise, not supportedOracle: Not supported
SQLite and JSONB
Storing JSON strings as JSONB binary format is supported in SQLite starting from version 3.45.0.0. To facilitate this, any JSON value set to a .jsonb() column will be automatically wrapped by the SQL function JSONB(). This applies to values set in DDL default clauses:
And to values set in DML operations:
This means that attempting to read the value directly as it is stored in the column would throw a decode exception due to the non-readable JSONB binary format. SQLite provides the SQL function JSON() when it is required to read the stored value in a text format, which Exposed uses by default whenever the column is included in a SELECT clause:
If this automatic function wrapping in queries is not wanted, it can be toggled off by setting the parameter castToJsonFormat to false. This parameter is ignored if any other databases are detected.
The JSON() function can also be applied to individual JSONB columns by using .castToJson().
JSON functions
Extract data
You can use JSON path strings to extract values as JSON or as a scalar value at a specific field/key. To do this, use the .extract() function:
Databases that support a path context root $ will have this value appended to the generated SQL path expression by default, so it is not necessary to include it in the provided argument string. In the above example, if MySQL is being used, the provided path arguments should be .name and .language respectively.
Check if data exists
To check whether data exists within a JSON expression, use the .exists() function:
Depending on the database, filter paths can be provided instead, as well as optional arguments:
Check if JSON contains an expression
To check whether an expression is contained within a JSON, use the .contains() function:
Depending on the database, you could also provide an optional path:
Cast data to JSON type
You can cast other supported data types, like text or JSONB, to the JSON data type using the .castToJson() function:
If supported by your database, this function can be used to cast valid JSON strings stored in a text column to the serializable class of your choosing.
JSON arrays
JSON columns also accept JSON arrays as input values, allowing structured data to be stored and manipulated directly in the database.
To define a JSON column that holds an array, use the json() function with the appropriate type. The following example defines JSON columns for arrays of primitive values and objects using the serializable data class Project:
To insert data into the JSON array columns, use standard Kotlin collections:
This results in the following SQL query: