Appearance
aowlspt/json
Source: aowl/src/aowlspt/json.nim — 701 lines.
Reading JSON, for mods.
let name = body.field("Info.Nickname").asText let n = body.field("items").count for it in body.field("items").each: echo it.field("_id").asText
A mod on either side spends most of its time reading a document it did not build: a request body, a template out of the database, its own saved state. Without this it would either hand-scan strings or pull in a parser, and the first is what produces the class of bug where a nested "_id" is mistaken for the outer one.
It scans; it does not build a tree. A JsonRef is a pair of offsets into text somebody else owns, so field costs a walk of the document and nothing else — no allocation, no copy, and no lifetime to think about beyond the text it came from. That is the right trade for the access pattern here (a handful of paths out of each body) and the wrong one for walking a 40MB item table field by field, which is what count/each exist to avoid needing.
Paths are dotted, with [n] for array elements:
"Info.Nickname" "items[0]._id" "data[2].Slots[1]"
A key containing a dot is reachable with child, one step at a time. That is deliberate: an escaping rule inside the path syntax would be one more thing to get wrong at a call site that reads fine. Error semantics: field, child, and at return notFound in two cases: (1) a step in the path does not exist, or (2) the path navigation was incorrect (mismatched braces, reached array boundary, etc). The API does not distinguish these two cases. Callers should validate document structure before relying on specific paths; use exists() to check if a reference was found. Reading a notFound ref is safe (returns empty string or default values) and will not crash, but confidently reading a default value as if it were the actual data is a silent failure. This is intentional: distinguishing "absent" from "mis-navigated" would require a three-value result type, making every call site more verbose.
Types
JsonRef
nim
JsonRef* = object
text*: string
first*: int ## index of the value's first character
last*: int ## index of its last character, inclusive
found*: boolA view of one JSON value inside text. found distinguishes "the path is not there" from "the path is there and holds null", which is a distinction the game's own bodies rely on.
aowl/src/aowlspt/json.nim:42
Member
nim
Member* = object
name*: string
value*: string ## the member's value as raw JSONaowl/src/aowlspt/json.nim:511
Doc
nim
Doc* = object
fields*: seq[Member]
ok*: boolA JSON object, taken apart for editing. ok is false when the text was not an object -- reported rather than raised, because the text usually came from a request body and a malformed one is an answer to give, not a crash to take.
aowl/src/aowlspt/json.nim:515
List
nim
List* = object
items*: seq[string]
ok*: boolA JSON array, taken apart for editing. Elements stay as raw text.
aowl/src/aowlspt/json.nim:523
Routines
notFound
nim
func notFound*(): JsonRefaowl/src/aowlspt/json.nim:51
raw
nim
proc raw*(j: JsonRef): stringThe value exactly as it appears, "quotes" and all.
aowl/src/aowlspt/json.nim:164
exists
nim
proc exists*(j: JsonRef): boolaowl/src/aowlspt/json.nim:170
isNull
nim
proc isNull*(j: JsonRef): boolaowl/src/aowlspt/json.nim:172
isText
nim
proc isText*(j: JsonRef): boolaowl/src/aowlspt/json.nim:176
isObject
nim
proc isObject*(j: JsonRef): boolaowl/src/aowlspt/json.nim:180
isArray
nim
proc isArray*(j: JsonRef): boolaowl/src/aowlspt/json.nim:184
asText
nim
proc asText*(j: JsonRef; default: string = ""): stringA string value, unescaped. A non-string value comes back as written, so reading a number as text gives "42" rather than the empty string.
aowl/src/aowlspt/json.nim:188
asFloat
nim
proc asFloat*(j: JsonRef; default: float = 0.0): floataowl/src/aowlspt/json.nim:198
asInt
nim
proc asInt*(j: JsonRef; default: int = 0): intaowl/src/aowlspt/json.nim:251
asBool
nim
proc asBool*(j: JsonRef; default: bool = false): boolaowl/src/aowlspt/json.nim:255
child
nim
proc child*(j: JsonRef; key: string): JsonRefOne member of an object, by exact key. The step field is built from, and the way to reach a key that contains a dot.
aowl/src/aowlspt/json.nim:264
count
nim
proc count*(j: JsonRef): intThe number of elements in an array, or of members in an object. Zero for anything else, including a value that is not there.
aowl/src/aowlspt/json.nim:304
at
nim
proc at*(j: JsonRef; index: int): JsonRefOne element of an array. Out of range is notFound, not a crash: an index from a request body is not something a mod should have to bounds-check before it can read it.
aowl/src/aowlspt/json.nim:340
keys
nim
proc keys*(j: JsonRef): seq[string]The member names of an object, in document order.
aowl/src/aowlspt/json.nim:372
each
nim
proc each*(j: JsonRef): seq[JsonRef]Every element of an array, as views. Built once so a loop over it is a walk of a sequence rather than a rescan of the document per index -- at in a loop is quadratic and this is the reason not to write that.
aowl/src/aowlspt/json.nim:403
whole
nim
proc whole*(text: string): JsonRefThe document itself, as a value.
aowl/src/aowlspt/json.nim:434
field
nim
proc field*(j: JsonRef; path: string): JsonRefA dotted path from this value. An empty path is the value itself.
aowl/src/aowlspt/json.nim:474
field
nim
proc field*(text, path: string): JsonRefA dotted path from a document.
aowl/src/aowlspt/json.nim:493
members
nim
proc members*(j: JsonRef): seq[Member]aowl/src/aowlspt/json.nim:528
parseObject
nim
proc parseObject*(text: string): Docaowl/src/aowlspt/json.nim:565
parseObject
nim
proc parseObject*(j: JsonRef): Docaowl/src/aowlspt/json.nim:571
newDoc
nim
proc newDoc*(): Docaowl/src/aowlspt/json.nim:576
has
nim
proc has*(d: Doc; name: string): boolaowl/src/aowlspt/json.nim:578
getRaw
nim
proc getRaw*(d: Doc; name: string): stringaowl/src/aowlspt/json.nim:584
get
nim
proc get*(d: Doc; name: string): JsonRefaowl/src/aowlspt/json.nim:590
escapeText
nim
proc escapeText*(s: string): stringThe JSON string escapes. Present here as well as in aowlspt/server because a client-side mod editing a document must not have to import the server API to do it.
aowl/src/aowlspt/json.nim:596
quoted
nim
proc quoted*(s: string): stringaowl/src/aowlspt/json.nim:617
setRaw
nim
proc setRaw*(d: var Doc; name, rawValue: string)Sets a member, keeping its position if it is already there. Position matters less than it looks: it keeps a diff of two saved profiles readable, which is the difference between finding a bug and re-reading the whole file.
aowl/src/aowlspt/json.nim:619
setText
nim
proc setText*(d: var Doc; name, value: string)aowl/src/aowlspt/json.nim:629
setNumber
nim
proc setNumber*(d: var Doc; name: string; value: int)aowl/src/aowlspt/json.nim:630
setNumber
nim
proc setNumber*(d: var Doc; name: string; value: float)aowl/src/aowlspt/json.nim:631
setBool
nim
proc setBool*(d: var Doc; name: string; value: bool)aowl/src/aowlspt/json.nim:632
remove
nim
proc remove*(d: var Doc; name: string)aowl/src/aowlspt/json.nim:635
text
nim
proc text*(d: Doc): stringaowl/src/aowlspt/json.nim:642
parseArray
nim
proc parseArray*(text: string): Listaowl/src/aowlspt/json.nim:651
parseArray
nim
proc parseArray*(j: JsonRef): Listaowl/src/aowlspt/json.nim:661
newList
nim
proc newList*(): Listaowl/src/aowlspt/json.nim:670
len
nim
proc len*(l: List): intaowl/src/aowlspt/json.nim:672
at
nim
proc at*(l: List; index: int): JsonRefaowl/src/aowlspt/json.nim:674
add
nim
proc add*(l: var List; rawValue: string)aowl/src/aowlspt/json.nim:679
add
nim
proc add*(l: var List; d: Doc)aowl/src/aowlspt/json.nim:680
replaceAt
nim
proc replaceAt*(l: var List; index: int; rawValue: string)aowl/src/aowlspt/json.nim:682
removeAt
nim
proc removeAt*(l: var List; index: int)aowl/src/aowlspt/json.nim:686
text
nim
proc text*(l: List): stringaowl/src/aowlspt/json.nim:695

