Invex.Extensions.Json
1.1.0
dotnet add package Invex.Extensions.Json --version 1.1.0
NuGet\Install-Package Invex.Extensions.Json -Version 1.1.0
<PackageReference Include="Invex.Extensions.Json" Version="1.1.0" />
<PackageVersion Include="Invex.Extensions.Json" Version="1.1.0" />
<PackageReference Include="Invex.Extensions.Json" />
paket add Invex.Extensions.Json --version 1.1.0
#r "nuget: Invex.Extensions.Json, 1.1.0"
#:package Invex.Extensions.Json@1.1.0
#addin nuget:?package=Invex.Extensions.Json&version=1.1.0
#tool nuget:?package=Invex.Extensions.Json&version=1.1.0
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.
Features
- Flatten any node. Turn any
JsonNodeinto path/value pairs. - Rebuild hierarchies. Recreate a
JsonObjectfrom flattened pairs. - Update in place. Replace one or many values in an existing
JsonObject, including array elements. - Type preservation. Keep JSON value types when flattening to a
JsonObject. - Configurable separators. Use any separator, including multi-character ones such as
"__". - Lean hot paths. No reflection or regular expressions when flattening and unflattening.
Installation
dotnet add package Invex.Extensions.Json
The package targets net10.0 and netstandard2.0, and
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",
});
Usage
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 |
See Introduction for when to use each surface.
Flatten for a key/value store
JsonExtensions.Flatten returns string values (JSON null becomes a null value),
and Unflatten rebuilds a JsonObject whose leaves are all JSON strings. Use
the same separator for both halves of a round trip:
var flat = JsonExtensions.Flatten(json, separator: "__");
var rebuilt = JsonExtensions.Unflatten(flat, separator: "__");
Property names are not escaped, so pick a separator that never occurs in them. See Flattening and Unflattening and Path Notation.
Round-trip without losing value types
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();
Leaf nodes are deep-cloned in both directions, so results never share mutable nodes with their source. See JsonObject Extension Members.
Replace values in place
ReplaceValue and ReplaceValues modify the supplied JsonObject and return
the same instance. Replacement values are stored as JSON strings (or JSON null),
and no intermediate objects or arrays are ever created. ReplaceValues can step
into existing arrays with bare numeric segments such as users:0:name; a
bracketed [0] is treated as a literal property name.
var obj = JsonNode.Parse("""
{ "user": { "address": { "city": "NYC" } }, "users": [ { "name": "A" } ] }
""")!.AsObject();
obj.ReplaceValue("user:address:city", "LA")
.ReplaceValues(new Dictionary<string, string?> { ["users:0:name"] = "Alpha" });
See Replacing Values for the complete traversal and fallback rules.
Documentation
Full documentation: https://invex-games.github.io/json-extensions/
| Guide | Contents |
|---|---|
| Introduction | What the library is for, the two API surfaces, and design principles |
| 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 reference for every public type |
Development
The repository builds with the .NET SDK selected by global.json. From the
repository root:
dotnet build Invex.Extensions.Json.slnx
dotnet test Invex.Extensions.Json.slnx
The documentation site is built with DocFX
from the conceptual pages in docs/ and the XML doc comments in the source.
Build or preview it through the Atom build:
dotnet run --project _atom -- BuildDocs
dotnet run --project _atom -- ServeDocs
(atom BuildDocs and atom ServeDocs when the Atom tool is installed.) To run
DocFX directly instead:
dotnet tool install -g docfx
docfx docfx.json --serve
Then browse to http://localhost:8080.
License
Licensed under the MIT License.
| Product | Versions 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 was computed. 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 was computed. 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. |
-
.NETStandard 2.0
- JetBrains.Annotations (>= 2026.2.0)
- System.Text.Json (>= 10.0.13)
-
net10.0
- JetBrains.Annotations (>= 2026.2.0)
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 | 48 | 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 |