Skip to content

Paths and steps

Two ways address a node. A Step is the real one; a dotted path is sugar over it, smaller than it looks, and knowing exactly how small saves an afternoon.

Steps

root.Get(yamldoc.StringStep("server"), yamldoc.StringStep("port"))
servers.Get(yamldoc.IndexStep(0), yamldoc.StringStep("name"))
ports.Get(yamldoc.KeyStep(yamldoc.IntKey(8080)))
Step Matches
StringStep(s) A mapping entry whose key is the string s, spelt exactly so; StringStep("a.b") is the key a.b
IndexStep(i) The ith item of a sequence, zero-based; past the end is ErrNotFound
KeyStep(k) A mapping entry whose key is semantically equal to the typed Key: IntKey(8) matches 8, 0o10 and 0x8; BoolKey(true) matches true; a NullKey matches ~; BigIntKey, FloatKey, TimeKey and BinaryKey reach keys no other library can address at all

A StringStep matches string keys only. A key that resolves to an integer under the document's schema is reached with KeyStep(IntKey(n)), not with StringStep("n"). Where two entries have equal keys the lookup is ErrAmbiguous rather than a guess.

Steps go through anchors and stop at aliases: an anchored mapping is walked into with its anchor intact, and an alias is a reference, reached by AliasTarget rather than walked through by accident.

Dotted paths

path    = segment ( "." segment )*
segment = key index*  |  index+       (a bare index only as the first segment)
index   = "[" digits "]"

A path is a dotted list of string mapping keys, each optionally followed by bracketed integers, walked from the document root. A bracket means what the container it lands on can take: on a sequence, [n] is the nth item; on a mapping, it is the entry whose key is the integer n, matched by value, so ports[8080] finds 8080: and 0x1F90: alike. There is no wildcard, no filter, no negative index, no escape character and no quoting; PathSteps splits on every dot, reads each [n] as an IndexStep, and refuses an empty segment, a malformed bracket, a signed or zero-padded index, or ] inside a key with ErrPath. The bracket rule is applied by the Document helpers as they walk; a Step handed to Node.Get keeps its own meaning, so IndexStep on a mapping is still ErrKind there.

doc.Get("server.tls.cert")     // root → "server" → "tls" → "cert"
doc.Get("servers[0].port")     // root → "servers" → item 0 → "port"
doc.Get("matrix[1][0]")        // root → "matrix" → item 1 → item 0
doc.Get("ports[8080]")         // root → "ports" → the entry keyed 8080
doc.Get("[0].name")            // a root sequence: item 0 → "name"
doc.Get("")                    // the root
doc.Set("", value)             // ErrPath: Set and Remove need a step

Sequence elements

servers:
  - name: alpha
    port: 8080
  - name: beta
doc.Get("servers")             // the whole sequence
doc.Get("servers[1].name")     // "beta"
doc.Get("servers[2]")          // ErrNotFound: past the end
doc.Get("servers.name")        // ErrKind: a sequence has no keys
doc.Get("servers[0][0]")       // ErrNotFound: the first item has no key 0

Writing through an index follows the shape of the sequence rather than inventing one:

doc.Set(path, v) Effect
servers[0].port replaces port in the first item, comments intact
servers[1] replaces the second item whole
servers[2] appends, because 2 is the sequence's length
servers[5] ErrNotFound: a gap cannot be filled in
ports[9090] when ports is a mapping writes the entry keyed by the integer 9090, creating it if absent
groups[0].name when groups is absent creates groups as a one-item sequence holding {name: v}: a bracket never creates a mapping
groups[1].name when groups is absent ErrNotFound: only [0] can start a new sequence

doc.Remove("servers[0]") removes the item, and the items that survive keep their comments. Inside Edit, the same reach is IndexStep, with tx.Insert(servers, IndexStep(i), v) to add before i and tx.Replace to change an item in place.

Keys containing a dot

A path cannot reach a.b: because it reads as two segments, and it cannot reach a key containing [ or ] either, because those read as an index. Such a key is listed by Entries like any other, and StringStep reaches it; see Edit a value for writing one.

Non-string keys

doc.Get("ports.8080") compares the string 8080 to keys and finds nothing under a schema where 8080 is an integer; doc.Get("ports[8080]") finds it, because a bracket on a mapping is an integer key. Any other key type, a boolean, a float, a timestamp, needs KeyStep, or Lookup(key) from Node.Mapping().