Working with YAML¶
The kpointer-yamlkt module reads and mutates YamlKt YamlMap
and YamlList values by JSON Pointer. Like the JSON module, it is built on
kpointer-adapter but is used through convenience extensions on the YamlKt
types. Every mutation produces a new immutable value; the original is unchanged.
No wasmJs target
YamlKt does not publish a wasmJs artifact, so kpointer-yamlkt does not target wasmJs.
Its other targets (JVM, Android, iOS, Linux x64, JS) match the rest of kPointer.
Reading a value¶
YamlKt's YamlMap already declares operator fun get(key: Any?), which would silently shadow a
KPointer-based get extension. To avoid that trap, this module uses elementAt for pointer reads
rather than the [] operator:
val doc: YamlMap = Yaml.decodeYamlMapFromString(
"""
user:
name: Alice
age: 30
""".trimIndent()
)
val name: YamlElement? = doc.elementAt(KPointer.from("/user/name"))
println(name) // Alice
// Pass a string directly — KPointer.from is called for you
val same: YamlElement? = doc.elementAt("/user/name")
// Absent paths yield null
println(doc.elementAt("/user/email")) // null
YamlList works the same way.
Typed accessors¶
val user: YamlMap? = doc.mapAt("/user")
val tags: YamlList? = doc.listAt("/user/tags")
val name: YamlPrimitive? = doc.primitiveAt("/user/name")
They return null for an absent path and throw IllegalArgumentException on a type mismatch. Each
accessor also accepts a pre-parsed KPointer.
Checking whether a path exists¶
contains distinguishes "absent" from "present but YamlNull":
KPointer.from("/user/name") in doc // true
doc.contains(KPointer.from("/user/nullField")) // true, even though the value is YamlNull
doc.contains(KPointer.from("/missing")) // false
Mutating¶
val updated: YamlMap = doc.mutate {
"/user/name" to "Bob"
"/user/active" to true
"/user/age" to 42
"/user/ratio" to 3.14
"/user/temp" to null
remove("/user/deprecated")
}
Supported right-hand-side types: String, Boolean, Int, Long, Float, Double, null, and
KpaElement (wrap a YamlElement with .toKpaElement() to insert a nested map or list).
YamlList mutation mirrors the JSON array API:
val original: YamlList = Yaml.decodeYamlListFromString("[a, b, c]")
val replaced: YamlList = original.mutate { "/1" to "B" } // [a, B, c]
val appended: YamlList = original.mutate { "/-" to "d" } // [a, b, c, d]
val removed: YamlList = original.mutate { remove("/1") } // [a, c]
YAML-specific behavior¶
Primitive type inference¶
YamlKt stores every literal — number, boolean, or string — as a single String, with no preserved
type tag. This module classifies primitives per the YAML 1.2 core schema:
true,True,TRUE,false,False,FALSE→ boolean.- Anything that parses via
String.toLongOrNull()orString.toDoubleOrNull()→ number. - Everything else → string.
Consequences worth knowing:
- YAML 1.1 boolean spellings (
yes/no/on/off) are strings here, not booleans. - Exotic numeric forms (hex, octal,
.inf, underscore-separated digits) are strings too. If you need those, reach through the adapter'sbackingproperty to YamlKt's own API.
Compound map keys¶
YAML permits non-scalar keys — a sequence or map used as a map key. For example, in YAML:
The key [a, b] is a sequence, not a string. JSON Pointer segments are always strings, so there is
no pointer expression that can address a compound-keyed entry. kPointer exposes only the
literal-keyed entries of a YamlMap through pointer operations:
// A map with one compound key ([a, b]) and two literal keys (literal1, literal2)
val doc: YamlMap = /* the YamlMap above, built by YamlKt */
// Only literal keys are visible:
doc.elementAt("/literal1") // YamlLiteral("v1")
doc.elementAt("/literal2") // YamlLiteral("v2")
// The segment "a" does not match any literal key — compound keys are invisible:
doc.elementAt("/a") // null
The underlying YamlMap — compound keys intact — is still reachable via .asKpaStruct().backing
if you need to inspect or copy those entries directly.
Mutation drops compound-keyed entries
YamlMap.mutate { … } rebuilds the map from its literal-keyed snapshot, so compound-keyed
entries are silently dropped from the result:
val mutated = doc.mutate { "/literal1" to "newValue" }
// mutated has only "literal1" and "literal2" — the [a, b] entry is gone
If you must preserve compound-keyed entries across a mutation, manipulate the YamlMap
directly rather than through the pointer DSL.
Handing adapters to other libraries¶
When integrating with generic code that consumes the kPointer document model directly: