Table of Contents

Class JsonUtil

Namespace
Invex.Extensions.Json
Assembly
Invex.Extensions.Json.dll

Provides extension members on System.Text.Json.Nodes.JsonObject for flattening a hierarchical JSON structure into a single level and reconstructing (unflattening) it again.

Path conventions used by this class:

  • Object property segments are joined with a configurable separator (default ":"), e.g., "user:address:city".
  • Array elements are addressed with bare zero-based numeric segments, e.g., "users:0:name".

Note: this differs from Flatten(JsonNode, string), which uses bracketed indices such as "users:[0]:name".

public static class JsonUtil
Inheritance
object
JsonUtil

Methods

HasNestedObjects(JsonObject)

Determines whether this System.Text.Json.Nodes.JsonObject contains any nested containers, i.e., whether any direct property value is a System.Text.Json.Nodes.JsonObject or System.Text.Json.Nodes.JsonArray.

public static bool HasNestedObjects(this JsonObject jsonObject)

Parameters

jsonObject JsonObject

Returns

bool

true if at least one direct property value is an object or array; otherwise, false.

Remarks

Only the top level is inspected; this is a quick check for whether the object is already "flat".

ToFlattenedDictionary(JsonObject, string)

Flattens this System.Text.Json.Nodes.JsonObject into a System.Collections.Generic.Dictionary<TKey, TValue> mapping each leaf path to its string representation.

public static Dictionary<string, string?> ToFlattenedDictionary(this JsonObject jsonObject, string separator = ":")

Parameters

jsonObject JsonObject
separator string

The string used to join path segments. May be more than one character. Defaults to ":".

Returns

Dictionary<string, string>

A dictionary in which:

  • Each key is the separator-joined path to a leaf value (array elements use bare numeric segments, e.g., "users:0:name").
  • String values are stored verbatim; numbers use their raw JSON text; booleans become "true"/"false"; JSON nulls become null entries.

Keys appear in depth-first, insertion order. The source object is not modified.

Examples

var obj = JsonNode.Parse("""{ "user": { "age": 42, "active": true } }""").AsObject();
var flat = obj.ToFlattenedDictionary();
// { ["user:age"] = "42", ["user:active"] = "true" }

Remarks

Because all values are converted to strings, the original JSON value kinds (number vs. string vs. boolean) are not recoverable from the result. Use ToFlattenedJsonObject(JsonObject, string) when type fidelity matters.

Empty nested objects and arrays produce no entries in the output.

Exceptions

InvalidOperationException

Thrown if a leaf node reports an unexpected System.Text.Json.JsonValueKind (e.g., System.Text.Json.JsonValueKind.Undefined); this is not expected for well-formed JSON.

ToFlattenedJsonObject(JsonObject, string)

Flattens this System.Text.Json.Nodes.JsonObject into a new single-level System.Text.Json.Nodes.JsonObject whose keys are full paths to each leaf value.

public static JsonObject ToFlattenedJsonObject(this JsonObject jsonObject, string separator = ":")

Parameters

jsonObject JsonObject
separator string

The string used to join path segments. May be more than one character. Defaults to ":".

Returns

JsonObject

A new System.Text.Json.Nodes.JsonObject in which:

  • Each key is the separator-joined path to a leaf value (array elements use bare numeric segments, e.g., "users:0:name").
  • Each value is a deep clone of the original leaf node, so JSON types (string, number, boolean) are preserved and no references are shared with the source object.
  • JSON null leaves are preserved as null values.

Keys appear in depth-first, insertion order. The source object is not modified.

Examples

var obj = JsonNode.Parse("""{ "user": { "name": "John", "tags": ["admin", "user"] } }""").AsObject();
var flat = obj.ToFlattenedJsonObject();
// {"user:name":"John","user:tags:0":"admin","user:tags:1":"user"}

Remarks

Empty nested objects and arrays produce no entries in the output, so they are not round-trippable via ToUnflattenedJsonObject(JsonObject, string).

Choose a separator that does not occur in any property name; otherwise unflattening will split those names incorrectly.

ToUnflattenedJsonObject(JsonObject, string)

Reconstructs a hierarchical System.Text.Json.Nodes.JsonObject from a flattened object whose keys are separator-joined paths, reversing ToFlattenedJsonObject(JsonObject, string).

public static JsonObject ToUnflattenedJsonObject(this JsonObject jsonObject, string separator = ":")

Parameters

jsonObject JsonObject
separator string

The string used to split keys into path segments. Must match the separator used when flattening. May be more than one character. Defaults to ":".

Returns

JsonObject

A new System.Text.Json.Nodes.JsonObject with nested objects and arrays rebuilt from the paths:

  • A purely numeric segment causes its parent container to be created as a System.Text.Json.Nodes.JsonArray; any other segment creates a System.Text.Json.Nodes.JsonObject.
  • Leaf values are deep clones of the source values, so no references are shared.
  • Sparse array indices are padded with nulls (e.g., a single key "items:2" yields [null, null, value]).

The source object is not modified.

Examples

var flat = new JsonObject
{
    ["user:name"] = "John",
    ["user:tags:0"] = "admin",
    ["user:tags:1"] = "user",
};
var obj = flat.ToUnflattenedJsonObject();
// {"user":{"name":"John","tags":["admin","user"]}}

Remarks

Property names that are purely numeric are indistinguishable from array indices and will be reconstructed as array elements.

If the same path is supplied more than once, the last value wins.

Exceptions

InvalidOperationException

Thrown when a non-numeric segment is used where an array index is required (i.e., the structure implied by one key conflicts with an array created for another key).