Package-level declarations

The problem-detail model, its builder, and the JSON codec.

TypeRole
ProblemThe document. Five standard members as properties, everything else in extensions.
ProblemTypeA problem type — the type URI paired with the title that belongs to it.
ProblemValueThe value of an extension member: primitive, null, array or object.
problemThe builder. problem { status = 404; title = "..." }.
ProblemSerializerThe flattening codec, already attached to Problem.
ProblemExceptionA Problem that can be thrown. throw someType.exception(detail = "…").

Reading extension values is deliberately offered twice: problem.extensions["x"]?.string throws on a wrong-typed member, ?.stringOrNull returns null for it. RFC 9457 §3 requires consumers to ignore a member of an unexpected type, so *OrNull is what a conforming reader wants; the strict half exists for a caller that minted the problem type and wants a mismatch to surface.

Types

Link copied to clipboard
data class Problem(val type: String = ABOUT_BLANK, val status: Int? = null, val title: String? = null, val detail: String? = null, val instance: String? = null, val extensions: Map<String, ProblemValue> = emptyMap())

An RFC 9457 problem detail. This is the document carried by application/problem+json and application/problem+xml.

Link copied to clipboard

A JSON array as an extension value. Implements List<ProblemValue> by delegation, so it can be iterated and indexed directly.

Link copied to clipboard

Builds a Problem. Obtain one through problem, which also supplies the Json that encodes the typed payloads handed to extension and extensions. That defaults to ProblemJson unless the caller overrides it.

Link copied to clipboard
annotation class ProblemDsl

Scopes the builder DSL, so that a problem { } nested inside another cannot silently set members on the outer builder.

Link copied to clipboard
class ProblemException(val problem: Problem, cause: Throwable? = null) : RuntimeException

A Problem that can be thrown.

Link copied to clipboard

The explicit JSON null of an extension member.

Link copied to clipboard

A JSON object as an extension value. Implements Map<String, ProblemValue> by delegation, so members can be read with [] and iterated directly.

Link copied to clipboard

A scalar extension value: a string, a number, or a boolean.

Link copied to clipboard

The JSON codec for Problem, and Problem's own serializer.

Link copied to clipboard
interface ProblemType

A problem type carrying the three things §4 asks a type author to define: a type URI, a title, and the HTTP status code to use with it. Implementing this interface states those three once, instead of restating them at every throw site.

Link copied to clipboard
sealed interface ProblemValue

A value of an RFC 9457 extension member (§3.2).

Properties

Link copied to clipboard

The scalar parsed as a Boolean.

Link copied to clipboard

The scalar parsed as a Boolean, or null if this value is not a scalar or is not true/false.

Link copied to clipboard

The scalar parsed as a Double. Reads the wire literal, so a quoted "1.5" also returns 1.5.

Link copied to clipboard

The scalar parsed as a Double, or null if this value is not a scalar or does not parse.

Link copied to clipboard

The scalar parsed as an Int.

Link copied to clipboard

The scalar parsed as an Int, or null if this value is not a scalar or does not parse.

Link copied to clipboard

The scalar parsed as a Long. Reads the wire literal, so a quoted "30" also returns 30.

Link copied to clipboard

The scalar parsed as a Long, or null if this value is not a scalar or does not parse.

Link copied to clipboard

This value as a scalar.

Link copied to clipboard

This value as a scalar, or null if it is an array, an object or ProblemNull.

Link copied to clipboard

This value as an array.

Link copied to clipboard

This value as an array, or null if it is anything else.

Link copied to clipboard

The Json used to convert typed extension payloads when the caller supplies none. That covers the values passed to extensions(…) and extension(name, …), and the types read back by extensionsAs.

Link copied to clipboard

This value as an object.

Link copied to clipboard

This value as an object, or null if it is anything else.

Link copied to clipboard

The scalar's wire literal as text, with the quotes of a JSON string already removed.

Link copied to clipboard

The scalar's wire literal as text, or null if this value is not a scalar.

Functions

Link copied to clipboard
inline fun <T> Json.encodeToProblemValue(value: T): ProblemValue

Encodes value to a standalone ProblemValue, with the serializer resolved from T.

Encodes value to a standalone ProblemValue. ProblemBuilder.extension does the same thing for one whole extension member; this is for callers building a ProblemValue that is not itself a member, such as one element of a ProblemArray.

Link copied to clipboard
fun ProblemType.exception(detail: String? = null, instance: String? = null, cause: Throwable? = null): ProblemException

Raises this problem type as a ProblemException.

Link copied to clipboard
inline fun <T> Problem.extension(name: String, json: Json = ProblemJson): T?

Decodes a single extension member with the deserializer resolved from T, or returns null when the member is absent.

inline fun <T> ProblemBuilder.extension(name: String, value: T)

Adds one extension member holding an arbitrary @Serializable value, with the serializer resolved from T.

fun <T> Problem.extension(name: String, deserializer: DeserializationStrategy<T>, json: Json = ProblemJson): T?

Decodes a single extension member, or returns null when it is absent.

Link copied to clipboard
inline fun <T> ProblemBuilder.extensions(value: T)

Spreads value's own properties into the problem as sibling extension members, with the serializer resolved from T.

Link copied to clipboard
inline fun <T> Problem.extensionsAs(json: Json = ProblemJson): T

Decodes the whole extension map into T, with the deserializer resolved from T. Members T does not declare are ignored, as RFC 9457 §3.2 requires.

fun <T> Problem.extensionsAs(deserializer: DeserializationStrategy<T>, json: Json = ProblemJson): T

Decodes the whole extension map into T. This is the typed consuming edge.

Link copied to clipboard
inline fun problem(json: Json = ProblemJson, block: ProblemBuilder.() -> Unit): Problem

Entry point for the builder DSL.

Link copied to clipboard
fun ProblemType.problem(detail: String? = null, instance: String? = null): Problem

Builds a Problem for this type. detail and instance are the only per-occurrence fields.

Link copied to clipboard
fun problemLiteral(content: String, isString: Boolean): ProblemPrimitive

Builds a scalar from an already-encoded wire literal.

Link copied to clipboard

A boolean extension value, written as the unquoted literal true or false.

A floating-point extension value.

An integer extension value.

A 64-bit integer extension value.

A string extension value. It is written quoted, so ProblemPrimitive("30") is not the number 30.