The Document Model¶
Every kPointer adapter module — JSON, YAML, XML/HTML — represents document nodes through the same
small hierarchy of interfaces, defined in kpointer-adapter. Understanding this model lets you write
generic code that works across adapter families and helps you make sense of what the typed accessors
return.
The hierarchy¶
Every addressable value is a KpaElement. It is a sealed interface with exactly three concrete
forms:
| Interface | Represents | Addressed by |
|---|---|---|
KpaStruct |
Object/map-shaped node | String keys |
KpaList |
Array/list-shaped node | Integer indices |
KpaPrimitive |
Leaf value (string, number, boolean, null) | — (terminal node) |
A JSON object becomes a KpaStruct, a JSON array becomes a KpaList, and a JSON string, number,
boolean, or null becomes a KpaPrimitive. The same mapping applies to YAML and (with some shape
rules) to HTML/XML elements.
KpaStruct¶
KpaStruct exposes an object-shaped node addressable by string keys:
val struct: KpaStruct = /* obtained from elementAt / structAt / asKpaStruct() */
// Inspect the available keys
val keys: Set<String> = struct.keys
// Look up a single key; null if absent
val child: KpaElement? = struct["user"]
// Check presence (distinguishes absent from null-valued)
val present: Boolean = "user" in struct
// Snapshot as a plain map
val snapshot: Map<String, KpaElement> = struct.toMap()
Typed accessors on KpaStruct¶
Rather than casting the result of struct["key"] yourself, use the typed extension functions.
All return null for an absent path or a type mismatch:
val child: KpaStruct? = struct.structAt("/user")
val items: KpaList? = struct.listAt("/user/tags")
val name: KpaPrimitive? = struct.primitiveAt("/user/name")
// Primitive-unwrapping shortcuts
val str: String? = struct.stringAt("/user/name")
val flag: Boolean? = struct.booleanAt("/user/active")
val count: Long? = struct.longAt("/user/age")
val ratio: Double? = struct.doubleAt("/user/ratio")
Each accessor also accepts a pre-parsed KPointer to avoid re-parsing on repeated calls:
KpaList¶
KpaList exposes an array-shaped node:
val list: KpaList = /* obtained from elementAt / listAt / asKpaList() */
val size: Int = list.size
// Access by index; throws on out-of-range
val first: KpaElement = list[0]
// Snapshot as a plain list
val snapshot: List<KpaElement> = list.toList()
The same typed accessors work on KpaList — structAt, listAt, primitiveAt, stringAt,
booleanAt, longAt, doubleAt — each accepting a KPointer or a plain string. Pointer
segments still navigate into nested structure; the first segment must be a valid integer index.
KpaPrimitive¶
KpaPrimitive is a leaf value. Inspect its type and unwrap its content with the typed properties:
val prim: KpaPrimitive = /* obtained from primitiveAt / stringAt etc. */
// Type flags
prim.isString // true for string values
prim.isBoolean // true for booleans
prim.isNumber // true for integers and floats
prim.isNull // true for null
// Unwrapping (null when type does not match)
val s: String? = prim.stringOrNull
val b: Boolean? = prim.booleanOrNull
val l: Long? = prim.longOrNull // null if fractional
val d: Double? = prim.doubleOrNull
// Human-readable form: unquoted string for strings, JSON literal for everything else
val display: String = prim.renderedString()
Long vs Double
longOrNull returns null for fractional numbers. For integer-valued numbers, both
longOrNull and doubleOrNull are non-null. Prefer longOrNull when you expect a count,
index, or other integer quantity.
Converting back to native types¶
When you have a KpaElement and need the underlying native representation, each adapter module
provides a conversion extension:
| Module | Extension |
|---|---|
kpointer-kxs |
KpaElement.toJsonElement() |
kpointer-yamlkt |
KpaElement.toYamlElement() |
The kpointer-ksoup module is read-only and has no corresponding conversion — the native KSoup
Element is reachable through the backing property of the adapter.
Relationship to format-specific types¶
Each adapter module provides its own format-specific typed accessors (objectAt / arrayAt for
JSON; mapAt / listAt for YAML) that return the native type directly (JsonObject, YamlMap,
…). The Kpa* accessors above return the adapter representation instead, which is useful when you
are writing generic code across adapter families or passing values to a library that consumes
KpaStruct / KpaList directly.
Going deeper¶
The kpointer-adapter module also defines mutable variants (RebuildableKpaStruct,
RebuildableKpaList) and the KpaElementFactory interface that powers mutate. Those are relevant
if you are implementing a custom adapter — not for everyday use.