JsonConstGenerator 1.0.0
dotnet add package JsonConstGenerator --version 1.0.0
NuGet\Install-Package JsonConstGenerator -Version 1.0.0
<PackageReference Include="JsonConstGenerator" Version="1.0.0" />
<PackageVersion Include="JsonConstGenerator" Version="1.0.0" />
<PackageReference Include="JsonConstGenerator" />
paket add JsonConstGenerator --version 1.0.0
#r "nuget: JsonConstGenerator, 1.0.0"
#:package JsonConstGenerator@1.0.0
#addin nuget:?package=JsonConstGenerator&version=1.0.0
#tool nuget:?package=JsonConstGenerator&version=1.0.0
JsonConstGenerator
JsonConstGenerator is a C# Source Generator that converts JSON files into strongly-typed, compile-time accessible node structures.
It allows developers to define hierarchical identifiers and values in JSON and access them in C# through a generated, strongly typed tree. The generator mirrors the JSON structure and exposes each node through a minimal, readable generated structure.
The generated code is designed to remain easy to inspect, while also providing helper methods for fast node lookup without reflection.
Installation
Install the generator package:
dotnet add package JsonConstGenerator
Design Goals
JsonConstGenerator aims to provide:
- Strongly typed access to JSON-defined identifiers
- Compile-time generation with no reflection
- Minimal and readable generated code
- Efficient runtime lookup of nodes
- A clear tree structure that mirrors the original JSON
Example Use Cases
Common use cases include:
- Permission identifiers
- Feature flags
- Event names
- Configuration keys
- Logging categories
- Localization keys
- API identifiers
Basic Usage
1. Create a JSON file
Example:
{
"Numbers": {
"IntValue": 42,
"LargeIntValue": 5000000000,
"DecimalValue": 3.1415
},
"Strings": {
"Greeting": "Hello world"
},
"Flags": {
"IsEnabled": true
},
//A comment placed above a property is copied in the node tree
"EmptyObject": {},
"NullValue": null,
"EmptyArray": [],
"Permissions": ["Read", "Write", "Delete"]
}
2. Add the JSON file as an AdditionalFiles item
<ItemGroup>
<AdditionalFiles Include="myJsonFile.json" />
</ItemGroup>
3. Declare a partial root class
namespace SourceNamespace;
[JsonConstGenerator("myJsonFile.json")]
public static partial class MyConstants
{
}
During compilation the generator creates the node tree.
Generated Structure
The generator produces a minimal tree structure that mirrors the JSON hierarchy. The structure is intentionally written in a clear object-initializer form so it is easy to inspect.
Example generated code:
namespace SourceNamespace
{
// Generated by myJsonFile.json
public static partial class MyConstants
{
public static readonly Numbers Numbers = new()
{
IntValue = new("Numbers.IntValue", 42),
LargeIntValue = new("Numbers.LargeIntValue", 5000000000L),
DecimalValue = new("Numbers.DecimalValue", 3.1415)
};
public static readonly Strings Strings = new()
{
Greeting = new("Strings.Greeting", "Hello world")
};
public static readonly Flags Flags = new()
{
IsEnabled = new("Flags.IsEnabled", true)
};
///<summary>
///A comment placed above a property is copied in the node tree
///</summary>
public static readonly ValueLessConstNode EmptyObject = new("EmptyObject");
public static readonly ValueLessConstNode NullValue = new("NullValue");
public static readonly ValueLessConstNode EmptyArray = new("EmptyArray");
public static readonly Permissions Permissions = new()
{
Read = new("Permissions.Read"),
Write = new("Permissions.Write"),
Delete = new("Permissions.Delete")
};
public static IEnumerable<IConstNode> GetAllNodes()
{
foreach (var node in _allNodes)
yield return node;
foreach (var child in Strings.GetAllNodes())
yield return child;
foreach (var child in Flags.GetAllNodes())
yield return child;
foreach (var child in Permissions.GetAllNodes())
yield return child;
foreach (var child in Numbers.GetAllNodes())
yield return child;
foreach (var child in Numbers.GetAllNodes())
yield return child;
}
public static IEnumerable<IConstNode> GetAllEndNodes()
{
foreach (var node in _allEndNodes)
yield return node;
foreach (var child in Strings.GetAllEndNodes())
yield return child;
foreach (var child in Flags.GetAllEndNodes())
yield return child;
foreach (var child in Permissions.GetAllEndNodes())
yield return child;
foreach (var child in Numbers.GetAllEndNodes())
yield return child;
foreach (var child in Numbers.GetAllEndNodes())
yield return child;
}
///<summary>
/// A function that finds a single node
///</summary>
public static IConstNode FindNode(string path) => AllNodes().First(x => x.NodePath == path);
public static bool TryFindNode(string path, out IConstNode node)
{
...
}
private static readonly IConstNode[] _allNodes =
{
Numbers,
Strings,
Flags,
EmptyObject,
NullValue,
EmptyArray,
Permissions,
};
private static readonly IConstNode[] _allEndNodes =
{
EmptyObject,
NullValue,
EmptyArray,
};
}
}
The node classes themselves are generated separately and represent the tree structure.
Attribute Usage
To generate constants from JSON, apply the JsonConstGenerator attribute to a partial class.
using JsonConstGenerator;
[JsonConstGenerator("constants.json")]
public static partial class MyConstants
{
}
During compilation, the generator reads the provided JSON file(s) and generates a strongly typed node structure inside the class.
Multiple JSON Files
The attribute supports multiple file paths:
[JsonConstGenerator("base.json", "overrides.json")]
public static partial class MyConstants
{
}
When multiple files are provided:
- Files are processed in the order they are defined
- All files are merged into a single structure
- In case of conflicts, later files overwrite earlier ones
Example
base.json
{
"Feature": {
"Enabled": false
}
}
overrides.json
{
"Feature": {
"Enabled": true
}
}
Result:
MyConstants.Feature.Enabled // true
Wildcard Support
The attribute supports simple wildcard patterns using *:
[JsonConstGenerator("constants/*.json")]
public static partial class MyConstants
{
}
This allows you to include multiple files without listing them individually.
Requirements
All matched files must be included as AdditionalFiles in your project:
<ItemGroup>
<AdditionalFiles Include="constants\*.json" />
</ItemGroup>
Behavior
- Wildcards are matched against files provided via
AdditionalFiles - Matching files are processed in deterministic order
Path Resolution
All file paths are resolved relative to the project root.
Example:
[JsonConstGenerator("constants/settings.json")]
Resolves to:
<ProjectRoot>/constants/settings.json
Paths are not relative to the file where the attribute is used.
Path Separator
Each node has a NodePath representing its position in the hierarchy.
By default, the separator is ".":
MyConstants.Feature.Enabled.NodePath
// "Feature.Enabled"
You can customize the separator:
[JsonConstGenerator("constants.json", Seperator = "/")]
public static partial class MyConstants
{
}
Result:
// "Feature/Enabled"
Requirements
- The target class must be partial
- The class is typically declared as
static - JSON files must be included as
AdditionalFiles
Node Types
The generator produces different node types depending on the JSON value.
| JSON value | Generated node |
|---|---|
| integer number | ValueConstNode<int> |
large integer (outside Int32) |
ValueConstNode<long> |
number containing . |
ValueConstNode<double> |
| string | ValueConstNode<string> |
| arrays | ValueConstNode<object[]> |
| empty object | ValueLessConstNode |
null |
ValueLessConstNode |
| empty array | ValueLessConstNode |
Array Handling
Arrays with a consistent type are generated as arrays of that type. If the array contains multiple types, it is generated as an object[].
Arrays of strings are generated as a class that is both a IParentConstNode and a IValueConstNode<string[]>, enabling both the usability of the array while also making arrays a easy implementation for many ValueLessConstNodes.
Example JSON:
{
"Permissions": ["Read", "Write", "Delete"]
}
Each entry becomes a ValueLessConstNode.
Generated structure:
Permissions = new()
{
Read = new("Permissions.Read"),
Write = new("Permissions.Write"),
Delete = new("Permissions.Delete")
}
GeneratedClass:
public class PermissionsConstNode : IParentConstNode, IValueConstNode<string[]>
{
public ValueLessConstNode Read {get; init;}
public ValueLessConstNode Write {get; init;}
public ValueLessConstNode Delete {get; init;}
public const string NodePath = "Permissions";
public const string[] NodeValue = new [] { "Permissions.Read", "Permissions.Write", "Permissions.Delete" };
}
Accessing Nodes
Nodes can be accessed directly through the generated tree.
var node = MyConstants.Parent1.Group1.Child1;
var value = node.NodeValue;
var path = node.NodePath;
Root Helper Methods
The generated root class contains helper methods for working with nodes without reflection.
Get all nodes
foreach (var node in MyConstants.GetAllNodes())
{
Console.WriteLine(node.NodePath);
}
Get all end nodes
End nodes are nodes that do not contain any child nodes.
foreach (var node in MyConstants.GetAllEndNodes())
{
Console.WriteLine(node.NodePath);
}
Find a node by path
The FindNode function is used to find a node based on its string value, throws an exception if the provided node does not exist. The TryFindNode functions is a safer way to get a node without getting a exception
var node = MyConstants.FindNode("Parent1.Group1.Child1");
if(MyConstants.TryFindNode("Parent1.Group1.Child1", out var node2))
{
}
Get a node value directly
This function can be used on value nodes where you know the expected value type. This method throws a exception if the node is not found or the valuetype cannot be converted.
bool isEnabled = MyConstants.GetNodeValue<bool>(featureFlagKey);
Node Classes
Interfaces
IConstNode
This is the basic interface that is applied on all node classes. All implementations of the IConstNode also have the ToString() method overwritten to return the NodePath
/// <summary>
/// The base interface that is applied on all the const nodes
/// </summary>
public interface IConstNode
{
/// <summary>
/// Get the absolute path of this node
/// </summary>
public string NodePath { get; }
}
IValueConstNode
/// <summary>
/// A value node is a <see cref="IConstNode"/> that also contains a value
/// </summary>
interface IValueConstNode : IConstNode
{
/// <summary>
/// The current value of this node
/// </summary>
public object NodeValue { get; }
}
IParentConstNode
/// <summary>
/// A parent node that contains child nodes
/// </summary>
public interface IParentConstNode : IConstNode
{
/// <summary>
/// Returns all the child nodes of this parent
/// </summary>
/// <returns></returns>
public IEnumerable<IConstNode> GetChildNodes();
/// <summary>
/// Returns all the child nodes of this parent, but also include sub children
/// </summary>
/// <returns></returns>
public IEnumerable<IConstNode> GetChildNodesRecursive();
}
Structs
ValueLessConstNode
A valueless node can implicitly convert to a string representing its path. Since this is likely the only available property
/// <summary>
/// A endnode which does not contain a value
/// </summary>
public struct ValueLessConstNode : IConstNode
{
public ValueLessConstNode(string path)
{
NodePath = path;
}
public string NodePath { get; }
public override string ToString() => NodePath;
public static implicit operator string(ValueLessConstNode node) => node.NodePath;
}
ValueConstNode<T>
A ConstNode which also contains a value. A ConstNode can implicitly parse the value, while providing the NodePath with .ToString()
/// <summary>
/// A const node which contains a value
/// </summary>
/// <typeparam name="T"></typeparam>
public struct ValueConstNode<T> : IValueConstNode, IConstNode
{
public ValueConstNode(string path, T value)
{
NodePath = path;
NodeValue = value;
}
public T NodeValue { get; }
public string NodePath { get; }
object IValueConstNode.NodeValue => NodeValue;
public override string ToString() => NodePath;
public static implicit operator T(ValueConstNode<T> node) => node.NodeValue;
}
Parent Nodes
Parent nodes implement the [IParentConstNode] and are generated at compile time for each object required, but can be reused if multiple objects contain the exact same properties. The .ToString() is overwritten to return the NodePath of this parentnode.
Requirements
- .NET SDK supporting C# Source Generators
- JSON files included as
AdditionalFiles
License
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 was computed. 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
- No dependencies.
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.0.0 | 110 | 7/11/2026 |