CX+AI

Read some JSON and use it

A document in your source stays a document — no parse step, no conversion calls

Read some JSON and use it

Most languages make JSON a library: parse a string, get a bag of something, convert every field by hand. In CX+AI it is a type, and a document you wrote in your source stays a document you can read.

One page. Everything here was run.


Write it as itself

A _json block is not a string. There is not one escaped quote in it, the compiler parses it when the file is compiled, and a malformed document is an error on its own line — not a bad handle discovered somewhere else at run time.

_json fleet {
  {
    "port":  "Valletta",
    "ships": [
      { "name": "Nostromo",  "crew": 7,  "hull": 0.82 },
      { "name": "Sulaco",    "crew": 12, "hull": 1.00 }
    ]
  }
}

printf("out of %s\n", fleet["port"]);
out of Valletta

From the next line on, fleet is an ordinary json handle. A literal inside a loop never re-parses, because there is no text to parse.


Reach into it with a path

Not a query language — a path. It crosses objects and arrays the same way, and the value that comes back is a value: it goes straight into a typed variable, into printf, into arithmetic.

first.s = fleet["ships"][0]["name"];       // "Nostromo"
hull.f  = fleet["ships"][1]["hull"];       // 1.0

No conversion step anywhere. The variable's own type is the instruction: .s reads a string, .f reads a double. That is the whole of what atoi, strtod and a %-format you got wrong used to be for.


Ask the handle about itself with ->

count is how many, type is what kind, valid is whether it is still live. It is spelled as a field because that is what it is — an attribute of the thing you are holding, with no function call standing between you and it.

printf("%d fields, %d ships\n", fleet->count, fleet["ships"]->count);
2 fields, 2 ships

Type 5 is an object and type 4 is an array, so fleet->type and fleet["ships"]->type answer 5 and 4.


Loop it

foreach walks the container; jsonGet hands you the current element and jsonKey its key when you are walking an object.

json ships = fleet["ships"];
json ship;
crew.i = 0;

foreach ships {
    ship = jsonGet(ships);
    crew = crew + ship["crew"];
    printf("  %-10s crew %2d\n", ship["name"], ship["crew"]);
}
printf("%d hands aboard\n", crew);
  Nostromo   crew  7
  Sulaco     crew 12
19 hands aboard

Grow it by assigning into a path that does not exist

Nothing below was declared. manifest is not declared, manifest.hands is not declared, and neither needs to be: the assignment builds the path it is given. No schema, no allocation, no sizes, no cleanup.

fleet["manifest"]["hands"]  = crew;
fleet["manifest"]["berths"] = ships->count;

A document that arrives as text is the same type

parseDoc is for the shape you did not know when you wrote the program — a request body, a config file, a model's answer. What you did know goes in a _json block above.

string sent = "{ \"port\": 8080, \"host\": \"localhost\", \"ratio\": 0.75 }";
json cfg = parseDoc(sent);

port.i  = cfg["port"];
host.s  = cfg["host"];
ratio.f = cfg["ratio"];

printf("%s:%d at %.2f\n", host, port, ratio);
localhost:8080 at 0.75

Three reads of one document, three types, no conversion call. jsonExport takes it back the other way.


The four things worth knowing

A missing key reads as nothing, not as an error. cfg["nope"] into an int sink is 0 and into a string sink is empty. If a key's absence matters to you, ask: if (cfg["nope"]->type == 0).

if (doc <= 0) does not mean what it looks like. It compares the document's value, not whether the handle is good. Ask the question you mean:

json doc = parseDoc("{\"a\":1}");
if (doc->valid) { printf("usable\n"); }
usable

A container passed to a function is shared, not copied. function f(json d) gets the caller's document, and what it writes the caller sees. That is the point; there is no byref keyword to remember.

Reassigning one container to another is refused, with a warning, because two owners on one object is a double free waiting to happen. Pass it to a function instead.


Where to go next