Getting Started with JSON

Requirements: IPWorks

Introduction

The JSON component offers a fast and simple way to parse and write information in JSON documents.

Contents

Overview

The JSON can both parse and write JSON documents. As a document is parsed, events will fire allowing more granular control. Alternatively, the structure of the document can also be accessed with the XPath property.

The parser is optimized for read applications, with a very fast engine that builds internal DOM structures with close to zero heap allocations. Additionally, BuildDOM can be set to False which reduces the overhead of creating the DOM and offers a fast forward-only parsing implementation which fires events to provide the parsed data.

The JSON component may also be used to create JSON documents. Documents are written from beginning to end in a forward-only manner. The finished document can be accessed with on of the output properties described in the writing section, or as methods are called with the JSON event.

Parsing

Input Properties

To start parsing first set a source for input. The component will determine the source of the input based on which properties are set. The order in which the input properties are checked is as follows:

  • SetInputStream (.NET and Java Only)
  • InputFile
  • InputData

When a valid source for input is found the search stops. If parsing multiple documents call Reset between documents to reset the parser. After calling Parse the following events will fire:

  • StartElement
  • Characters
  • EndElement
  • IgnorableWhitespace

After Parse returns the document may be navigated by setting XPath if BuildDOM is set to True (default). If BuildDOM is False parsed data is only accessible through the events.

Parsing with the XPath Property

XPath may be set to navigate to specific elements within the JSON document. This will be the path to a specified value within the document. As arrays in JSON contain only values and no associated object name, an empty name will be used for these values. To reach an array element at position 1, the path must be set to "[1]". In addition, a root element named "json" will be added to each JSON document in the parser.

The XPath property can take input in either XPath or JSONPath notation. Normally the component will detect which type of notation is being used, but it can also be explicitly set with the XPathNotation configuration setting. The following are possible values for an element accessor, regardless of the style of notation:

AccessorDescription
'name'A particular element name.
[i]The i-th subelement of the current element.
..The parent of the current element.

When XPath is set to a valid path the following properties are updated:

  • XElement
  • XElementType
  • XParent
  • XText
  • XSubTree
  • XChildren

Using XPath Notation

XPath notation uses a series of one or more element accessors separated by '/', like the example below:

/json/store/book[1]/title

Please be aware that XPath notation is 1-indexed.

Example of Parsing a Simple JSON Document
The next section shows a simple JSON document and how to access various parts of it using both XPath and JSONPath notation from the XPath property.

{
  "store": {
    "books": [
      {
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      {
        "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}
Accessing the Root Element
With XPath-style notation:
Json.XPath = "/";
Console.WriteLine(Json.XElement);
This would output:
json
Accessing the Author of the First Book
With XPath-style notation:
json.XPath = "/json/store/books/[1]/[2]";
Debug.WriteLine(json.XElement + ": " + json.XText);
This would output:
author: "Nigel Rees"
Display All Information About All Books
With XPath-style notation:
json.XPath = "/json/store/books";
int bookCount = json.XChildren.Count;
int propCount = 0;
for (int x = 1; x < bookCount+1; x++)
{
  Debug.WriteLine("\r\nBook #" + x);
  json.XPath = "/json/store/books/[" + x + "]";
  propCount = json.XChildren.Count;
  for (int y = 1; y < propCount+1; y++)
  {
    json.XPath = "/json/store/books/[" + x + "]/[" + y + "]";
    Debug.WriteLine(json.XElement + ": " + json.XText);
  }
}
This would output:
Book #1
category: "reference"
author: "Nigel Rees"
title: "Sayings of the Century"
price: 8.95

Book #2
category: "fiction"
author: "Evelyn Waugh"
title: "Sword of Honour"
price: 12.99

Book #3
category: "fiction"
author: "Herman Melville"
title: "Moby Dick"
isbn: "0-553-21311-3"
price: 8.99

Book #4
category: "fiction"
author: "J. R. R. Tolkien"
title: "The Lord of the Rings"
isbn: "0-395-19395-8"
price: 22.99

Using JSONPath Notation

JSONPath notation uses a series of one or more accessors in either dot-notation or bracket-notation, like the examples below:

//dot-notation
$.store.book[0].title
//bracket-notation
$['store']['book'][0]['title']

Please be aware that JSONPath notation is 0-indexed.

Example of Parsing a Simple JSON Document
The next section shows a simple JSON document and how to access various parts of it using both XPath and JSONPath notation from the XPath property.

{
  "store": {
    "books": [
      {
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      {
        "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}
Accessing the Root Element
With JSONPath-style notation:
Json.XPath = "$";
Console.WriteLine(Json.XElement);
This would output:
json
Accessing the Author of the First Book
With JSONPath-style notation:
json.XPath = "$.store.books.[0].[1]";
Debug.WriteLine(json.XElement + ": " + json.XText);
This would output:
author: "Nigel Rees"
Display All Information About All Books
With JSONPath-style notation:
json.XPath = "$.store.books";
int bookCount = json.XChildren.Count;
int propCount = 0;
for (int x = 0; x < bookCount; x++)
{
  Debug.WriteLine("\r\nBook #" + x);
  json.XPath = "$.store.books.[" + x + "]";
  propCount = json.XChildren.Count;
  for (int y = 0; y < propCount; y++)
  {
    json.XPath = "$.store.books.[" + x + "].[" + y + "]";
    Debug.WriteLine(json.XElement + ": " + json.XText);
  }
}
This would output:
Book #1
category: "reference"
author: "Nigel Rees"
title: "Sayings of the Century"
price: 8.95

Book #2
category: "fiction"
author: "Evelyn Waugh"
title: "Sword of Honour"
price: 12.99

Book #3
category: "fiction"
author: "Herman Melville"
title: "Moby Dick"
isbn: "0-553-21311-3"
price: 8.99

Book #4
category: "fiction"
author: "J. R. R. Tolkien"
title: "The Lord of the Rings"
isbn: "0-395-19395-8"
price: 22.99

Forward-Only Parsing

The component can optionally be used in a forward-only mode which further reduces the overhead and increases performance if building a DOM is not necessary. To enable this mode set BuildDOM to False before calling Parse. When Parse is called data will be available through the events. For instance, using the same example above the code:

Json json = new Json();
json.BuildDOM = false; //Forward-Only
json.OnStartElement += Json_OnStartElement;
json.OnEndElement += Json_OnEndElement;
json.OnCharacters += Json_OnCharacters;
json.InputData = input;
json.Parse();

//Events defined in another section of the application:
private static void Json_OnCharacters(object sender, JsonCharactersEventArgs e)
{
  Console.WriteLine("Characters: " + e.Text);
}

private static void Json_OnEndElement(object sender, JsonEndElementEventArgs e)
{
  Console.WriteLine("End Element: " + e.Element);
}

private static void Json_OnStartElement(object sender, JsonStartElementEventArgs e)
{
  Console.WriteLine("Start Element: " + e.Element);
}

Will output data through events, resulting in output like below. Note that this is truncated here for readability.

Start Element: json
    Start Element: store
    Start Element: book
    Start Element: 
    Start Element: category
    Characters: "reference"
    End Element: category
    Start Element: author
    Characters: "Nigel Rees"
    End Element: author
    Start Element: title
    Characters: "Sayings of the Century"
    End Element: title
    ...

Writing

The JSON component may also be used to create JSON documents. Documents are written from beginning to end in a forward-only manner. In addition as the document is written the JSON event will fire. The Text event parameter contains the part of the document currently being written.

Output Properties

The component will determine the destination of the output based on which properties are set. The order in which the input properties are checked is as follows:

  • SetOutputStream (.NET and Java Only)
  • OutputFile
  • OutputData

For more granular control the JSON event can accessed during writing. The event fires once for each element added.

Example of Writing a JSON Document
This example will write a small document describing a pet with several previous owners.

Json json = new Json();
json.OnJSON += (obj, ev) => { Debug.Write(ev.Text); };
json.OutputFile = "C:\\temp\\fido.json";
json.StartObject();
json.PutProperty("name", "fido", 2);
json.PutName("previousOwners");
json.StartArray();
json.PutValue("Steve Widgetson", 2);
json.PutValue("Wanda Widgetson", 2);
json.PutValue("Randy Cooper", 2);
json.PutValue("Linda Glover", 2);
json.EndArray();
json.PutProperty("weightUnit", "lbs", 2);
json.PutProperty("weight", "62", 3);
json.EndObject();
json.Flush();

The above example creates the following JSON:

{
  "name": "fido",
  "previousOwners": [
    "Steve Widgetson",
    "Wanda Widgetson",
    "Randy Cooper",
    "Linda Glover"
  ],
  "weightUnit": "lbs",
  "weight": 62
}

Modifying JSON

After loading a JSON document with Parse the document may be editted. The component supports inserting new values, renaming or overwriting existing values, and removing values. After editting is complete call Save to output the updated JSON document.

The following methods are applicable when modifying a JSON document:

  • InsertProperty
  • InsertValue
  • Remove
  • Save
  • SetName
  • SetValue

When Save is called the modified JSON is written to the specified output location.

Output Properties

The component will determine the destination of the output based on which properties are set.

The order in which the output properties are checked is as follows:

  • SetOutputStream
  • OutputFile
  • OutputData: The output data is written to this property if no other destination is specified.

Inserting New Values

To insert new values in a JSON document first load the existing document with Parse. Next set XPath to the sibling or parent of the data to be inserted. Call InsertProperty or InsertValue and pass the ValueType and Position parameters to indicate the type of data being inserted and the position.

The ValueType parameter of the above methods specifies the type of the value. Possible values are:

  • 0 (Object)
  • 1 (Array)
  • 2 (String)
  • 3 (Number)
  • 4 (Bool)
  • 5 (Null)
  • 6 (Raw)

The Position parameter of the above methods specifies the position of Value. Possible values are:

  • 0 (Before the current element)
  • 1 (After the current element)
  • 2 (The first child of the current element)
  • 3 (The last child of the current element)

For example:

Given the following JSON:

{
    "store": {
        "books": [
            {
                "category": "reference",
                "author": "Nigel Rees",
                "title": "Sayings of the Century",
            },
            {
                "category": "fiction",
                "author": "Evelyn Waugh",
                "title": "Sword of Honour",
            }
        ]    
    }
}

Insert a new property "price" for each book:

json.XPath = "/json/store/books/[1]";
json.InsertProperty("price", "8.95", 3, 3);  //3 - Number, 3 - Last Child

json.XPath = "/json/store/books/[2]";
json.InsertProperty("price", "12.99", 3, 3); //3 - Number, 3 - Last Child

json.Save();

Produces the JSON:

{
  "store": {
    "books": [
    {
      "category": "reference",
      "author": "Nigel Rees",
      "title": "Sayings of the Century",
      "price": 8.95
    },
    {
      "category": "fiction",
      "author": "Evelyn Waugh",
      "title": "Sword of Honour",
      "price": 12.99
    }
    ]
  }
}

To add a new book to the array:

json.XPath = "/json/store/books";
json.InsertValue("", 0, 3); //0 - Object, 3 - Last Child
json.XPath = "/json/store/books/[3]";
json.InsertProperty("category", "fiction", 2, 3);        //2 - String, 3 - Last Child
json.InsertProperty("author", "Herman Melville", 2, 3);  //2 - String, 3 - Last Child
json.InsertProperty("title", "Moby Dick", 2, 3);         //2 - String, 3 - Last Child
json.InsertProperty("price", "8.99", 3, 3);              //3 - Number, 3 - Last Child

json.Save();

Produces the JSON:

{
  "store": {
    "books": [
    {
      "category": "reference",
      "author": "Nigel Rees",
      "title": "Sayings of the Century",
      "price": 8.95
    },
    {
      "category": "fiction",
      "author": "Evelyn Waugh",
      "title": "Sword of Honour",
      "price": 12.99
    },
    {
      "category": "fiction",
      "author": "Herman Melville",
      "title": "Moby Dick",
      "price": 8.99
    }
    ]
  }
}

To add a new array property to each book:

json.XPath = "/json/store/books/[1]";
json.InsertProperty("tags", "", 1, 2); //1 - Array, 2 - First Child
json.XPath = "/json/store/books/[1]/tags"; 
json.InsertValue("quotes", 2, 3);      //2 - String, 3 - Last Child
json.InsertValue("british", 2, 3);     //2 - String, 3 - Last Child

json.XPath = "/json/store/books/[2]";
json.InsertProperty("tags", "", 1, 2); //1 - Array, 2 - First Child
json.XPath = "/json/store/books/[2]/tags";
json.InsertValue("trilogy", 2, 3);     //2 - String, 3 - Last Child
json.InsertValue("war", 2, 3);         //2 - String, 3 - Last Child

json.XPath = "/json/store/books/[3]";
json.InsertProperty("tags", "", 1, 2); //1 - Array, 2 - First Child
json.XPath = "/json/store/books/[3]/tags";
json.InsertValue("classic", 2, 3);     //2 - String, 3 - Last Child
json.InsertValue("whales", 2, 3);      //2 - String, 3 - Last Child

json.Save();

Producse the JSON:

{
  "store": {
    "books": [
    {
      "tags": ["quotes", "british"],
      "category": "reference",
      "author": "Nigel Rees",
      "title": "Sayings of the Century",
      "price": 8.95
    },
    {
      "tags": ["trilogy", "war"],
      "category": "fiction",
      "author": "Evelyn Waugh",
      "title": "Sword of Honour",
      "price": 12.99
    },
    {
      "tags": ["classic", "whales"],
      "category": "fiction",
      "author": "Herman Melville",
      "title": "Moby Dick",
      "price": 8.99
    }
    ]
  }
}

Removing Values

To remove existing values set XPath and call the Remove method. Continuing with the example above, to remove the first book:

json.XPath = "/json/store/books/[1]";
json.Remove();

json.Save();

Produces the JSON:

{
  "store": {
    "books": [
    {
      "tags": ["trilogy", "war"],
      "category": "fiction",
      "author": "Evelyn Waugh",
      "title": "Sword of Honour",
      "price": 12.99
    },
    {
      "tags": ["classic", "whales"],
      "category": "fiction",
      "author": "Herman Melville",
      "title": "Moby Dick",
      "price": 8.99
    }
    ]
  }
}

To remove the "category" properties from each book:

json.XPath = "/json/store/books/[1]/category";
json.Remove();

json.XPath = "/json/store/books/[2]/category";
json.Remove();

json.Save();
Produces the JSON:

{
  "store": {
    "books": [
    {
      "tags": ["trilogy", "war"],
      "author": "Evelyn Waugh",
      "title": "Sword of Honour",
      "price": 12.99
    },
    {
      "tags": ["classic", "whales"],
      "author": "Herman Melville",
      "title": "Moby Dick",
      "price": 8.99
    }
    ]
  }
}

Updating Existing Names and Values

The SetName and SetValue methods may be used to modify existing names and values. Continuing with the JSON directly above, to rename "tags" to "meta" and update values within the array and prices:

//Rename "tags" to "meta" for 1st book
json.XPath = "/json/store/books/[1]/tags";
json.SetName("meta");

//Update Price
json.XPath = "/json/store/books/[1]/price";
json.SetValue("13.99", 3); //3 - Number

//Rename "tags" to "meta" for 2nd book
json.XPath = "/json/store/books/[2]/tags";
json.SetName("meta");

//Update tag "whales" to "revenge"
json.XPath = "/json/store/books/[2]/meta/[2]";
json.SetValue("revenge", 2); //2 - String

//Update Price
json.XPath = "/json/store/books/[2]/price";
json.SetValue("9.99", 3); //3 - Number

json.Save();

Produces the JSON:

{
  "store": {
    "books": [
    {
      "meta": ["trilogy", "war"],
      "author": "Evelyn Waugh",
      "title": "Sword of Honour",
      "price": 13.99
    },
    {
      "meta": ["classic", "revenge"],
      "author": "Herman Melville",
      "title": "Moby Dick",
      "price": 9.99
    }
    ]
  }
}

We appreciate your feedback.  If you have any questions, comments, or suggestions about this article please contact our support team at kb@nsoftware.com.