Deserializer
Reflection-based deserializer that converts a Map (from parsed YAML) into Kotlin data class instances.
Algorithm
For each target data class the deserializer:
Resolves metadata -- reflects on the primary constructor and caches parameter info in a ConcurrentHashMap for fast repeated access.
Detects unknown keys -- any YAML key that does not match a constructor parameter (except the reserved
"configVersion") is reported as ConfigError.UnknownKey, with a Levenshtein-based "did you mean ...?" suggestion when a close match exists.Iterates constructor parameters and for each one:
@Transient -- skips the parameter entirely; Kotlin uses its default value.
Key lookup -- tries the parameter name, then any legacy names from @MigrateFrom.
Missing value -- if the parameter is optional (has a Kotlin default) it is skipped; if nullable it is set to
null; otherwise a ConfigError.MissingRequired is recorded.Deserialization -- delegates to deserializeValue which handles primitives, enums,
List,Set,Map, SecretString, custom serializers from the SerializerRegistry, and recursive nested data classes.@Range / @Pattern validation -- if the constraint is violated, an error is recorded and the field falls back to its Kotlin default (when available).
Constructs the instance via
KFunction.callBy, which honours Kotlin default values for any parameter not explicitly provided.
Supported types
| Category | Types |
|---|---|
| Primitives | String, Int, Long, Double, Float, Boolean |
| Special | SecretString, any enum class |
| Collections | List<T>, Set<T>, Map<K, V> (recursively typed) |
| Custom | Any type registered in SerializerRegistry |
| Nested | Any Kotlin data class (deserialized recursively) |
Annotation interactions
@Transient: Field is skipped; always uses Kotlin default value.
@MigrateFrom: Falls back to legacy key names when the current key is missing.
@Range: Validates numeric values are within bounds; falls back to default on violation.
@Pattern: Validates string values against a regex; falls back to default on violation.
Caching
Constructor metadata (ClassMetadata) is cached in a ConcurrentHashMap keyed by KClass, so reflection costs are paid only once per type even across multiple deserialization calls.
Example:
val registry = SerializerRegistry()
BuiltinSerializers.registerAll(registry)
val deserializer = Deserializer(registry)
val errors = ConfigErrorCollector()
val config = deserializer.deserialize(MyConfig::class, yamlMap, errors)
if (errors.hasErrors()) {
println(ConfigErrorFormatter().format(errors.all()))
}Since
1.0
Parameters
The SerializerRegistry used to resolve custom type serializers.
See also
Types
Cached reflection metadata for a data class, capturing its primary constructor and the set of valid parameter names.