Skip to content

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:

val pointer = KPointer.from("/user/name")
val name: String? = struct.stringAt(pointer)

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 KpaListstructAt, 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.