JsonDOM

JsonDOM is a simple-to-use and fast-acting .Net Standard 2.0 assembly for processing JSON content. Its performance is a side-effect of its simplicity.

.Net Standard 2.0 was chosen for maximum compatibility - this can be consumed by .Net Framework (4.7.2 onwards), .Net Core, and .Net 5+ applications. It is available as a NuGet package from nuget.org.

Re the nomenclature, although both JSON (JavaScript Object Notation) and DOM (Document Object Model) are usually capitalised, I have chosen to use "Json" throughout in order to assist the camel-casing of various identifiers. Please bear with me on this - I hope you'll find in the end that on the whole it works. And in case you're wondering, I have left "DOM" capitalised because it isn't actually used as part of any identifier other than the assembly's filename; since it's a common term, I preferred to leave it as is - "JsonDOM" to my mind makes it clear that this is a DOM for JSON.

  Notes to Callers

The namespace within which all types are defined is "MichaelBrooks.UK.Json".

  Note

Within the reference pages you will see a link within each page's Remarks section to "Online page". This is actually a convenience for developers working in Visual Studio, as it allows them to jump straight to the online documentation for a particular type or member (F1 only works with Microsoft documentation).

Please report any issues with the assembly or the documentation via the issues list (https://github.com/m-brooks/JsonDOM-docs/issues) on the GitHub repository for this documentation.

Overview

The typical use case for which this was developed (note that I am not suggesting that this is the only possible use case) is where your code needs to process JSON data that it has received, and optionally to modify the data (or create anew) and send it on its way to the next consumer. This differs from the use case that other JSON-related assemblies are typically designed to address, which is to serialise .Net objects to JSON and deserialise back again to known .Net objects. JsonDOM allows you to discover the structure of the JSON data that you are processing and to navigate it without needing to know in advance what .Net types it should be deserialised to.

JsonDOM works with all legal JSON. As an example, the following data (shown here as XML):

XML
<Samples>
  <Sample>45.3</Sample>
  <Sample>73.5</Sample>
</Samples>

might typically be represented in JSON as:

JSON
{
    "Samples" : [ 45.3, 73.5 ]
}

but could alternatively be represented by the following equally valid JSON:

JSON
{
    "Samples" : {
        "Sample" : 45.3,
        "Sample" : 73.5
    }
}

Unlike some other JSON-related libraries, JsonDOM will handle both of these options with equal ease.

Data types

The assembly exposes just two main classes (JsonNode and JsonParser) supported by three new exception classes (JsonNodeDuplicateSiblingNameException, JsonNodeMemberNotFoundException, and JsonNodeListDuplicateItemException) and two enumerations (JsonNodeUnexpectedDuplicateSiblingReaction and JsonValueType). These types all reside within the MichaelBrooks.UK.Json namespace.

  • JsonNode objects are arranged as a hierarchical document object model (DOM) that represents a JSON document. They can be so arranged explicitly or as a result of parsing a JSON document via JsonParser. A DOM can be serialised to a JSON string with various formatting options.

  • JsonParser is a static class whose sole purpose is to take a JSON string and parse it into a DOM comprising a hierarchy of JsonNode objects.

Consuming JSON

This assembly currently only consumes JSON from a string. You do so via the JsonParser static class, by calling one of its ParseJson methods, passing in the string that contains the JSON to be consumed. This method also accepts an optional parameter which determines a nuance of navigation - explained further below.

The ParseJson method either returns a DOM (Document Object Model) if the JSON is valid, or throws an exception if it isn't. The DOM is a hierarchy of JsonNode objects. Each JsonNode has five properties:

  • JsonName - null for the root of the DOM.

  • JsonValueType - which of the seven defined JSON data types the node represents (object, array, string, number, true, false, null).

  • JsonValue - not always populated; see reference documentation for full details.

  • Parent - null when this is the root node, otherwise references the parent node (which will be either of type object or array).

  • Children - a list of members (if this node represents an object) or elements (if this node represents an array), or null.

Navigation

There are two mechanisms for navigating a DOM, which are covered below. If you don't know the structure of the JSON that you are processing, your only choice is the first of these.

  1. The first is to use the Parent and Children properties of the nodes to navigate up and down the hierarchy, examining the JsonName and JsonValueType for each node that you discover, before deciding where to go next. Note that the root node of the DOM has a null Parent property, and that only nodes representing objects or arrays (which you can identify by checking the JsonValueType property) will have a non-null Children property. If the object or array is empty, the Children property will be an empty list rather than null; otherwise it will contain one member for each member of the object or element of the array.

  2. If you know the structure of the JSON, you can quickly navigate using a bracket-based mechanism whereby within each pair of brackets you specify either the zero-based index of an array element or the name of an object member. The details of each of these are as follows:

    So for example, consider that you have a DOM that represents the JSON shown below:

    JSON
    {
        "Sample type" : "Depths",
        "Sample values" : [ 45.3, 73.5 ]
    }

    You could navigate to the second value by using the following code:

    C#
    JsonNode secondValue = dom["Sample values"][1];

      Note

    This is me providing a degree of support for Stefan Goessner's JSONPath syntax, which is a widely used syntax for navigating JSON. However, I have only implemented the subset of JSONPath that I could do without too much effort (namely the bracket notation), so this is not a full implementation of JSONPath but it does allow you to navigate a DOM using a familiar syntax if you already know the structure of the JSON that you are processing.

    For those who are familiar with XPath but unfamiliar with JSONPath, be aware that whereas XPath correctly recognises and accounts for the fact that an XML document can have multiple sibling elements with the same name, JSONPath fails to take account of the fact that a JSON object can also legally have multiple sibling members with the same name. When the IETF formed a working group to standardise JSONPath, they were aware of this shortcoming, but rather than attempt to address it they instead defined it as out of scope - see section 1.3 of RFC9535. The above-mentioned optional parameters that allow you to specify how you want JsonDOM to react when it encounters multiple sibling members with the same name are my approach to addressing this.

Creating, modifying, and outputting JSON

Once you have a DOM, you can modify it by adding, removing, or replacing nodes. You can also modify the JsonName and/or JsonValue of any node, but there are constraints upon changing the JsonValueType property (see the reference documentation for details).

Also, you can create an entire DOM from scratch by instantiating JsonNode objects and arranging them in a hierarchy.

  Caution

When explicitly instantiating nodes within a DOM, you have the option of specifying how you want each node to react if it later encounters multiple sibling members with the same name when being navigated via the name-based index property (see above). Although it's legal for you to configure different nodes to react differently, in most scenarios this is likely to be a source of confusion and bugs, so I recommend that you choose one reaction and apply it consistently across all nodes in your DOM.

Once you have created the DOM, you can generate a JSON document as a string (using various formatting options) from any node, whereby that node is treated as the root of the JSON document to be generated. This is achieved via the Serialise methods.

See Also

Other Resources