System.Text.Json

Serialization.NETC#High PerformanceUTF-8Source GenerationThread Safe

Library

System.Text.Json

Overview

System.Text.Json is Microsoft's official high-performance JSON library introduced in .NET Core 3.0. Positioned as the successor to Newtonsoft.Json, it emphasizes performance, security, and standards compliance in its design. With native UTF-8 support, source generation optimization, and thread-safe design, it has established itself as the recommended standard JSON library for new .NET applications in 2025.

Details

System.Text.Json 2025 edition achieves even greater performance improvements by leveraging .NET 8 features. Compared to Newtonsoft.Json, it achieves up to 40% faster processing and significant memory usage reduction, with particularly excellent performance in large dataset processing. It provides modern .NET development features as standard, including elimination of runtime metadata collection through C# source generation, UTF-8 encoding optimization, and application size reduction through trimming support.

Key Features

  • High Performance: Up to 40% faster than Newtonsoft.Json with significant memory reduction
  • UTF-8 Optimization: Efficient processing through native UTF-8 support
  • Source Generation: Runtime performance improvement through compile-time optimization
  • Thread Safe: Safe usage in multi-threaded environments
  • Standards Compliance: Security and compatibility assurance through RFC compliance
  • .NET Integration: Standard library in .NET Core/.NET 5+

Pros and Cons

Pros

  • Overwhelming performance improvement compared to Newtonsoft.Json (up to 40% faster)
  • Efficient memory usage and I/O processing through native UTF-8 support
  • Application startup time reduction and memory reduction through source generation
  • Safety in multi-threaded environments through thread-safe design
  • No additional dependencies required as .NET standard library
  • Application size reduction through Assembly trimming support

Cons

  • Limited advanced features and flexibility compared to Newtonsoft.Json
  • Insufficient custom functionality and converter support
  • Learning cost required for migrating existing Newtonsoft.Json-based code
  • Configuration changes may be needed due to default case-sensitive behavior
  • Potential feature insufficiency for complex JSON schemas or conversion logic
  • Not available in some .NET Framework environments

Reference Pages

Code Examples

Basic Setup

// Included by default in .NET Core 3.0+ / .NET 5+
using System.Text.Json;
using System.Text.Json.Serialization;

// When using as NuGet package (.NET Framework, etc.)
// Install-Package System.Text.Json
<!-- PackageReference for .NET Framework -->
<PackageReference Include="System.Text.Json" Version="8.0.4" />

Basic Serialization and Deserialization

using System.Text.Json;
using System.Text.Json.Serialization;

// Basic POCO
public class User
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public bool IsActive { get; set; }
    public DateTime CreatedAt { get; set; }
    public List<string> Tags { get; set; } = new List<string>();
}

public class BasicSerialization
{
    public static void DemonstrateBasicOperations()
    {
        // Object creation
        var user = new User
        {
            Id = 123,
            Name = "John Doe",
            Email = "[email protected]",
            IsActive = true,
            CreatedAt = DateTime.Now,
            Tags = new List<string> { "admin", "premium" }
        };

        // 1. Object → JSON string
        string jsonString = JsonSerializer.Serialize(user);
        Console.WriteLine($"JSON string: {jsonString}");

        // 2. Object → UTF-8 byte array
        byte[] jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(user);
        Console.WriteLine($"UTF-8 byte count: {jsonUtf8Bytes.Length}");

        // 3. Object → File (async)
        await using var fileStream = File.Create("user.json");
        await JsonSerializer.SerializeAsync(fileStream, user);

        // 4. JSON string → Object
        string jsonInput = """{"Id":456,"Name":"Jane Smith","Email":"[email protected]","IsActive":true,"CreatedAt":"2025-01-01T12:00:00","Tags":["user"]}""";
        User? deserializedUser = JsonSerializer.Deserialize<User>(jsonInput);
        Console.WriteLine($"Deserialized: {deserializedUser?.Name}");

        // 5. UTF-8 byte array → Object
        User? userFromBytes = JsonSerializer.Deserialize<User>(jsonUtf8Bytes);
        Console.WriteLine($"From bytes: {userFromBytes?.Name}");

        // 6. File → Object (async)
        await using var readStream = File.OpenRead("user.json");
        User? userFromFile = await JsonSerializer.DeserializeAsync<User>(readStream);
        Console.WriteLine($"From file: {userFromFile?.Name}");

        // 7. Stream processing
        using var memoryStream = new MemoryStream();
        await JsonSerializer.SerializeAsync(memoryStream, user);
        memoryStream.Position = 0;
        User? userFromStream = await JsonSerializer.DeserializeAsync<User>(memoryStream);
        Console.WriteLine($"From stream: {userFromStream?.Name}");
    }

    // Generic types and collections processing
    public static void GenericCollections()
    {
        // List<T> serialization
        var users = new List<User>
        {
            new User { Id = 1, Name = "John", Email = "[email protected]", IsActive = true, CreatedAt = DateTime.Now },
            new User { Id = 2, Name = "Jane", Email = "[email protected]", IsActive = false, CreatedAt = DateTime.Now }
        };

        string userListJson = JsonSerializer.Serialize(users);
        List<User>? deserializedUsers = JsonSerializer.Deserialize<List<User>>(userListJson);
        Console.WriteLine($"User count: {deserializedUsers?.Count}");

        // Dictionary<TKey, TValue> serialization
        var scoreMap = new Dictionary<string, int>
        {
            ["English"] = 85,
            ["Math"] = 92,
            ["Science"] = 78
        };

        string mapJson = JsonSerializer.Serialize(scoreMap);
        Dictionary<string, int>? deserializedMap = JsonSerializer.Deserialize<Dictionary<string, int>>(mapJson);
        Console.WriteLine($"Subject count: {deserializedMap?.Count}");

        // Complex nested types
        var departmentData = new Dictionary<string, List<User>>
        {
            ["Engineering"] = users.Where(u => u.Id == 1).ToList(),
            ["Sales"] = users.Where(u => u.Id == 2).ToList()
        };

        string complexJson = JsonSerializer.Serialize(departmentData);
        var deserializedComplex = JsonSerializer.Deserialize<Dictionary<string, List<User>>>(complexJson);
        Console.WriteLine($"Department count: {deserializedComplex?.Count}");
    }
}

JsonSerializerOptions Detailed Configuration

using System.Text.Json;
using System.Text.Json.Serialization;

public class ConfigurationExamples
{
    public static JsonSerializerOptions CreateConfiguredOptions()
    {
        var options = new JsonSerializerOptions
        {
            // JSON output formatting
            WriteIndented = true,

            // Property naming policy
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,

            // Enum handling
            Converters =
            {
                new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
            },

            // Ignore unknown properties
            UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,

            // Default value handling
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,

            // Allow reading numbers from strings
            NumberHandling = JsonNumberHandling.AllowReadingFromString,

            // Encoder settings (for non-ASCII characters)
            Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,

            // Include read-only properties
            IncludeFields = false,

            // Case-insensitive comparison
            PropertyNameCaseInsensitive = true,

            // Allow comments and trailing commas
            ReadCommentHandling = JsonCommentHandling.Skip,
            AllowTrailingCommas = true
        };

        return options;
    }

    public static void DemonstrateConfiguration()
    {
        var options = CreateConfiguredOptions();

        var data = new
        {
            UserId = 123,
            UserName = "John Doe",
            UserEmail = "[email protected]",
            CreatedAt = DateTime.Now,
            Status = UserStatus.Active,
            Score = 0, // Default value
            Description = (string?)null // Null value
        };

        // Serialization with applied configuration
        string json = JsonSerializer.Serialize(data, options);
        Console.WriteLine($"Configured JSON:\n{json}");

        // Flexible JSON parsing
        string flexibleJson = """
            {
                // This is a comment
                "userId": "456", // Number specified as string
                "userName": "Jane Smith",
                "userEmail": "[email protected]",
                "status": "inactive", // Lowercase enum
                "score": 95,
                "unknownField": "This will be ignored" // Unmapped field
            }
            """;

        var parsed = JsonSerializer.Deserialize<User>(flexibleJson, options);
        Console.WriteLine($"Flexible parsing result: {parsed?.Name}");
    }

    public enum UserStatus
    {
        Active,
        Inactive,
        Pending
    }
}

Custom Attributes and Converters

using System.Text.Json;
using System.Text.Json.Serialization;

// Advanced POCO using attributes
public class AdvancedUser
{
    [JsonPropertyName("user_id")]
    public int Id { get; set; }

    [JsonPropertyName("full_name")]
    public string Name { get; set; } = string.Empty;

    [JsonIgnore] // Ignore in serialization
    public string Password { get; set; } = string.Empty;

    [JsonInclude] // Include private field
    private string _secretToken = "token123";

    [JsonPropertyName("created_at")]
    [JsonConverter(typeof(CustomDateTimeConverter))]
    public DateTime CreatedAt { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Description { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public List<string> Tags { get; set; } = new List<string>();

    [JsonExtensionData]
    public Dictionary<string, JsonElement>? ExtensionData { get; set; }

    [JsonConstructor]
    public AdvancedUser(int id, string name)
    {
        Id = id;
        Name = name;
        CreatedAt = DateTime.UtcNow;
    }

    public AdvancedUser() { }
}

// Custom DateTime converter
public class CustomDateTimeConverter : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.GetString() is string dateString)
        {
            if (DateTime.TryParseExact(dateString, "yyyy-MM-dd HH:mm:ss", null, DateTimeStyles.None, out DateTime result))
            {
                return result;
            }
        }
        throw new JsonException("Invalid date format");
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
    }
}

// Immutable struct (record)
public record ImmutablePerson(
    [property: JsonPropertyName("person_id")] int Id,
    [property: JsonPropertyName("person_name")] string Name,
    [property: JsonPropertyName("person_email")] string Email
)
{
    [JsonPropertyName("created_at")]
    public DateTime CreatedAt { get; init; } = DateTime.UtcNow;

    [JsonIgnore]
    public string DisplayName => $"{Name} ({Email})";
}

public class AdvancedFeatures
{
    public static void DemonstrateAdvancedFeatures()
    {
        // Using custom attributes
        var user = new AdvancedUser(1, "Advanced User")
        {
            Description = "This is a description",
            Tags = new List<string> { "advanced", "test" }
        };

        var options = new JsonSerializerOptions { WriteIndented = true };
        string json = JsonSerializer.Serialize(user, options);
        Console.WriteLine($"Advanced serialization:\n{json}");

        // JSON with extension data
        string jsonWithExtensions = """
            {
                "user_id": 2,
                "full_name": "Extension User",
                "created_at": "2025-01-01 12:00:00",
                "extra_field1": "Additional data 1",
                "extra_field2": 42,
                "extra_field3": {
                    "nested": "Nested data"
                }
            }
            """;

        AdvancedUser? userWithExtensions = JsonSerializer.Deserialize<AdvancedUser>(jsonWithExtensions, options);
        Console.WriteLine($"Extension data: {userWithExtensions?.ExtensionData?.Count} fields");

        // Record serialization
        var person = new ImmutablePerson(123, "John", "[email protected]");
        string recordJson = JsonSerializer.Serialize(person, options);
        Console.WriteLine($"Record JSON:\n{recordJson}");

        ImmutablePerson? deserializedPerson = JsonSerializer.Deserialize<ImmutablePerson>(recordJson, options);
        Console.WriteLine($"Record: {deserializedPerson?.DisplayName}");
    }
}

Source Generation Optimization

using System.Text.Json;
using System.Text.Json.Serialization;

// Source generation context definition
[JsonSerializable(typeof(User))]
[JsonSerializable(typeof(List<User>))]
[JsonSerializable(typeof(Dictionary<string, User>))]
[JsonSerializable(typeof(PerformanceTestData))]
[JsonSourceGenerationOptions(
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    WriteIndented = true,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
)]
public partial class MyJsonContext : JsonSerializerContext
{
}

// Performance test data
public class PerformanceTestData
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public DateTime Timestamp { get; set; }
    public double Value { get; set; }
    public List<string> Tags { get; set; } = new List<string>();
    public Dictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
}

public class SourceGenerationExample
{
    public static void DemonstrateSourceGeneration()
    {
        var testData = new PerformanceTestData
        {
            Id = 1,
            Name = "Source Generation Test",
            Timestamp = DateTime.UtcNow,
            Value = 123.45,
            Tags = new List<string> { "performance", "source-gen" },
            Metadata = new Dictionary<string, object>
            {
                ["category"] = "test",
                ["priority"] = 1
            }
        };

        // Serialization using source generation
        string json = JsonSerializer.Serialize(testData, MyJsonContext.Default.PerformanceTestData);
        Console.WriteLine($"Source generation JSON:\n{json}");

        // Deserialization using source generation
        PerformanceTestData? deserialized = JsonSerializer.Deserialize(json, MyJsonContext.Default.PerformanceTestData);
        Console.WriteLine($"Source generation deserialize: {deserialized?.Name}");

        // Source generation with lists
        var dataList = new List<PerformanceTestData> { testData };
        string listJson = JsonSerializer.Serialize(dataList, MyJsonContext.Default.ListPerformanceTestData);
        List<PerformanceTestData>? deserializedList = JsonSerializer.Deserialize(listJson, MyJsonContext.Default.ListPerformanceTestData);
        Console.WriteLine($"Source generation list: {deserializedList?.Count} items");
    }

    // Performance comparison
    public static async Task PerformanceBenchmark()
    {
        const int iterations = 100_000;
        var testData = GenerateTestData(1000);

        // Normal serialization
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            _ = JsonSerializer.Serialize(testData);
        }
        stopwatch.Stop();
        long normalTime = stopwatch.ElapsedMilliseconds;

        // Source generation serialization
        stopwatch.Restart();
        for (int i = 0; i < iterations; i++)
        {
            _ = JsonSerializer.Serialize(testData, MyJsonContext.Default.ListPerformanceTestData);
        }
        stopwatch.Stop();
        long sourceGenTime = stopwatch.ElapsedMilliseconds;

        Console.WriteLine($"Normal serialization: {normalTime}ms");
        Console.WriteLine($"Source generation: {sourceGenTime}ms");
        Console.WriteLine($"Performance improvement: {(double)normalTime / sourceGenTime:F2}x");

        // Memory usage measurement
        long memoryBefore = GC.GetTotalMemory(true);
        
        for (int i = 0; i < 10_000; i++)
        {
            _ = JsonSerializer.Serialize(testData, MyJsonContext.Default.ListPerformanceTestData);
        }
        
        long memoryAfter = GC.GetTotalMemory(false);
        Console.WriteLine($"Memory usage: {(memoryAfter - memoryBefore) / 1024 / 1024} MB");
    }

    private static List<PerformanceTestData> GenerateTestData(int count)
    {
        var data = new List<PerformanceTestData>();
        for (int i = 0; i < count; i++)
        {
            data.Add(new PerformanceTestData
            {
                Id = i,
                Name = $"Item {i}",
                Timestamp = DateTime.UtcNow.AddSeconds(i),
                Value = i * 1.5,
                Tags = new List<string> { $"tag{i % 3}", $"category{i % 5}" },
                Metadata = new Dictionary<string, object>
                {
                    ["index"] = i,
                    ["active"] = i % 2 == 0
                }
            });
        }
        return data;
    }
}

Streaming and Utf8JsonReader Utilization

using System.Text.Json;
using System.Buffers;

public class StreamingJsonExample
{
    // Streaming read of large JSON files
    public static async Task<List<User>> ReadLargeJsonFileAsync(string filePath)
    {
        var users = new List<User>();

        await using var fileStream = File.OpenRead(filePath);
        using var jsonDoc = await JsonDocument.ParseAsync(fileStream);

        if (jsonDoc.RootElement.TryGetProperty("users", out JsonElement usersElement) && 
            usersElement.ValueKind == JsonValueKind.Array)
        {
            foreach (JsonElement userElement in usersElement.EnumerateArray())
            {
                var user = JsonSerializer.Deserialize<User>(userElement.GetRawText());
                if (user != null)
                {
                    users.Add(user);
                }
            }
        }

        return users;
    }

    // High-performance reading with Utf8JsonReader
    public static List<string> ExtractNamesWithUtf8Reader(ReadOnlySpan<byte> jsonData)
    {
        var names = new List<string>();
        var reader = new Utf8JsonReader(jsonData);

        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.PropertyName)
            {
                string propertyName = reader.GetString() ?? string.Empty;
                if (propertyName.Equals("name", StringComparison.OrdinalIgnoreCase))
                {
                    if (reader.Read() && reader.TokenType == JsonTokenType.String)
                    {
                        string? name = reader.GetString();
                        if (name != null)
                        {
                            names.Add(name);
                        }
                    }
                }
            }
        }

        return names;
    }

    // High-performance writing with Utf8JsonWriter
    public static byte[] CreateJsonWithUtf8Writer(IEnumerable<User> users)
    {
        using var buffer = new ArrayBufferWriter<byte>();
        using var writer = new Utf8JsonWriter(buffer, new JsonWriterOptions { Indented = true });

        writer.WriteStartObject();
        writer.WriteStartArray("users");

        foreach (var user in users)
        {
            writer.WriteStartObject();
            writer.WriteNumber("id", user.Id);
            writer.WriteString("name", user.Name);
            writer.WriteString("email", user.Email);
            writer.WriteBoolean("isActive", user.IsActive);
            writer.WriteString("createdAt", user.CreatedAt.ToString("O"));
            
            writer.WriteStartArray("tags");
            foreach (var tag in user.Tags)
            {
                writer.WriteStringValue(tag);
            }
            writer.WriteEndArray();
            
            writer.WriteEndObject();
        }

        writer.WriteEndArray();
        writer.WriteEndObject();

        return buffer.WrittenSpan.ToArray();
    }

    // Asynchronous streaming processing
    public static async IAsyncEnumerable<User> ProcessUsersStreamAsync(Stream jsonStream)
    {
        using var jsonDoc = await JsonDocument.ParseAsync(jsonStream);
        
        if (jsonDoc.RootElement.TryGetProperty("users", out JsonElement usersElement))
        {
            foreach (JsonElement userElement in usersElement.EnumerateArray())
            {
                var user = JsonSerializer.Deserialize<User>(userElement.GetRawText());
                if (user != null)
                {
                    // Simulate asynchronous processing
                    await Task.Delay(1);
                    yield return user;
                }
            }
        }
    }

    public static async Task DemonstrateStreamingOperations()
    {
        // Generate test data
        var users = Enumerable.Range(1, 10000)
            .Select(i => new User
            {
                Id = i,
                Name = $"User {i}",
                Email = $"user{i}@example.com",
                IsActive = i % 10 != 0,
                CreatedAt = DateTime.UtcNow.AddDays(-i),
                Tags = new List<string> { $"tag{i % 5}", $"category{i % 3}" }
            })
            .ToList();

        // High-speed writing with Utf8JsonWriter
        byte[] jsonBytes = CreateJsonWithUtf8Writer(users);
        Console.WriteLine($"Generated JSON size: {jsonBytes.Length / 1024} KB");

        // High-speed reading with Utf8JsonReader
        var extractedNames = ExtractNamesWithUtf8Reader(jsonBytes);
        Console.WriteLine($"Extracted name count: {extractedNames.Count}");

        // Asynchronous streaming processing
        using var memoryStream = new MemoryStream(jsonBytes);
        int processedCount = 0;
        
        await foreach (var user in ProcessUsersStreamAsync(memoryStream))
        {
            processedCount++;
            if (processedCount % 1000 == 0)
            {
                Console.WriteLine($"Processed: {processedCount} users");
            }
        }

        Console.WriteLine($"Total processed: {processedCount}");
    }
}

Error Handling and Best Practices

using System.Text.Json;

public class ErrorHandlingExamples
{
    public static void ComprehensiveErrorHandling()
    {
        // 1. Basic error handling
        try
        {
            string invalidJson = "{\"name\": John Doe, \"age\": }"; // Invalid JSON
            _ = JsonSerializer.Deserialize<User>(invalidJson);
        }
        catch (JsonException ex)
        {
            Console.WriteLine($"JSON format error: {ex.Message}");
            Console.WriteLine($"Position: Line {ex.LineNumber}, Position {ex.BytePositionInLine}");
        }

        // 2. Type conversion error handling
        try
        {
            string typeError = """{"id": "not-a-number", "name": "Test"}""";
            _ = JsonSerializer.Deserialize<User>(typeError);
        }
        catch (JsonException ex)
        {
            Console.WriteLine($"Type conversion error: {ex.Message}");
        }

        // 3. Safe deserialization
        static T? SafeDeserialize<T>(string json) where T : class
        {
            try
            {
                return JsonSerializer.Deserialize<T>(json);
            }
            catch (JsonException ex)
            {
                Console.WriteLine($"Deserialization failed: {ex.Message}");
                return null;
            }
            catch (ArgumentNullException)
            {
                Console.WriteLine("JSON is null");
                return null;
            }
            catch (NotSupportedException ex)
            {
                Console.WriteLine($"Unsupported operation: {ex.Message}");
                return null;
            }
        }

        // 4. Partial JSON parsing
        static Dictionary<string, object?> ParsePartialJson(string json)
        {
            var result = new Dictionary<string, object?>();
            
            try
            {
                using var doc = JsonDocument.Parse(json);
                foreach (var property in doc.RootElement.EnumerateObject())
                {
                    result[property.Name] = property.Value.ValueKind switch
                    {
                        JsonValueKind.String => property.Value.GetString(),
                        JsonValueKind.Number => property.Value.GetDouble(),
                        JsonValueKind.True => true,
                        JsonValueKind.False => false,
                        JsonValueKind.Null => null,
                        _ => property.Value.ToString()
                    };
                }
            }
            catch (JsonException ex)
            {
                Console.WriteLine($"Error during partial parsing: {ex.Message}");
            }
            
            return result;
        }

        // 5. Custom error handling
        var options = new JsonSerializerOptions
        {
            // Skip unknown properties
            UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
            
            // Allow default values
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
            
            // Case-insensitive
            PropertyNameCaseInsensitive = true,
            
            // Allow reading numbers from strings
            NumberHandling = JsonNumberHandling.AllowReadingFromString
        };

        // Robust deserialization example
        string robustJson = """
            {
                "ID": "123", // Number as string
                "Name": "Robust User",
                "email": "[email protected]", // Lowercase
                "IsActive": true,
                "UnknownField": "This will be ignored", // Unknown field
                "createdAt": "2025-01-01T12:00:00Z"
            }
            """;

        User? robustUser = SafeDeserialize<User>(robustJson);
        if (robustUser != null)
        {
            Console.WriteLine($"Robust parsing success: {robustUser.Name}");
        }

        // 6. Performance monitoring
        static async Task<T?> DeserializeWithMetrics<T>(string json) where T : class
        {
            var stopwatch = Stopwatch.StartNew();
            long memoryBefore = GC.GetTotalMemory(false);
            
            try
            {
                T? result = JsonSerializer.Deserialize<T>(json);
                stopwatch.Stop();
                
                long memoryAfter = GC.GetTotalMemory(false);
                long memoryUsed = memoryAfter - memoryBefore;
                
                Console.WriteLine($"Deserialization time: {stopwatch.ElapsedMilliseconds}ms");
                Console.WriteLine($"Memory usage: {memoryUsed} bytes");
                
                return result;
            }
            catch (Exception ex)
            {
                stopwatch.Stop();
                Console.WriteLine($"Error (elapsed time: {stopwatch.ElapsedMilliseconds}ms): {ex.Message}");
                return null;
            }
        }

        // Usage example
        string testJson = JsonSerializer.Serialize(new User 
        { 
            Id = 1, 
            Name = "Metrics Test", 
            Email = "[email protected]", 
            IsActive = true, 
            CreatedAt = DateTime.Now 
        });
        
        _ = DeserializeWithMetrics<User>(testJson);
    }
}