
Security News
npm ‘is’ Package Hijacked in Expanding Supply Chain Attack
The ongoing npm phishing campaign escalates as attackers hijack the popular 'is' package, embedding malware in multiple versions.
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. Supports cross-platform compatibility.
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.
Install via NuGet Package Manager:
dotnet add package StructuredJson
Or via Package Manager Console:
Install-Package StructuredJson
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}");
}
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
The library performs comprehensive path validation and throws ArgumentException
for:
items[]
)items[abc]
)items[-1]
)items[0][1]
)// Create empty structure
var sj = new StructuredJson();
// Create from JSON string (with validation)
var sj = new StructuredJson(jsonString);
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
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
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:
default(T)
for failed conversionsConverts 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 });
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)
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
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
Removes all data from the structure.
sj.Clear();
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();
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();
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
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");
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
Dictionary<string, object?>
internally for O(1) key lookupsStructuredJson
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);
}
}
ArgumentException
when working with dynamic pathsGet<T>()
methods for better type safetyHasPath()
to check existence before Get()
operationsListPaths()
results if called frequentlyClear()
when reusing instances with large datasetsContributions 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.
This project is licensed under the MIT License - see the LICENSE file for details.
FAQs
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. Supports cross-platform compatibility.
We found that structuredjson demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
The ongoing npm phishing campaign escalates as attackers hijack the popular 'is' package, embedding malware in multiple versions.
Security News
A critical flaw in the popular npm form-data package could allow HTTP parameter pollution, affecting millions of projects until patched versions are adopted.
Security News
Bun 1.2.19 introduces isolated installs for smoother monorepo workflows, along with performance boosts, new tooling, and key compatibility fixes.