Skip to content

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*: bool

A 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 JSON

aowl/src/aowlspt/json.nim:511

Doc ​

nim
  Doc* = object
    fields*: seq[Member]
    ok*: bool

A 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*: bool

A JSON array, taken apart for editing. Elements stay as raw text.

aowl/src/aowlspt/json.nim:523

Routines ​

notFound ​

nim
func notFound*(): JsonRef

aowl/src/aowlspt/json.nim:51

raw ​

nim
proc raw*(j: JsonRef): string

The value exactly as it appears, "quotes" and all.

aowl/src/aowlspt/json.nim:164

exists ​

nim
proc exists*(j: JsonRef): bool

aowl/src/aowlspt/json.nim:170

isNull ​

nim
proc isNull*(j: JsonRef): bool

aowl/src/aowlspt/json.nim:172

isText ​

nim
proc isText*(j: JsonRef): bool

aowl/src/aowlspt/json.nim:176

isObject ​

nim
proc isObject*(j: JsonRef): bool

aowl/src/aowlspt/json.nim:180

isArray ​

nim
proc isArray*(j: JsonRef): bool

aowl/src/aowlspt/json.nim:184

asText ​

nim
proc asText*(j: JsonRef; default: string = ""): string

A 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): float

aowl/src/aowlspt/json.nim:198

asInt ​

nim
proc asInt*(j: JsonRef; default: int = 0): int

aowl/src/aowlspt/json.nim:251

asBool ​

nim
proc asBool*(j: JsonRef; default: bool = false): bool

aowl/src/aowlspt/json.nim:255

child ​

nim
proc child*(j: JsonRef; key: string): JsonRef

One 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): int

The 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): JsonRef

One 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): JsonRef

The document itself, as a value.

aowl/src/aowlspt/json.nim:434

field ​

nim
proc field*(j: JsonRef; path: string): JsonRef

A 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): JsonRef

A 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): Doc

aowl/src/aowlspt/json.nim:565

parseObject ​

nim
proc parseObject*(j: JsonRef): Doc

aowl/src/aowlspt/json.nim:571

newDoc ​

nim
proc newDoc*(): Doc

aowl/src/aowlspt/json.nim:576

has ​

nim
proc has*(d: Doc; name: string): bool

aowl/src/aowlspt/json.nim:578

getRaw ​

nim
proc getRaw*(d: Doc; name: string): string

aowl/src/aowlspt/json.nim:584

get ​

nim
proc get*(d: Doc; name: string): JsonRef

aowl/src/aowlspt/json.nim:590

escapeText ​

nim
proc escapeText*(s: string): string

The 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): string

aowl/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): string

aowl/src/aowlspt/json.nim:642

parseArray ​

nim
proc parseArray*(text: string): List

aowl/src/aowlspt/json.nim:651

parseArray ​

nim
proc parseArray*(j: JsonRef): List

aowl/src/aowlspt/json.nim:661

newList ​

nim
proc newList*(): List

aowl/src/aowlspt/json.nim:670

len ​

nim
proc len*(l: List): int

aowl/src/aowlspt/json.nim:672

at ​

nim
proc at*(l: List; index: int): JsonRef

aowl/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): string

aowl/src/aowlspt/json.nim:695