Getting Started with JSON


Requirements: IPWorks

Contents

Introduction

The JSON component offers a fast and simple way to parse, write, and modify JSON documents. This guide shows how to use each of these features, with examples in C#.

When a document is parsed, the component builds an internal object model (DOM) of the document, which can then be navigated with the XPath property using either XPath or JSONPath notation. The parser is optimized for read applications, and builds the DOM with very few heap allocations. If a DOM is not needed, the BuildDOM property can be set to false, in which case the document is parsed in a forward-only manner and the parsed data are provided only through events.

Documents are written from beginning to end in a forward-only manner, by calling methods such as StartObject and PutProperty. A parsed document can also be modified (e.g., by inserting, renaming, or removing values) and then saved.

Parsing

To parse a document, first specify the input. The component checks the input properties in the following order, and uses the first one that is set:

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

Then call the Parse method. While the document is parsed, the StartElement, Characters, EndElement, and IgnorableWhitespace events fire. If BuildDOM is true (default), the document can be navigated by setting XPath after Parse returns. To parse another document with the same instance, call the Reset method first.

The examples in this section use the following document:

{
  "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
    }
  }
}
JSON json = new JSON();
json.InputFile = "C:\\temp\\store.json";
json.Parse();

When XPath is set to a valid path, the following properties are populated with information about the element at that path:

Property Description
XElement The name of the element.
XElementType The type of the element (etObject, etArray, etString, etNumber, etBool, or etNull).
XText The value of the element.
XParent The name of the parent of the element.
XChildren A collection of the children of the element, each with a Name, XText, and ElementType.
XSubTree The JSON of the element and its children.

The XPath property accepts both XPath and JSONPath notation, and detects which one is used automatically. The notation can also be set explicitly with the XPathNotation configuration setting (0 for automatic detection, 1 for XPath, and 2 for JSONPath). If the path does not exist in the document, setting XPath throws an exception. To check whether a path exists first, use the HasXPath method, or use the TryXPath method, which sets XPath only if the path exists and returns whether it does.

Note that XText returns string values as they appear in the document (i.e., with quotes and escape sequences). To remove the quotes, unescape the values, or both, set the StringProcessingOptions configuration setting to 1, 2, or 3, respectively.

In XPath notation, a path is a series of element accessors separated by /. The component adds a root element named json to each document, so absolute paths begin with /json. Paths that do not begin with / are relative to the current element.

Accessor Description
name The child element with the specified name.
[i] The i-th child of the current element (1-based).
[last()] The last child of the current element.
.. The parent of the current element.

Since the values in an array do not have names, they can be accessed only by position (e.g., /json/store/books/[1]). Note that the position must be a separate accessor, i.e., /json/store/books/[1] is valid, while /json/store/books[1] is not.

// Get the author of the first book.
json.XPath = "/json/store/books/[1]/author";
Console.WriteLine(json.XElement + ": " + json.XText); // Prints author: "Nigel Rees".

// Get the price of the bicycle, relative to the store element.
json.XPath = "/json/store";
json.XPath = "bicycle/price";
Console.WriteLine(json.XText); // Prints 19.95.

The following code prints the properties of every book in the document, using XChildren:

json.XPath = "/json/store/books";
int bookCount = json.XChildren.Count;
for (int i = 1; i <= bookCount; i++)
{
  json.XPath = "/json/store/books/[" + i + "]";
  Console.WriteLine("Book #" + i);
  foreach (JSONElement child in json.XChildren)
  {
    Console.WriteLine("  " + child.Name + ": " + child.XText);
  }
}

This prints:

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

The component implements a subset of JSONPath notation. A path begins with $ (the root element) and is followed by a series of accessors in either dot notation or bracket notation:

$.store.books[0].title
$['store']['books'][0]['title']

Note that positions in JSONPath notation are 0-based, unlike in XPath notation. The last() function is also supported (e.g., $.store.books[last() - 1] for the second to last book). After XPath is set, reading the property returns the path in bracket notation, with any last() expressions resolved to positions.

// Get the title of the third book.
json.XPath = "$.store.books[2].title";
Console.WriteLine(json.XText); // Prints "Moby Dick".
Console.WriteLine(json.XPath); // Prints $['store']['books'][2]['title'].

// Get the price of the second book.
json.XPath = "$['store']['books'][1]['price']";
Console.WriteLine(json.XText); // Prints 12.99.

// Print the JSON of the first book.
json.XPath = "$.store.books[0]";
Console.WriteLine(json.XSubTree);

Forward-Only Parsing

If the document does not need to be navigated, set BuildDOM to false before calling Parse. This reduces the memory usage and increases the performance of the parser, and the parsed data are then provided only through the events. Values in arrays have empty element names, and the root element is again named json.

JSON json = new JSON();
json.BuildDOM = false;
json.OnStartElement += (s, e) => { Console.WriteLine("Start Element: " + e.Element); };
json.OnCharacters += (s, e) => { Console.WriteLine("Characters: " + e.Text); };
json.OnEndElement += (s, e) => { Console.WriteLine("End Element: " + e.Element); };
json.InputFile = "C:\\temp\\store.json";
json.Parse();

This prints the following (truncated for readability):

Start Element: json
Start Element: store
Start Element: books
Start Element: 
Start Element: category
Characters: "reference"
End Element: category
Start Element: author
Characters: "Nigel Rees"
End Element: author
...

To parse a stream of multiple documents (e.g., line-delimited JSON), set the InputFormat configuration setting. This setting applies only when BuildDOM is false.

Writing

To write a document, first specify the output. The component checks the output properties in the following order, and uses the first one that is set:

  • SetOutputStream (.NET and Java only)
  • OutputFile
  • OutputData (used if no other output is specified)

The document is then written from beginning to end by calling StartObject, EndObject, StartArray, EndArray, PutName, PutValue, and PutProperty (which writes a name and value at once). The valueType parameter of the PutValue and PutProperty methods specifies the type of the value:

Value Type
0 Object
1 Array
2 String
3 Number
4 Bool
5 Null
6 Raw (the value is written exactly as specified)

After the document is complete, call the Flush method. As the document is written, the JSON event also fires with each part of the document in its Text parameter. By default, the document is written on a single line, and setting the PrettyPrint configuration setting to true writes it on multiple indented lines instead. To write another document with the same instance, call Reset first.

JSON json = new JSON();
json.Config("PrettyPrint=true");
json.StartObject();
json.PutProperty("name", "fido", 2);
json.PutName("previousOwners");
json.StartArray();
json.PutValue("Steve Widgetson", 2);
json.PutValue("Wanda Widgetson", 2);
json.EndArray();
json.PutProperty("weight", "62", 3);
json.PutProperty("vaccinated", "true", 4);
json.PutProperty("microchipId", "", 5);
json.EndObject();
json.Flush();
Console.WriteLine(json.OutputData);

This prints:

{
  "name": "fido",
  "previousOwners": [
    "Steve Widgetson",
    "Wanda Widgetson"
  ],
  "weight": 62,
  "vaccinated": true,
  "microchipId": null
}

Modifying

After a document is parsed with BuildDOM set to true, it can be modified with the following methods, which operate on the element that XPath points to:

  • InsertProperty and InsertValue insert a new property or value.
  • Remove removes the element.
  • SetName and SetValue rename the element or change its value.

When the changes are complete, call the Save method to write the modified document to the output, which is specified in the same way as when writing a document. The PrettyPrint configuration setting also applies to the saved document.

The examples in this section start with the following document, and each example continues from the result of the previous one:

{
  "store": {
    "books": [
      {
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century"
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour"
      }
    ]
  }
}
JSON json = new JSON();
json.InputFile = "C:\\temp\\books.json";
json.Parse();
json.Config("PrettyPrint=true");

Inserting Values

To insert a value, set XPath to the parent or a sibling of the new value, and call InsertProperty (for a value with a name, in an object) or InsertValue (for a value without a name, in an array). The valueType parameter specifies the type of the value, with the same values as when writing, and the position parameter specifies where the value is inserted:

Value Position
0 Before the current element.
1 After the current element.
2 As the first child of the current element.
3 As the last child of the current element.

The following code adds a price property to each book:

// Insert a number as the last child of each book.
json.XPath = "/json/store/books/[1]";
json.InsertProperty("price", "8.95", 3, 3);

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

json.Save();

This produces:

{
  "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, insert an empty object into the array, and then insert its properties:

// Insert an empty object as the last child of the array.
json.XPath = "/json/store/books";
json.InsertValue("", 0, 3);

// Insert the properties of the new book as strings and a number.
json.XPath = "/json/store/books/[3]";
json.InsertProperty("category", "fiction", 2, 3);
json.InsertProperty("author", "Herman Melville", 2, 3);
json.InsertProperty("title", "Moby Dick", 2, 3);
json.InsertProperty("price", "8.99", 3, 3);

json.Save();

Similarly, to add an array property, insert an empty array, and then insert its values:

// Insert an empty array as the first child of the first book.
json.XPath = "/json/store/books/[1]";
json.InsertProperty("tags", "", 1, 2);

// Insert two strings into the array.
json.XPath = "/json/store/books/[1]/tags";
json.InsertValue("quotes", 2, 3);
json.InsertValue("british", 2, 3);

json.Save();

This produces:

{
  "store": {
    "books": [
      {
        "tags": [
          "quotes",
          "british"
        ],
        "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
      }
    ]
  }
}

Removing Values

To remove a value, set XPath to it and call Remove. Note that removing an element changes the positions of the elements after it.

// Remove the first book, so that the remaining books become [1] and [2].
json.XPath = "/json/store/books/[1]";
json.Remove();

// Remove the category property of each remaining book.
json.XPath = "/json/store/books/[1]/category";
json.Remove();

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

json.Save();

This produces:

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

Updating Names and Values

To rename an element, set XPath to it and call SetName. To change its value, call SetValue with the new value and its type.

// Rename the title property of the first book.
json.XPath = "/json/store/books/[1]/title";
json.SetName("name");

// Change the price of the second book to a new number.
json.XPath = "/json/store/books/[2]/price";
json.SetValue("9.99", 3);

// Change the author of the second book to a new string.
json.XPath = "/json/store/books/[2]/author";
json.SetValue("H. Melville", 2);

json.Save();

This produces:

{
  "store": {
    "books": [
      {
        "author": "Evelyn Waugh",
        "name": "Sword of Honour",
        "price": 12.99
      },
      {
        "author": "H. 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 support@nsoftware.com.