StructuredJson 1.0.0
dotnet add package StructuredJson --version 1.0.0
NuGet\Install-Package StructuredJson -Version 1.0.0
<PackageReference Include="StructuredJson" Version="1.0.0" />
<PackageVersion Include="StructuredJson" Version="1.0.0" />
<PackageReference Include="StructuredJson" />
paket add StructuredJson --version 1.0.0
#r "nuget: StructuredJson, 1.0.0"
#:package StructuredJson@1.0.0
#addin nuget:?package=StructuredJson&version=1.0.0
#tool nuget:?package=StructuredJson&version=1.0.0
StructuredJson
A powerful .NET library for creating, reading, and updating JSON objects using a path-based API. Built with Dictionary<string, object?>
as the underlying data structure and System.Text.Json
for serialization.
Features
- Path-based API: Use intuitive path syntax to navigate and manipulate JSON structures
- .NET Standard 2.0: Compatible with all modern .NET platforms
- System.Text.Json: Uses the modern, high-performance JSON serializer
- Type Safety: Generic methods with intelligent type conversion for strongly-typed value retrieval
- Comprehensive: Full CRUD operations with path validation and robust error handling
- Smart Type Conversion: Automatic conversion between strings and numbers, plus JsonElement handling
- Array Support: Full array manipulation with automatic null-filling for sparse arrays
- Well Documented: Complete XML documentation and extensive unit tests
Installation
Install via NuGet Package Manager:
dotnet add package StructuredJson
Or via Package Manager Console:
Install-Package StructuredJson
Quick Start
using StructuredJson;
// Create a new instance
var sj = new StructuredJson();
// Set values using path syntax
sj.Set("user:name", "John Doe");
sj.Set("user:age", 30);
sj.Set("user:addresses[0]:city", "Ankara");
sj.Set("user:addresses[0]:country", "Turkey");
sj.Set("user:addresses[1]:city", "Istanbul");
// Get values with automatic type conversion
var name = sj.Get("user:name"); // "John Doe"
var age = sj.Get<int>("user:age"); // 30
var city = sj.Get("user:addresses[0]:city"); // "Ankara"
// Convert to JSON
var json = sj.ToJson();
Console.WriteLine(json);
// List all paths
var paths = sj.ListPaths();
foreach (var kvp in paths)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
Path Syntax
The library uses a simple and intuitive path syntax with comprehensive validation:
Object Properties: Use
:
to separate object properties"user:name"
→user.name
"config:database:host"
→config.database.host
Array Elements: Use
[index]
to access array elements"users[0]"
→users[0]
"items[2]:name"
→items[2].name
Complex Paths: Combine objects and arrays
"user:addresses[0]:city"
→user.addresses[0].city
"data:items[1]:properties[0]:value"
→data.items[1].properties[0].value
Path Validation
The library performs comprehensive path validation and throws ArgumentException
for:
- Empty or null paths
- Empty array indices (
items[]
) - Invalid array indices (non-numeric:
items[abc]
) - Negative array indices (
items[-1]
) - Multiple array indices in one segment (
items[0][1]
) - Invalid path segments
API Reference
Constructors
// Create empty structure
var sj = new StructuredJson();
// Create from JSON string (with validation)
var sj = new StructuredJson(jsonString);
Core Methods
Set(string path, object? value)
Sets a value at the specified path. Creates nested structures and arrays automatically.
sj.Set("user:name", "John");
sj.Set("user:addresses[0]:city", "Ankara");
sj.Set("items[5]", "value"); // Creates sparse array with nulls at indices 0-4
Throws: ArgumentException
for invalid paths
Get(string path)
Gets a value from the specified path. Returns null
if path doesn't exist.
var value = sj.Get("user:name");
Throws: ArgumentException
for invalid paths
Get<T>(string path)
Gets a strongly-typed value with intelligent type conversion.
var age = sj.Get<int>("user:age");
var isActive = sj.Get<bool>("user:isActive");
var name = sj.Get<string>("user:name");
// Smart conversions
sj.Set("stringNumber", "42");
var number = sj.Get<int>("stringNumber"); // Returns 42
sj.Set("numberAsString", 123);
var text = sj.Get<string>("numberAsString"); // Returns "123"
Type Conversion Features:
- String ↔ Number conversions (int, long, double, decimal, float)
- JsonElement handling for complex deserialization
- Fallback to JsonSerializer for complex types
- Returns
default(T)
for failed conversions
ToJson(JsonSerializerOptions? options = null)
Converts the structure to a JSON string with optional formatting.
var json = sj.ToJson(); // Pretty-printed by default
var compactJson = sj.ToJson(new JsonSerializerOptions { WriteIndented = false });
ListPaths()
Returns all paths and their values as a dictionary. Handles sparse arrays intelligently.
var paths = sj.ListPaths();
// Returns: Dictionary<string, object?>
// Note: Only includes non-null values for array elements (sparse array support)
HasPath(string path)
Checks if a path exists in the structure with safe error handling.
bool exists = sj.HasPath("user:name");
// Returns false for invalid paths instead of throwing
Remove(string path)
Removes a value at the specified path. For arrays, removes the element and shifts indices.
bool removed = sj.Remove("user:age");
bool arrayRemoved = sj.Remove("items[1]"); // Removes and shifts remaining elements
Clear()
Removes all data from the structure.
sj.Clear();
Examples
Working with Complex Structures
var sj = new StructuredJson();
// Create a user with multiple addresses
sj.Set("user:name", "John Doe");
sj.Set("user:age", 30);
sj.Set("user:isActive", true);
// Home address
sj.Set("user:addresses[0]:type", "home");
sj.Set("user:addresses[0]:street", "123 Main St");
sj.Set("user:addresses[0]:city", "Ankara");
sj.Set("user:addresses[0]:country", "Turkey");
// Work address
sj.Set("user:addresses[1]:type", "work");
sj.Set("user:addresses[1]:street", "456 Business Ave");
sj.Set("user:addresses[1]:city", "Istanbul");
sj.Set("user:addresses[1]:country", "Turkey");
// Hobbies array
sj.Set("user:hobbies[0]", "reading");
sj.Set("user:hobbies[1]", "swimming");
sj.Set("user:hobbies[2]", "coding");
// Convert to JSON
var json = sj.ToJson();
Loading from Existing JSON
var existingJson = """
{
"config": {
"database": {
"host": "localhost",
"port": 5432,
"name": "mydb"
},
"features": ["auth", "logging", "caching"]
}
}
""";
var sj = new StructuredJson(existingJson);
// Read values with type conversion
var host = sj.Get("config:database:host"); // "localhost"
var port = sj.Get<int>("config:database:port"); // 5432
var firstFeature = sj.Get("config:features[0]"); // "auth"
// Modify values
sj.Set("config:database:host", "production-server");
sj.Set("config:features[3]", "monitoring");
// Get updated JSON
var updatedJson = sj.ToJson();
Advanced Array Manipulation
var sj = new StructuredJson();
// Create sparse array (automatically fills with nulls)
sj.Set("items[5]", "value at index 5");
// items[0] through items[4] will be null
var nullValue = sj.Get("items[0]"); // null
var actualValue = sj.Get("items[5]"); // "value at index 5"
// Add more items
sj.Set("items[0]", "first item");
sj.Set("items[1]", "second item");
// Remove an item (shifts indices)
sj.Remove("items[1]");
// Now items[1] contains what was previously at items[2]
// ListPaths() only shows non-null array elements
var paths = sj.ListPaths();
// Won't include null array elements in the output
Type Conversion Examples
var sj = new StructuredJson();
// String to number conversions
sj.Set("stringInt", "42");
sj.Set("stringDouble", "3.14");
sj.Set("stringDecimal", "99.99");
var intValue = sj.Get<int>("stringInt"); // 42
var doubleValue = sj.Get<double>("stringDouble"); // 3.14
var decimalValue = sj.Get<decimal>("stringDecimal"); // 99.99
// Number to string conversions
sj.Set("numberValue", 123);
var stringValue = sj.Get<string>("numberValue"); // "123"
// Complex type handling via JsonSerializer
sj.Set("complexObject", new { Name = "Test", Value = 42 });
var complexResult = sj.Get<Dictionary<string, object>>("complexObject");
Error Handling
The library provides comprehensive error handling with specific exception types:
try
{
var sj = new StructuredJson();
// These will throw ArgumentException with descriptive messages
sj.Set("", "value"); // "Path cannot be null or empty"
sj.Set(null, "value"); // "Path cannot be null or empty"
sj.Set("items[]", "value"); // "Empty array index in path"
sj.Set("items[abc]", "value"); // "Invalid array index 'abc' in path"
sj.Set("items[-1]", "value"); // "Negative array index '-1' in path"
sj.Set("items[0][1]", "value"); // "Multiple array indices not supported"
// Invalid JSON in constructor
var invalid = new StructuredJson("{invalid json}"); // ArgumentException with JsonException inner
}
catch (ArgumentException ex)
{
Console.WriteLine($"Invalid argument: {ex.Message}");
if (ex.InnerException is JsonException jsonEx)
{
Console.WriteLine($"JSON Error: {jsonEx.Message}");
}
}
// Safe methods that don't throw
bool exists = sj.HasPath("invalid[path"); // Returns false instead of throwing
bool removed = sj.Remove("invalid[path"); // Returns false instead of throwing
Performance Considerations
- Dictionary-based: Uses
Dictionary<string, object?>
internally for O(1) key lookups - List semantics: Array operations maintain proper list behavior with appropriate performance
- System.Text.Json: Leverages high-performance JSON serialization
- Regex optimization: Path parsing uses compiled regex patterns
- Lazy evaluation: Type conversions are performed only when needed
- Memory efficient: Sparse arrays don't allocate unnecessary null elements in ListPaths()
Thread Safety
StructuredJson
is not thread-safe. If you need to access the same instance from multiple threads, implement appropriate synchronization mechanisms such as:
private readonly object _lock = new object();
private readonly StructuredJson _sj = new StructuredJson();
public void SafeSet(string path, object value)
{
lock (_lock)
{
_sj.Set(path, value);
}
}
Best Practices
- Path Validation: Always handle
ArgumentException
when working with dynamic paths - Type Safety: Use generic
Get<T>()
methods for better type safety - Error Handling: Use
HasPath()
to check existence beforeGet()
operations - Performance: Cache
ListPaths()
results if called frequently - Memory: Call
Clear()
when reusing instances with large datasets
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
Version 1.0.0
- Initial release
- Path-based JSON manipulation API
- Support for .NET Standard 2.0
- Intelligent type conversion system
- Comprehensive path validation
- Sparse array support
- Robust error handling
- Full XML documentation
- Comprehensive unit test coverage
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 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 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
- System.Text.Json (>= 9.0.0)
-
net8.0
- System.Text.Json (>= 9.0.0)
-
net9.0
- System.Text.Json (>= 9.0.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.0.0 | 234 | 6/13/2025 |
See CHANGELOG.md for release notes