serialization

Serialization in UCloud is provided by the jackson library. We use the following modules, on top of the core jackson module:

A jackson mapper is available in service-lib and is exported as defaultMapper in the dk.sdu.cloud package. The defaultMapper uses JSON as its data format. This mapper uses the following configuration:

ConfigDescription

DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES = false

Don't fail if an ignored property is encountered

DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES = false

Don't fail if a new property is introduced in the API

DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY = true

Provides flexibility in terms of compatibility

JsonParser.Feature.ALLOW_TRAILING_COMMA = true

Provides flexibility in terms of compatibility

JsonParser.Feature.ALLOW_COMMENTS = true

Provides flexibility in terms of compatibility

The reason for this configuration is to be as relaxed as possible in order to improve backwards-compatibility.

Sealed Classes

For a variety of reasons, including security, is generally not recommended that you use large class-hierarchies for request/response types. The one exception to this rule is sealed classes. This introduces a problem on the client-side of how to determine the correct type. To solve this problem, you must add annotations to the sealed class which tell Jackson to annotate the resulting JSON with a new key-value pair which include a type-hint.

Examples

Example: Using sealed classes with Jackson

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = TYPE_PROPERTY
)
@JsonSubTypes(
    JsonSubTypes.Type(value = LongRunningResponse.Timeout::class, name = "timeout"),
    JsonSubTypes.Type(value = LongRunningResponse.Result::class, name = "result")
)
sealed class LongRunningResponse<T> {
    data class Timeout<T>(
        val why: String = "The operation has timed out and will continue in the background"
    ) : LongRunningResponse<T>()

    data class Result<T>(
        val item: T
    ) : LongRunningResponse<T>()
}

Example: Using the defaultMapper instance to parse a JSON object

val result = defaultMapper.readValue<Page<Tool>>(jsonText)

Example: Using the defaultMapper instance to serialize an object to JSON

defaultMapper.writeValueAsString(SlackMessage(message))

Last updated