Case sensitivity in JSON deserialization

By FoxLearn 3/21/2025 2:24:24 AM   51
By default, Newtonsoft.Json uses case-insensitive deserialization, while System.Text.Json performs case-sensitive deserialization.

When deserializing JSON strings into objects, case sensitivity can cause issues. Specifically, if you're using case-sensitive deserialization, the property names in your JSON string must match the exact casing of the class properties. If there's a mismatch, the deserialization will fail, and properties with mismatched casing will be left null.

This can be particularly tricky when switching between Newtonsoft.Json and System.Text.Json, as the deserialization behavior differs in terms of case sensitivity.

Here's a table showing the comparison of case sensitivity handling in Newtonsoft.Json and System.Text.Json:

Case SensitivityNewtonsoft.JsonSystem.Text.Json
Case insensitive deserializationDefault behaviorPass in an option to make it case insensitive
Case sensitive deserializationWrite a custom converterDefault behavior

In this article, I'll walk through how to implement both case-sensitive and case-insensitive deserialization with Newtonsoft.Json and System.Text.Json.

Consider this example JSON, where the keys do not match the property names in the Person class:

{
  "firstName": "John",
  "lastName": "Doe",
  "age": 30
}

I want to deserialize this JSON into a Person object, where the property names are FirstName, LastName, and Age:

var person = new Person()
{
    FirstName = "John",
    LastName = "Doe",
    Age = 30
};

Notice that the firstName key in the JSON does not match the casing of the FirstName property in the Person class.

Case Insensitive Deserialization - Using System.Text.Json

System.Text.Json supports case-insensitive deserialization using the PropertyNameCaseInsensitive setting:

using System.Text.Json;

var json = "{\"firstName\": \"John\", \"lastName\": \"Doe\", \"age\": 30}";

var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};

var person = JsonSerializer.Deserialize<Person>(json, options);

Console.WriteLine($"Person.FirstName={person.FirstName}");

Output:

Person.FirstName=John

Case Insensitive Deserialization – Using Newtonsoft.Json

Newtonsoft.Json performs case-insensitive deserialization by default, so this is straightforward:

using Newtonsoft.Json;

var json = "{\"firstName\": \"John\", \"lastName\": \"Doe\", \"age\": 30}";

var person = JsonConvert.DeserializeObject<Person>(json);

Console.WriteLine($"Person.FirstName={person.FirstName}");

Output:

Person.FirstName=John

Case Sensitive Deserialization – Using System.Text.Json

System.Text.Json performs case-sensitive deserialization by default:

using System.Text.Json;

var json = "{\"firstName\": \"John\", \"lastName\": \"Doe\", \"age\": 30}";

var options = new JsonSerializerOptions();

var person = JsonSerializer.Deserialize<Person>(json, options);

Console.WriteLine($"Person.FirstName={person.FirstName}");

Output:

Person.FirstName=

In this case, FirstName remains null because the JSON key firstName does not match the casing of the FirstName property in the Person class.

Case Sensitive Deserialization - Using Newtonsoft.Json

Unlike System.Text.Json, case-insensitive deserialization is hardcoded in Newtonsoft.Json, and making it configurable has been a feature request for years.

Option 1: Write a Custom Converter that Ignores Properties with Mismatched Casing

To achieve case-sensitive deserialization, you can write a custom converter.

Here's an example that filters out properties with mismatched casing:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class CaseSensitivePersonDeserializer : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        JObject target = new JObject();

        foreach(JProperty property in JToken.Load(reader).Children())
        {
            if(objectType.GetProperty(property.Name) != null)
            {
                target.Add(property);
            }
        }

        return target.ToObject(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JObject o = (JObject)JToken.FromObject(value);
        o.WriteTo(writer);
    }
}

Now, use the custom converter during deserialization:

using Newtonsoft.Json;

var json = "{\"firstName\": \"John\", \"lastName\": \"Doe\", \"age\": 30}";

var person = JsonConvert.DeserializeObject<Person>(json, new CaseSensitivePersonDeserializer());

Console.WriteLine($"Person.FirstName={person.FirstName}");

Output:

Person.FirstName=

Since there's no exact match for the FirstName property, it's left null.

Option 2: Modify the Newtonsoft.Json Repository for Case-Sensitive Matching

Newtonsoft.Json is open-source, so you can modify the repository to make it case-sensitive by default. The method you need to alter is JsonPropertyCollection.GetClosestMatchProperty().

public JsonProperty? GetClosestMatchProperty(string propertyName)
{
    JsonProperty? property = GetProperty(propertyName, StringComparison.Ordinal);
    if (property == null)
    {
        property = GetProperty(propertyName, StringComparison.OrdinalIgnoreCase);
    }

    return property;
}

You would need to fork the repository, implement this change, and then generate a custom NuGet package for your project.