JsonToolkit.STJ 1.0.3

dotnet add package JsonToolkit.STJ --version 1.0.3
                    
NuGet\Install-Package JsonToolkit.STJ -Version 1.0.3
                    
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="JsonToolkit.STJ" Version="1.0.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="JsonToolkit.STJ" Version="1.0.3" />
                    
Directory.Packages.props
<PackageReference Include="JsonToolkit.STJ" />
                    
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 JsonToolkit.STJ --version 1.0.3
                    
#r "nuget: JsonToolkit.STJ, 1.0.3"
                    
#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 JsonToolkit.STJ@1.0.3
                    
#: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=JsonToolkit.STJ&version=1.0.3
                    
Install as a Cake Addin
#tool nuget:?package=JsonToolkit.STJ&version=1.0.3
                    
Install as a Cake Tool

JsonToolkit.STJ

NuGet Version NuGet Downloads License: MIT

JsonToolkit.STJ is a comprehensive library that enhances System.Text.Json with developer-friendly features while maintaining high performance. It provides drop-in helpers for common JSON operations, making it easier for developers to migrate from Newtonsoft.Json or work more efficiently with System.Text.Json.

๐Ÿš€ Key Features

  • ๐Ÿ”„ Deep Merge: Recursively merge JSON objects with configurable conflict resolution
  • ๐Ÿฉน JSON Patch: Apply RFC 6902 JSON Patch operations (add, remove, replace, move, copy, test)
  • ๐ŸŽญ Polymorphic Deserialization: Simplified handling of inheritance hierarchies with type discriminators
  • โš™๏ธ Optional Property Defaults: Gracefully handle missing JSON properties with fallback values
  • ๐Ÿ”ค Case-Insensitive Matching: Property matching that works regardless of casing
  • ๐Ÿท๏ธ Flexible Enums: String/numeric enum serialization with case-insensitive matching
  • ๐ŸŽฏ Enhanced Null Handling: Distinguish between missing properties, null values, and defaults
  • ๐Ÿ” JsonPath Queries: Query JSON documents using JsonPath expressions with wildcards and filters
  • ๐Ÿ”— LINQ-to-JSON: Familiar LINQ operations for querying and transforming JSON data
  • โœ… JSON Schema Validation: Comprehensive validation with detailed error reporting using JSON Schema
  • ๐Ÿ”„ Object Transformation: Map between different object shapes through JSON with JsonMapper
  • ๐Ÿท๏ธ Validation Attributes: Declarative validation with custom attributes (JsonRange, JsonLength, JsonPattern)
  • ๐Ÿ†• Modern C# Support: Records, init-only properties, required properties, immutable collections
  • ๐Ÿ”ง Extension Methods: Newtonsoft.Json-style convenience methods (ToJson, FromJson, DeepClone)
  • ๐Ÿ“Š Dynamic Access: JElement class for JObject-like functionality
  • โšก Enhanced Error Handling: Better error messages and context information
  • โš–๏ธ JSON Equality: Semantic JSON comparison ignoring formatting and property order
  • ๐Ÿ“‹ Configuration Support: Specialized helpers for appsettings.json scenarios
  • ๐ŸŽฏ Multi-Framework: .NET Framework 4.6.2+, .NET Standard 2.0, .NET 6.0+, .NET 9.0

๐Ÿ“ฆ Installation

dotnet add package JsonToolkit.STJ

๐Ÿƒโ€โ™‚๏ธ Quick Start

Basic Usage

using JsonToolkit.STJ;
using JsonToolkit.STJ.Extensions;

// Enable JsonToolkit.STJ with validation by default (recommended for security)
var options = new JsonSerializerOptions().EnableJsonToolkit();

// Newtonsoft.Json-style extensions
var json = myObject.ToJson(options);
var obj = json.FromJson<MyType>(options);
var cloned = myObject.DeepClone();

// System.Text.Json-style enhanced methods
var json2 = JsonSerializer.SerializeEnhanced(myObject, options);
var obj2 = JsonSerializer.DeserializeEnhanced<MyType>(json2, options);

// For performance-critical scenarios, explicitly opt-out of validation
var fastOptions = new JsonSerializerOptions().EnableJsonToolkit().WithoutValidation();

Deep Merge

using System.Text.Json;
using JsonToolkit.STJ;

var config1 = """{"database": {"host": "localhost", "port": 5432}}""";
var config2 = """{"database": {"port": 3306, "ssl": true}}""";

var merged = JsonMerge.DeepMerge(
    JsonDocument.Parse(config1).RootElement,
    JsonDocument.Parse(config2).RootElement
);
// Result: {"database": {"host": "localhost", "port": 3306, "ssl": true}}

JSON Patch

using System.Text.Json;
using JsonToolkit.STJ;

var document = JsonDocument.Parse("""{"name": "John", "age": 30}""");

var patch = new JsonPatchDocument()
    .Replace("/age", 31)
    .Add("/email", "john@example.com")
    .Test("/name", "John");

var result = patch.ApplyTo(document.RootElement);
// Result: {"name": "John", "age": 31, "email": "john@example.com"}

JsonPath Queries

using System.Text.Json;
using JsonToolkit.STJ;

var json = """
{
  "users": [
    {"name": "John", "age": 30},
    {"name": "Jane", "age": 25}
  ]
}
""";

var adults = JsonPath.Query(JsonDocument.Parse(json).RootElement, "$.users[?(@.age >= 18)]");
// Returns both users since both are 18 or older

JSON Schema Validation

using System.Text.Json;
using JsonToolkit.STJ;

var schema = """
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "age": {"type": "number", "minimum": 0}
  },
  "required": ["name", "age"]
}
""";

var validator = new JsonSchemaValidator(schema);
var json = """{"name": "John", "age": 30}""";

var result = validator.Validate(json);
if (result.IsValid)
{
    Console.WriteLine("JSON is valid!");
}
else
{
    foreach (var error in result.Errors)
    {
        Console.WriteLine($"Error at {error.Path}: {error.Message}");
    }
}

Object Mapping

using JsonToolkit.STJ;

// Define source and target types
public class UserDto
{
    public string FullName { get; set; }
    public int YearsOld { get; set; }
}

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string DisplayName { get; set; }
}

// Configure mapping
var mapper = JsonMapper.Create()
    .Map<UserDto, User>(config => config
        .ForMember(u => u.Name, dto => dto.FullName)
        .ForMember(u => u.Age, dto => dto.YearsOld)
        .ForMember(u => u.DisplayName, dto => $"User: {dto.FullName}"));

// Transform objects
var dto = new UserDto { FullName = "John Doe", YearsOld = 30 };
var user = mapper.Transform<UserDto, User>(dto);
// Result: User with Name="John Doe", Age=30, DisplayName="User: John Doe"

Configuration with Fluent API

using JsonToolkit.STJ;

// Recommended: Enable all JsonToolkit.STJ features with validation by default
var options = new JsonSerializerOptions().EnableJsonToolkit();

// Or configure manually with fluent API
var customOptions = new JsonOptionsBuilder()
    .WithCaseInsensitiveProperties()
    .WithFlexibleEnums()
    .WithValidation() // Validation enabled explicitly
    .WithOptionalDefaults(new { timeout = 30, retries = 3 })
    .WithPolymorphicTypes(config => config
        .ForBaseType<Animal>()
        .WithDiscriminator("type")
        .MapType("dog", typeof(Dog))
        .MapType("cat", typeof(Cat)))
    .Build();

// For performance-critical scenarios, opt-out of validation
var performanceOptions = new JsonSerializerOptions()
    .EnableJsonToolkit()
    .WithoutValidation(); // Explicit opt-out for performance

// Use the configured options
var json = JsonSerializer.Serialize(myObject, options);
var obj = JsonSerializer.Deserialize<MyType>(json, options);

๐ŸŒŸ Real-World Examples

Configuration File Merging

using System.Text.Json;
using JsonToolkit.STJ;

// Base configuration
var baseConfig = """
{
  "database": {
    "host": "localhost",
    "port": 5432,
    "timeout": 30
  },
  "logging": {
    "level": "Information"
  }
}
""";

// Environment-specific overrides
var prodConfig = """
{
  "database": {
    "host": "prod-db.company.com",
    "ssl": true
  },
  "logging": {
    "level": "Warning"
  }
}
""";

// Merge configurations
var merged = JsonMerge.DeepMerge(
    JsonDocument.Parse(baseConfig).RootElement,
    JsonDocument.Parse(prodConfig).RootElement
);

// Result: Combined configuration with production overrides
// {
//   "database": {
//     "host": "prod-db.company.com",
//     "port": 5432,
//     "timeout": 30,
//     "ssl": true
//   },
//   "logging": {
//     "level": "Warning"
//   }
// }

REST API Integration

using Microsoft.AspNetCore.Mvc;
using JsonToolkit.STJ;
using JsonToolkit.STJ.Extensions;

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    // Configure JsonToolkit.STJ with validation enabled by default
    private static readonly JsonSerializerOptions _jsonOptions = 
        new JsonSerializerOptions().EnableJsonToolkit();

    [HttpPost]
    public async Task<IActionResult> CreateUser([FromBody] CreateUserRequest request)
    {
        try
        {
            // Validation happens automatically during model binding with EnableJsonToolkit()
            // Or validate manually if needed
            var validationResult = request.Validate();
            if (!validationResult.IsValid)
            {
                return BadRequest(validationResult.Errors);
            }

            // Transform DTO to domain model
            var mapper = JsonMapper.Create();
            var user = mapper.Transform<CreateUserRequest, User>(request);

            // Save user (implementation omitted)
            await SaveUserAsync(user);

            // Return response
            var response = new { Id = user.Id, Message = "User created successfully" };
            return Ok(response.ToJson(_jsonOptions));
        }
        catch (JsonValidationException ex)
        {
            // Enhanced error messages with specific property details
            return BadRequest(new { 
                Error = "Validation failed", 
                Details = ex.ValidationErrors.Select(e => new { 
                    Property = e.PropertyName, 
                    Message = e.Message,
                    Rule = e.ValidationRule 
                })
            });
        }
    }
}

public class CreateUserRequest
{
    [JsonLength(2, 50)]
    public string Name { get; set; }

    [JsonPattern(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")]
    public string Email { get; set; }

    [JsonRange(18, 120)]
    public int Age { get; set; }
}

Complex Data Transformation

using JsonToolkit.STJ;
using JsonToolkit.STJ.Extensions;

// Transform complex nested data structures
var sourceData = new
{
    customer = new
    {
        personal_info = new { first_name = "John", last_name = "Doe" },
        contact = new { email = "john@example.com", phone = "555-1234" }
    },
    orders = new[]
    {
        new { id = 1, amount = 99.99, status = "completed" },
        new { id = 2, amount = 149.50, status = "pending" }
    }
};

// Configure transformation mapping
var mapper = JsonMapper.Create()
    .Map<dynamic, CustomerSummary>(config => config
        .ForMember(c => c.FullName, src => $"{src.customer.personal_info.first_name} {src.customer.personal_info.last_name}")
        .ForMember(c => c.Email, src => src.customer.contact.email)
        .ForMember(c => c.TotalOrders, src => src.orders.Length)
        .ForMember(c => c.TotalAmount, src => src.orders.Sum(o => o.amount)));

var summary = mapper.Transform<dynamic, CustomerSummary>(sourceData);
// Result: CustomerSummary with aggregated data

๐Ÿ“š Documentation

Migration from Newtonsoft.Json

JsonToolkit.STJ provides familiar APIs to ease migration:

using JsonToolkit.STJ.Extensions;

// Before (Newtonsoft.Json)
var json = JsonConvert.SerializeObject(obj);
var obj = JsonConvert.DeserializeObject<MyType>(json);
var jobj = JObject.Parse(json);
var value = jobj["property"]["nested"].Value<string>();

// After (JsonToolkit.STJ)
var json = obj.ToJson();
var obj = json.FromJson<MyType>();
var jelem = JElement.Parse(json);
var value = jelem["property"]["nested"].Value<string>();

Advanced Features

Polymorphic Deserialization
using JsonToolkit.STJ;
using JsonToolkit.STJ.Extensions;

public abstract class Shape
{
    public string Color { get; set; }
}

public class Circle : Shape
{
    public double Radius { get; set; }
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
}

var options = new JsonOptionsBuilder()
    .WithPolymorphicTypes(config => config
        .ForBaseType<Shape>()
        .WithDiscriminator("$type")
        .MapType("circle", typeof(Circle))
        .MapType("rectangle", typeof(Rectangle)))
    .Build();

var json = """{"$type": "circle", "color": "red", "radius": 5.0}""";
var shape = json.FromJson<Shape>(options); // Returns Circle instance
Validation Attributes

๐Ÿ”’ Security Note: Validation is now enabled by default when using EnableJsonToolkit() for enhanced security. Use WithoutValidation() only in performance-critical scenarios where you trust the input data.

๐Ÿ“ Validation Attributes: JsonToolkit.STJ uses its own validation attributes ([JsonRange], [JsonLength], [JsonPattern]) which are optimized for JSON scenarios. Standard .NET validation attributes ([Required], [Range]) are not automatically processed by JsonToolkit.STJ validation.

using JsonToolkit.STJ;
using JsonToolkit.STJ.Extensions;
using JsonToolkit.STJ.ValidationAttributes;

public class User
{
    [JsonRange(1, 120)]
    public int Age { get; set; }
    
    [JsonLength(2, 50)]
    public string Name { get; set; }
    
    [JsonPattern(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")]
    public string Email { get; set; }
}

// Validation happens automatically with EnableJsonToolkit()
var options = new JsonSerializerOptions().EnableJsonToolkit();
var json = """{"name": "John", "age": 30, "email": "john@example.com"}""";

try 
{
    var user = JsonSerializer.Deserialize<User>(json, options); // Validates automatically
}
catch (JsonValidationException ex)
{
    // Enhanced error messages with specific property details
    Console.WriteLine($"Validation failed: {ex.Message}");
    foreach (var error in ex.ValidationErrors)
    {
        Console.WriteLine($"  - {error.PropertyName}: {error.Message}");
    }
}

// Legacy explicit validation method (still supported)
var user2 = json.ValidateAndDeserialize<User>();

// Or validate an existing object
var existingUser = new User { Name = "John", Age = 30, Email = "john@example.com" };
var validationResult = existingUser.Validate();
if (!validationResult.IsValid)
{
    foreach (var error in validationResult.Errors)
    {
        Console.WriteLine($"Error: {error.Message}");
    }
}

// For performance-critical scenarios, explicitly opt-out
var fastOptions = new JsonSerializerOptions().EnableJsonToolkit().WithoutValidation();
var userWithoutValidation = JsonSerializer.Deserialize<User>(json, fastOptions);

๐Ÿ”ง Troubleshooting & Common Issues

JSON Patch Path Casing

Problem: JSON Patch operations fail with "path not found" errors.

Solution: JSON Patch paths are case-sensitive and must match the exact property names in your JSON:

// โŒ Wrong - property name casing doesn't match
var patch = new JsonPatchDocument()
    .Replace("/name", "Jane");  // JSON has "Name" not "name"

// โœ… Correct - exact case match
var patch = new JsonPatchDocument()
    .Replace("/Name", "Jane");  // Matches JSON property exactly

Validation Not Triggering

Problem: Validation attributes are ignored during deserialization.

Solution: JsonToolkit.STJ now enables validation by default when using EnableJsonToolkit(). For legacy code, ensure validation is explicitly enabled:

// โœ… Recommended - validation enabled by default
var options = new JsonSerializerOptions().EnableJsonToolkit();
var user = JsonSerializer.Deserialize<User>(json, options);

// โœ… Legacy explicit validation
var options2 = new JsonSerializerOptions().WithValidation();
var user2 = JsonSerializer.Deserialize<User>(json, options2);

// โœ… Extension method (always validates)
var user3 = json.ValidateAndDeserialize<User>();

// โŒ Wrong - validation not enabled
var options3 = new JsonSerializerOptions();
var user4 = JsonSerializer.Deserialize<User>(json, options3);

Performance Note: For performance-critical scenarios where you trust the input data, explicitly opt-out:

var fastOptions = new JsonSerializerOptions().EnableJsonToolkit().WithoutValidation();
var user = JsonSerializer.Deserialize<User>(json, fastOptions);

Performance Optimization

Problem: Slow JSON processing in high-throughput scenarios.

Solution: Reuse JsonSerializerOptions and avoid repeated configuration:

// โŒ Wrong - creates new options every time
public string SerializeUser(User user)
{
    var options = new JsonOptionsBuilder().WithCaseInsensitiveProperties().Build();
    return JsonSerializer.Serialize(user, options);
}

// โœ… Correct - reuse configured options
private static readonly JsonSerializerOptions _options = 
    new JsonOptionsBuilder().WithCaseInsensitiveProperties().Build();

public string SerializeUser(User user)
{
    return JsonSerializer.Serialize(user, _options);
}

Migration from Newtonsoft.Json Gotchas

Key Differences to Watch:

  1. Property Naming: System.Text.Json uses PascalCase by default, Newtonsoft.Json uses the original property names
  2. Null Handling: Different default behaviors for null values
  3. Date Formats: Different default date serialization formats
// Configure JsonToolkit.STJ to match Newtonsoft.Json behavior
var options = new JsonSerializerOptions()
    .EnableJsonToolkit()                  // Enables validation, case-insensitive properties, flexible enums
    .WithNewtonsoftCompatibility();       // Additional Newtonsoft.Json compatibility settings

// For performance-critical scenarios, opt-out of validation if needed
var performanceOptions = new JsonSerializerOptions()
    .EnableJsonToolkit()
    .WithoutValidation()
    .WithNewtonsoftCompatibility();

๐Ÿ“– Additional Features

JSON Equality Comparison

using JsonToolkit.STJ.Extensions;

var json1 = """{"name": "John", "age": 30}""";
var json2 = """{"age": 30, "name": "John"}""";  // Different property order

// Semantic equality (ignores property order)
bool areEqual = json1.SemanticEquals(json2);  // Returns true

// For arrays, you can choose whether order matters
var array1 = """{"items": [1, 2, 3]}""";
var array2 = """{"items": [3, 2, 1]}""";

bool orderSensitive = array1.SemanticEquals(array2, orderSensitive: true);   // false
bool orderInsensitive = array1.SemanticEquals(array2, orderSensitive: false); // true

Async Stream Operations

using JsonToolkit.STJ;
using JsonToolkit.STJ.Extensions;

// Async serialization to stream
var user = new User { Name = "John", Age = 30 };
using var stream = new FileStream("user.json", FileMode.Create);
await user.ToJsonAsync(stream);

// Async deserialization from stream
using var readStream = new FileStream("user.json", FileMode.Open);
var deserializedUser = await JsonSerializer.DeserializeEnhancedAsync<User>(readStream);

// Enhanced async methods with better error handling
await JsonSerializer.SerializeEnhancedAsync(stream, user, options);
var result = await JsonSerializer.DeserializeEnhancedAsync<User>(stream, options);

Byte Array Operations

using JsonToolkit.STJ.Extensions;

var user = new User { Name = "John", Age = 30 };

// Serialize to UTF-8 bytes
byte[] jsonBytes = user.ToJsonBytes();

// Enhanced serialization with error handling
byte[] enhancedBytes = JsonSerializer.SerializeEnhancedToUtf8Bytes(user, options);

// Deserialize from bytes
var deserializedUser = JsonSerializer.DeserializeEnhanced<User>(jsonBytes, options);

LINQ-to-JSON Operations

using System.Text.Json;
using JsonToolkit.STJ;

var json = """
{
  "products": [
    {"name": "Laptop", "price": 999.99, "category": "Electronics"},
    {"name": "Book", "price": 19.99, "category": "Education"},
    {"name": "Phone", "price": 599.99, "category": "Electronics"}
  ]
}
""";

var document = JsonDocument.Parse(json);
var products = document.RootElement.GetProperty("products");

// Filter products using LINQ-style methods
var expensiveProducts = products.Where(p => 
    p.GetProperty("price").GetDouble() > 100);

// Project to new values
var productNames = products.Select(p => 
    p.GetProperty("name").GetString());

// Aggregate operations
var totalValue = products.Sum(p => 
    p.GetProperty("price").GetDouble());

var averagePrice = products.Average(p => 
    p.GetProperty("price").GetDouble());

// Find specific items
var firstElectronics = products.FirstOrDefault(p => 
    p.GetProperty("category").GetString() == "Electronics");

// Count items
var electronicsCount = products.Count(p => 
    p.GetProperty("category").GetString() == "Electronics");

๐ŸŽฏ Performance

JsonToolkit.STJ maintains System.Text.Json's performance characteristics while adding powerful features. Here are benchmark results from our comprehensive performance tests:

Benchmark Results

Operation .NET 9.0 .NET 8.0 .NET Framework 4.6.2 Description
Basic Serialization 100ms (1000 iterations) 53ms (1000 iterations) 385ms (1000 iterations) Raw serialization performance vs System.Text.Json
Basic Deserialization 200ms (1000 iterations) 111ms (1000 iterations) 768ms (1000 iterations) Raw deserialization performance vs System.Text.Json
Deep Merge 0.03ms per operation 0.03ms per operation 0.08ms per operation Recursive JSON object merging
JSON Patch 0.02ms per operation 0.02ms per operation 0.09ms per operation RFC 6902 patch operations
JsonPath Query 0.64ms per operation 0.66ms per operation 2.70ms per operation Query 1000 items with filter
JElement Access 0.001ms per operation 0.002ms per operation 0.006ms per operation Dynamic property access
Large Document (1.2MB) Serialize: 6ms, Deserialize: 13ms Serialize: 2ms, Deserialize: 12ms Serialize: 23ms, Deserialize: 41ms Processing large JSON documents
Memory Usage ~4.3MB increase (100 operations) ~2.5MB increase (100 operations) ~3.3MB increase (100 operations) Memory overhead for mixed operations

Performance Characteristics

  • Memory Efficient: Minimal allocations through efficient algorithms and object pooling
  • High Throughput: Performance identical to raw System.Text.Json for basic operations
  • Streaming Support: Async-friendly APIs for large documents
  • Framework Optimized: Better performance on modern .NET compared to .NET Framework
  • Scalable: Efficient processing of large documents (1MB+ tested)

๐Ÿ”ง Framework Support

Framework Version Status
.NET Framework 4.6.2+ โœ… Supported
.NET Standard 2.0+ โœ… Supported
.NET Core 3.1+ โœ… Supported
.NET 5.0+ โœ… Supported

๐Ÿš€ CI/CD Pipeline

This project uses GitHub Actions for continuous integration and automated NuGet publishing with modern security practices.

Build Status

CI Release

Features

  • ๐Ÿ”’ Secure Publishing: Uses NuGet Trusted Publishing with OIDC (no API keys stored)
  • ๐Ÿงช Comprehensive Testing: Unit tests and property-based tests across multiple frameworks
  • ๐Ÿ“Š Coverage Reports: Automated code coverage reporting and artifact collection
  • ๐Ÿ—๏ธ Multi-Framework Builds: Tests on .NET Framework 4.6.2, .NET 6.0, .NET 8.0, and .NET 9.0
  • โšก Optimized Performance: Efficient caching and parallel execution
  • ๐Ÿ“ฆ Automated Releases: Version tags trigger automatic NuGet publishing

Release Process

  1. Create Version Tag: Push a tag in format v{MAJOR}.{MINOR}.{PATCH} (e.g., v1.2.3)
  2. Automated Build: GitHub Actions builds and tests across all target frameworks
  3. Package Creation: NuGet package created with proper versioning and metadata
  4. Secure Publishing: Package published to NuGet.org using OIDC authentication
  5. GitHub Release: Automated GitHub release created with artifacts and checksums

Development Workflow

# Run tests locally
dotnet test

# Build release package
dotnet pack --configuration Release

๐Ÿค Contributing

We welcome contributions! Please see our Contributing Guide for details.

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ™ Acknowledgments


JsonToolkit.STJ - Bridging the gap between System.Text.Json performance and Newtonsoft.Json convenience.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  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 is compatible.  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.0.3 124 12/27/2025
1.0.2 184 12/26/2025
1.0.1 202 12/23/2025
1.0.0 198 12/23/2025

v1.0.0 - Initial Release
     โ€ข Deep merge functionality for JSON objects with conflict resolution
     โ€ข JSON Patch operations (add, remove, replace, move, copy, test)
     โ€ข Polymorphic deserialization with type discriminators
     โ€ข Optional property defaults system
     โ€ข Case-insensitive property matching
     โ€ข Flexible enum serialization (string/numeric with case-insensitive matching)
     โ€ข Enhanced null handling (distinguish missing vs null vs default)
     โ€ข JsonPath query engine with wildcards and filters
     โ€ข LINQ-to-JSON extensions for querying and transformation
     โ€ข JSON Schema validation with comprehensive error reporting
     โ€ข Configuration file support with environment-specific overrides
     โ€ข Object transformation/mapping through JSON
     โ€ข Validation attributes for declarative constraints
     โ€ข Modern C# support (records, init-only properties, immutable collections)
     โ€ข Newtonsoft.Json-style extension methods (ToJson, FromJson, DeepClone)
     โ€ข JElement class for dynamic JSON access
     โ€ข Multi-framework support (.NET Framework 4.6.2+, .NET Standard 2.0, .NET 6.0+)
     โ€ข Comprehensive error handling with detailed context information
     โ€ข High performance with System.Text.Json foundation