JSON DOM

Argentum JSON module has effective Writer and Parser that reads and writes generic application data structures to and from JSONs. It's a preferred way of using JSON in Argentum. Though in rare cases when an application has to deal with arbitrary JSONs not connected to any application data structures (for example when application is itself a JSON editor/transformer) it may be desired to read JSON into a JSON Document Object Model (DOM) and handle it as such.

For this purpose Argentum JSON module supports JSON DOM:

using sys { log }
using json { Parser, Writer, read }

// This is a JSON we work with
text = "
    {
        "x": 1,
        "z": {"a":"sss"},
        "y": "asdf"
    }
";

// Read it to the DOM, where `root` is a root node
root = read(Parser.init(text));

// Write it back to JSON and print it:
log(root.write(Writer).toStr());

// This prints: {"z":{"a":"sss"},"x":1,"y":"asdf"}

DOM API

// Reads JSON (or its subtree at which the parser is positioned)
// into a tree of DOM elements.
// Parser interface can be used to check for completeness and errors.
fn read(input Parser) @Node;

// A common interface for all DOM nodes.
// All JSON DOM nodes can `write` to the writer.
interface Node {
   write(output Writer) Writer;
}

// Classes that represent different JSON node types:
class JNull{ +Node; }                     // Represents null nodes
class JNum{  +Node; n = 0.0; }            // Numeric node
class JStr{  +Node; s = ""; }             // String node
class JBool{ +Node; b = false}            // Boolean node
class JArr{  +Node; +Array(Node); }       // Array node (it's just an Argentum array of nodes)
class JObj{  +Node; +Map(String, Node); } // Object node (its just a Argentum map String->Node)

// Functions that help build JSON nodes:
fn jnull() @JNull;  // Creates a new null node.
fn jnum(n double) @JNum;     // Creates a numeric node, sugar for JNum.{ _.n := n }
fn jbool(b bool) @JBool;
fn jstr(s str) @Jstr;
fn jarr(itemMaker(JArr)) @Jarr // Creates an array: jarr{ _.append(jbool(false)... }
fn jobj(fieldMaker(JObj)) @JObj // Create an object: iobj{ _["x"]:=jnum(42) }

Access DOM data

In this example we read DOM and access its elements. It's worth mentioning that since we read data in a way that we expect a structure in it, it definitely should be processed in a StAX mode, not in a DOM mode, but since it's hard to make-up a concise scenario of DOM usage, let it be this way.

// Source JSON - a table represented by an array of objects
text = "
    [
        {"name": "Andrey", "height": 6.5},
        {"name": "Katy", "height": 5.8}
    ]
";

// Read
root = read(Parser.init(text));

// Scan and print:
// Here we first check if the root object is an array
// and if so, iterate over it, processing only array items that are JSON objects
// For each item `i` we check if it contains two fields
// and if fields are of type string and numeric node.
// If all checks succeed, we print their values.
root~JArr ? _.each { _~JObj ? `i{
    i["name"] ? _~JStr ? `name
    i["height"] ? _~JNum ? `height
    log("{name.s}-{height.n} ")
}};

// This prints: Andrey-6.5 Katy-5.8

Read-modify-write

DOM containers are internally just standard Argentum arrays and maps, they don't use any specialized JSON API. All leaf nodes hold mutable fields of data that could be access/modified directly.

text = "
    {
        "x": 1,
        "z": {"a":"sss"},
        "y": "asdf"
    }
";
root = read(Parser.init(text));
root~JObj ? `r                       // if root is an object
r["x"] ? _~JNum ? `xn                // and it has a numeric field `x`
r["z"] ? _~JObj ?                    // and an object field `z`
      _["v"] := jnum(3.14 + xn.n);  // .. put in `z` a new numeric field `v` 

log(root.write(Writer.useSpaces(2)).toStr());

This prints:

{
  "z": {
    "a": "sss",
    "v": 4.14
  },
  "x": 1,
  "y": "asdf"
}

99.9% of JSON scenarios don't require using DOM. Instead it's more efficient and easier to read JSONs to you application data structures using StAX parser and streaming Writer.

JSON DOM is needed only if you make an application that edits/formats/processes the arbitrary JSONs not related to your application data structures. Such applications can add methods they need (like handle focus/layout/render etc.) directly to the Node interface and J* classes, flattening the class hierarchy.

Readiness

This JSON module with DOM, parser and Writer can be used in Argentum built from sources, and in the playground. It is not yet integrated into the binary demo.

Leave a Reply

Your email address will not be published. Required fields are marked *