Mixers (Tritium Libraries): JSON

The jsonlib mixer is used to execute functions that are able to convert HTML into JSON and modify JSON responses.

jsonlib requires Moovweb SDK 4.4+. You also need to specify a version in your Mixer.lock file like so: jsonlib (2.0.66).

For a full specification pertaining to each function in the JSON library please visit our Tritium API.


HTML to JSON

Here’s a simple example of how you can convert the HTML on a page into a JSON array.

First, the starting HTML.

<html>
  <head><title>Blog Post Title</title></head>
    <body>
      <h1 id="title">Blog Post Title</h1>
      <div id="content">
        I went to the woods.
      </div>
      <div id="comments">
        <div class="comment">
          <div class="c-user">Yates</div>
          <div class="c-date">4-14-2013</div>
          <div class="c-content">That was so two centuries ago.</div>
        </div>
        <div class="comment">
          <div class="c-user">Keats</div>
          <div class="c-date">4-16-2013</div>
          <div class="c-content">Good luck with tuberculosis!</div>
        </div>
      </div>
  </body>
</html>

Now we’re going to introduce the Tritium necessary to convert the HTML into JSON. Note that every function begins with “jsonlib.”. This is because the functions are in the jsonlib namespace.

We start by calling the hash() function that starts the scope. Note that you can only use one hash() per page.

After opening the hash scope, you can use the key() function to assign keys and their values. You can either create these yourself, or use the yank() function to pull in values from the HTML.

html("UTF-8") {
  $("/html/body") {
    jsonlib.hash() {
      jsonlib.key("type", "blog post")
      jsonlib.key("title", yank(".//h1[@id='title']"))
      jsonlib.key("content", yank(".//div[@id='content']"))
    }
  }
}

Please note that the yank() function is a standard Tritium function defined in the stdlib mixer.

You can use the array() function to insert an array into the hash. Within the array scope, you must use append() to add anything; if you need to add an extra hash, you must then use the hash() function before you can add a key again.

html("UTF-8") {
  $("/html/body") {
    jsonlib.hash() {
      jsonlib.key("type", "blog post")
      jsonlib.key("title", yank(".//h1[@id='title']"))
      jsonlib.key("content", yank(".//div[@id='content']"))
      jsonlib.key("comments") {
        jsonlib.array() {
          jsonlib.append("random comment!") {
            $(".//div[@class='comment']") {
              jsonlib.append() {
                jsonlib.hash() {
                  jsonlib.key("user", yank(".//div[@class='c-user']"))
                  jsonlib.key("date", yank(".//div[@class='c-date']"))
                  jsonlib.key("content", yank(".//div[@class='c-content']"))
                }
              }
            }
          }
        }
      }
    }
  }
}
jsonlib.set_json()

The final function you need to use is set_json(). The set_json() function must be called outside of the html() function. The other functions can be nested in other files if necessary, but set_json() must be called at the end (so this would usually be in the main.ts file).

The JSON output from the Tritium above would be the following:

{
  "type": "blog post",
  "title": "Blog Post Title",
  "content": "I went to the woods.",
  "comments": [
    "random comment!",
    {
      "user": "Yates",
      "date": "4-14-2013",
      "content": "That was so two centuries ago."
    },
    {
      "user": "Keats",
      "date": "4-16-2013",
      "content": "Good luck with tuberculosis!"
    }
  ]
}

Tritium Guide for JSON Structures

This section covers all the different structures and code logic you can use to construct JSON with Tritium. For these examples, you need to execute the code in the html() scope and be sure to call jsonlib.set_json() after.

Note that in jsonlib.array(), the values are sorted by the order they were appended, whereas in jsonlib.hash() there is no concept of order.

Array of Values, Arrays, Objects

Tritium Code

# Create an array() on the page
jsonlib.array() {
  # Example 1: Add a value with append()
  jsonlib.append("1")
  jsonlib.append("2")
  # Example 2: Add an array with append()
  jsonlib.append() {
    jsonlib.array() {
      jsonlib.append("3")
      jsonlib.append("4")
    }
  }
  # Example 3: Add an object with append()
  jsonlib.append() {
    jsonlib.hash() {
      jsonlib.key("key", "value")
      jsonlib.key("another", "pair")
    }
  }
}

JSON Output

  [
    "1",
    "2",
    [
      "3",
      "4"
    ],
    {
      "another": "pair",
      "key": "value"
    }
  ]

Object of Name/Value Pairs, Name/Array Pairs, Name/Object Pairs

Tritium Code

# Create a hash() on the page
jsonlib.hash() {
  # Example 1: Add a name/value pair with key()
  jsonlib.key("key", "value")
  # Example 2: Add a name/array pair with key()
  jsonlib.key("this is an array") {
    jsonlib.array() {
      jsonlib.append("apple")
      jsonlib.append("banana")
    }
  }
  # Example 3: Add a name/object pair with key()
  jsonlib.key("this is an object") {
    jsonlib.hash() {
      jsonlib.key("inner","pair")
    }
  }
}

JSON Output

{
  "key": "value",
  "this is an array": [
    "apple",
    "banana"
  ],
  "this is an object": {
    "inner": "pair"
  }
}

Parsing and Manipulating JSON

Using the jsonlib mixer, it is possible to properly parse and manipulate JSON content that is stored on a web page, read from a local file, or received in an AJAX response.

Assuming you have a text scope that contains the raw source of the JSON, you can parse it with the json() function:

# text scope containing raw JSON
jsonlib.json() {
  # stuff
}

Internally, the JSON is converted into XML nodes, so the usual Tritium and XPath functionality can be used to manipulate the JSON. The JSON is converted based on the types of each value, according to the following schema:

JSON XML
null
<null />
true
<true />
false
<false />
number
<number>[digits]</number>
string
<string>[characters]</string>
array
<array>[values]</array>
object
<object>
  <member name="[name1]">[value1]</member>
  <member name="[name2]">[value2]</member>
  ...
</object>

The preceding conversion schema is based on the JSON spec that may be found at this site: http://www.json.org

For example, suppose we have the following JSON:

{
  "foo": 42,
  "bar": true,
  "hux": ["a", false, null]
}

This will be converted in to the following XML structure:

<object>
  <member name="foo">
    <number>42</number>
  </member>
  <member name="bar">
    <true />
  </member>
  <member name="hux">
    <array>
      <string>a</string>
      <false />
      <null />
    </array>
  </member>
</object>

Within the JSON scope, you can use all the standard Tritium functions for manipulating XML nodes:

json() {
  # change the value of the `bar` field to `false`
  $(".//member[@name='bar']/*") {
    name("false")
  }
  # change the 3rd element of the array to "hello"
  $(".//array/*[3]") {
    name("string")
    text("hello")
  }
  # append the number 9001 to the array
  $(".//array") {
    insert_bottom("number", "9001")
  }
}

Keep in mind that JSON is internally converted into an HTML/XML fragment, so only relative selectors will work.

During development, it may be helpful to examine the structure of the parsed JSON; this can be done by fetching and logging:

json() {
  log(fetch("*"))
}

As a final example, suppose the JSON contains embedded uuencoded HTML, such as the following:

{
  "html": "\u003cspan class=\"blue\"\u003e\u003cBlue Spanspan\u003e"
}

The JSON can be cleanly parsed, and the embedded HTML can be fetched out and properly parsed in turn:

json() {
  $(".//member[@name='html']/string") {
    text() {
      html_fragment() {
        $(".//span") {
          attributes(class: "red")
          text("Red Span")
          insert_after("span", class: "green", "Green Span")
        }
      }
    }
  }
}

This allows for robust handling of the common use-case where page content is delivered in JSON that contains HTML fragments (typically transmitted via AJAX).