Migrating from Newtonsoft.Json to System.Text.Json in .NET 8

Discover how migrating from Newtonsoft.Json to System.Text.Json in .NET 8 can boost performance and security. Learn essential System.Text.Json tips!

amol ankit · 3 min read

Today we are talking about Migrating from Newtonsoft.Json to System.Text.Json in .NET 8 can improve performance, reduce dependencies, and enhance security. However, the transition requires careful handling due to differences between the two libraries. This guide walks you through the key differences, migration steps, challenges, and solutions to help ensure a smooth migration.

Migrating from Newtonsoft.Json?

Before we dive into migration steps, let's briefly explore why you should consider moving to System.Text.Json:

  • Performance: System.Text.Json is optimized for speed and memory efficiency.

  • Security: It offers stricter character escaping to prevent injection attacks.

  • Built-in Support: System.Text.Json is the default serializer in ASP.NET Core and .NET 8.

  • Reduced Dependencies: Unlike Newtonsoft.Json, no external package installation is required.

Key Differences Between Newtonsoft.Json and System.Text.Json

1. Case Sensitivity

  • System.Text.Json is case-sensitive by default, whereas Newtonsoft.Json is case-insensitive.

  • Solution: Enable case-insensitive deserialization using PropertyNameCaseInsensitive = true.

2. Property Naming Policy

  • System.Text.Json uses camelCase by default.

  • Solution: Explicitly define property names using \[JsonPropertyName\] attributes.

3. Character Escaping

  • System.Text.Json escapes characters differently to improve security.

  • Solution: Test your output and update clients if necessary.

4. Comments & Trailing Commas

  • System.Text.Json does not support JSON comments and trailing commas by default.

  • Solution: Enable them with JsonCommentHandling.Skip and AllowTrailingCommas = true.

5. Custom Converters

  • The approach for writing custom converters differs between the two libraries.

Migration Steps

Step 1: Remove Newtonsoft.Json Dependency

If your project references Newtonsoft.Json, remove it:

 dotnet remove package Newtonsoft.Json

Step 2: Update Serialization Attributes

Replace Newtonsoft.Json attributes with System.Text.Json equivalents.

Before (Newtonsoft.Json):

using Newtonsoft.Json;

public class Product
{
    [JsonProperty("product_id")]
    public int Id { get; set; }

    [JsonProperty("product_name")]
    public string Name { get; set; }
}

After (System.Text.Json):

using System.Text.Json.Serialization;

public class Product
{
    [JsonPropertyName("product_id")]
    public int Id { get; set; }

    [JsonPropertyName("product_name")]
    public string Name { get; set; }
}

Step 3: Configure JsonSerializerOptions

Set up serialization options for expected behaviour:

var options = new JsonSerializerOptions
{
   PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
   PropertyNameCaseInsensitive = true,
   WriteIndented = true,
   AllowTrailingCommas = true,
   ReadCommentHandling = JsonCommentHandling.Skip
};

Step 4: Handle Enum Serialization

By default, System.Text.Json serializes enums as integers. To serialize them as strings:

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum UserRole
{
    Administrator,
    StandardUser,
    GuestAccount
}

If enum values differ from their property names, create a custom converter:

public class UserRoleConverter : JsonConverter<UserRole>
{
    public override UserRole Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string value = reader.GetString();
        return value switch
        {
           "administrator" => UserRole.Administrator,
           "standard_user" => UserRole.StandardUser,
           "guest_account" => UserRole.GuestAccount,
            _ => throw new JsonException($"Unknown UserRole: {value}")
        };
    }

    public override void Write(Utf8JsonWriter writer, UserRole value, JsonSerializerOptions options)
    {
       string stringValue = value switch
        {
            UserRole.Administrator => "administrator",
            UserRole.StandardUser => "standard_user",
            UserRole.GuestAccount => "guest_account",
            _ => throw new JsonException($"Unknown UserRole: {value}")
        };
        writer.WriteStringValue(stringValue);
    }
}

Step 5: Implement Custom Converters

For types requiring special handling, implement a custom converter.

Example: Custom DateTime Converter

public class DateTimeConverter : JsonConverter<DateTime>
{
    private const string Format = "yyyy-MM-dd";

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateTime.ParseExact(reader.GetString(), Format, CultureInfo.InvariantCulture);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(Format));
    }
}

Register the converter:

var options = new JsonSerializerOptions();
options.Converters.Add(new DateTimeConverter());

Step 6: Test Thoroughly

After migration, test your application thoroughly to ensure serialization and deserialization work as expected.

Common Challenges and Solutions

Challenge Solution
Case sensitivity issues Enable PropertyNameCaseInsensitive = true
Enum serialization differences Use JsonStringEnumConverter or a custom converter
Newtonsoft features missing Implement custom converters for missing functionalities
Performance optimization Reuse JsonSerializerOptions instances to improve performance

Pros and Cons of System.Text.Json

✅ Pros

  • Performance: Faster and more memory-efficient than Newtonsoft.Json.

  • Security: Provides better character escaping for XSS protection.

  • Integrated: Comes built into .NET, reducing external dependencies.

❌ Cons

  • Fewer features: Some features in Newtonsoft.Json (e.g., JObject, LINQ to JSON) are not directly available.

  • Stricter parsing: Does not support comments or trailing commas by default.

Final Thoughts

Migrating to System.Text.Json in .NET 8 is a worthwhile effort for performance and security improvements. While it requires careful adjustments, following the outlined steps and addressing common challenges can make the transition smooth. Test thoroughly, implement custom converters where needed, and enjoy the benefits of a more efficient JSON serialization library!

Have you started migrating? Share your experiences and challenges!