Invex.Extensions.Json 0.3.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Invex.Extensions.Json --version 0.3.0
                    
NuGet\Install-Package Invex.Extensions.Json -Version 0.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Invex.Extensions.Json" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Invex.Extensions.Json" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Invex.Extensions.Json" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Invex.Extensions.Json --version 0.3.0
                    
#r "nuget: Invex.Extensions.Json, 0.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Invex.Extensions.Json@0.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Invex.Extensions.Json&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Invex.Extensions.Json&version=0.3.0
                    
Install as a Cake Tool

Invex.Extensions.Json

Invex.Extensions.Json is a small, allocation-conscious library for flattening, unflattening, and updating System.Text.Json node trees (JsonNode, JsonObject, and JsonArray) with readable paths such as user:address:city.

It is useful for configuration and environment-variable adapters, ETL pipelines, JSON diffing or patching, and exchanging JSON with flat key/value stores.

License: MIT

Features

  • Flatten any JsonNode into path/value pairs.
  • Rebuild a JsonObject from flattened pairs.
  • Replace one or many values in an existing JsonObject, including array elements.
  • Preserve JSON value types when flattening to a JsonObject.
  • Use configurable separators, including multi-character separators.
  • Avoid reflection and regular expressions on the hot paths.
  • Target net10.0, net9.0, net8.0, and netstandard2.0.

Installation

dotnet add package Invex.Extensions.Json

The package exposes the Invex.Extensions.Json namespace. The netstandard2.0 target includes a System.Text.Json package dependency.

Quick start

using System.Text.Json.Nodes;
using Invex.Extensions.Json;

var json = JsonNode.Parse("""
{
  "user": {
    "name": "John",
    "tags": ["admin", "user"]
  }
}
""")!;

// String-valued flattening uses bracketed array indices.
IDictionary<string, string?> flat = JsonExtensions.Flatten(json);
// user:name       = "John"
// user:tags:[0]   = "admin"
// user:tags:[1]   = "user"

// The matching operation rebuilds the hierarchy.
JsonObject rebuilt = JsonExtensions.Unflatten(flat);

// Replacements mutate the existing object and use bare numeric indices.
rebuilt.ReplaceValues(new Dictionary<string, string?>
{
    ["user:name"] = "Jane",
    ["user:tags:1"] = "editor",
});

Choose an API surface

The library has two complementary path and value models. Pair operations from the same column when round-tripping.

JsonExtensions JsonObject extension members
Flattening input Any JsonNode JsonObject
Array path syntax Bracketed: users:[0]:name Bare numeric: users:0:name
Flattened values string? Original JSON nodes, or string? for the dictionary method
Unflattened arrays Append order; no sparse padding Numeric indices; sparse indices padded with null
Best suited to Configuration and string key/value stores Lossless JSON round-tripping

String-valued operations

JsonExtensions.Flatten returns an IDictionary<string, string?>, where JSON null is represented by a null value. JsonExtensions.Unflatten creates a new JsonObject, but every non-null leaf becomes a JSON string; numbers and booleans are not restored to their original JSON kinds.

Array indices in Unflatten are applied in the order encountered in the input. The method appends values rather than padding arrays to the numeric index. Empty objects and arrays produce no flattened entries and therefore cannot be represented by a flattened result.

Type-preserving JsonObject operations

var source = JsonNode.Parse(
    """{ "a": { "number": 42, "enabled": true, "items": [null, "x"] } }""")!
    .AsObject();

JsonObject flatObject = source.ToFlattenedJsonObject();
// a:number = 42, a:enabled = true, a:items:0 = null, a:items:1 = "x"

JsonObject roundTrip = flatObject.ToUnflattenedJsonObject();
Dictionary<string, string?> flatDictionary = source.ToFlattenedDictionary();
bool hasNestedValues = source.HasNestedObjects();

ToFlattenedJsonObject deep-clones leaf nodes, and ToUnflattenedJsonObject deep-clones them again, so these operations do not share mutable node references with their source. ToFlattenedDictionary stringifies values and cannot preserve their JSON kinds. HasNestedObjects checks only direct property values; it returns true for a direct JsonObject or JsonArray.

Replacing values

ReplaceValue and ReplaceValues modify the supplied JsonObject in place and return the same instance. Replacement values are always stored as JSON strings, or as JSON null when the supplied value is null.

ReplaceValue supports nested object paths but does not interpret array segments. A simple root-level path is replaced only when that property already exists. For a separated path, existing object segments are traversed; if a segment is missing, traversal stops at the deepest object reached and the final path segment is assigned there. If no object segment exists, that final segment is assigned on the root object.

ReplaceValues additionally supports existing arrays using bare, zero-based numeric segments such as users:0:name. It never creates intermediate objects or arrays. A missing or out-of-bounds path is left unchanged unless the document contains the applicable documented literal-path fallback. Empty keys are ignored, and [0] is a literal property name rather than an array index.

See Replacing values for the complete traversal and fallback rules.

Separators and path notation

All path-based methods accept a separator, defaulting to ":". Separators can contain multiple characters:

var flat = JsonExtensions.Flatten(json, separator: "__");
var rebuilt = JsonExtensions.Unflatten(flat, separator: "__");

Use the same separator for both halves of a round trip, and avoid separators that occur in property names. Property names are not escaped; a separator in a property name will be interpreted as a path boundary when unflattening.

The two array conventions are intentionally different:

  • JsonExtensions.Flatten and Unflatten use [0], [1], and so on.
  • JsonObject extension members and ReplaceValues use 0, 1, and so on.

Do not pass bracketed paths from Flatten directly to ReplaceValues, and do not mix JsonExtensions.Unflatten with ToFlattenedJsonObject.

Documentation

Topic Description
Getting started Installation, namespaces, and guided examples
Path notation Array conventions, separators, values, and edge cases
Flattening and unflattening JsonExtensions.Flatten and Unflatten
JsonObject extension members Type-preserving flattening and reconstruction
Replacing values In-place updates, arrays, and fallbacks
API reference Generated DocFX API documentation

License

Licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 50 10/22/2026
1.1.0-rc.6 48 10/22/2026
1.1.0-rc.4 54 10/22/2026
1.1.0-rc.2 49 10/16/2026
1.0.0 91 10/15/2026
0.4.0-rc.30 49 10/15/2026
0.4.0-rc.28 49 10/14/2026
0.4.0-rc.26 52 10/14/2026
0.4.0-rc.24 46 10/14/2026
0.4.0-rc.22 47 10/14/2026
0.4.0-rc.20 56 10/14/2026
0.4.0-rc.16 50 10/13/2026
0.4.0-rc.10 68 9/11/2026
0.4.0-rc.8 76 8/28/2026
0.4.0-rc.6 71 8/21/2026
0.4.0-rc.4 84 8/14/2026
0.4.0-rc.2 76 8/7/2026
0.3.0 122 8/3/2026
0.3.0-rc.5 79 8/3/2026
0.3.0-rc.4 80 7/24/2026
Loading failed