C# – Case sensitivity in JSON deserialization

By default Newtonsoft does case insensitive JSON deserialization and System.Text.Json does case sensitive JSON deserialization.

Case sensitivity comes into play when a JSON string is being deserialized into an object. If you’re using case sensitive deserialization, then keys in the JSON string must match type names exactly, otherwise it won’t deserialize the class/property with the mismatching casing.

This can be confusing if you’re switching from Newtonsoft to System.Text.Json, because a property with mismatched casing will not be matched and it’ll be null.

The following table shows a comparison of how case sensitivity is handled in Newtonsoft vs System.Text.Json.

Case sensitive deserializationCase insensitive deserialization
NewtonsoftWrite a custom converterDefault
System.Text.JsonDefaultPass in an option to make it case insensitive

In this article I’ll show how to do case sensitive and case insensitive deserialization using both Newtonsoft and System.Text.Json.

Example JSON with mismatching casing

I am using the following JSON:

{
  "city": "Detroit",
  "Name": "Lions",
  "Conference": "NFC",
  "Division": "North"
}
Code language: JSON / JSON with Comments (json)

I want to deserialize this JSON into an NFLTeam object:

var detroitLions = new NFLTeam()
{
	City = "Detroit",
	Name = "Lions",
	Conference = Conference.NFC,
	Division = Division.North,
};
Code language: C# (cs)

Notice that the “city” key in the JSON string doesn’t match the casing of the “City” property in the NFLTeam class.

Case insensitive deserialization – using System.Text.Json

System.Text.Json supports case insensitive deserialization simply by using the PropertyNameCaseInsensitive setting, like this.

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

var json = "{\"city\": \"Detroit\",\"Name\": \"Lions\",\"Conference\": \"NFC\",\"Division\": \"North\"}";

var options = new JsonSerializerOptions();
options.PropertyNameCaseInsensitive = true;
options.Converters.Add(new JsonStringEnumConverter());

var team = JsonSerializer.Deserialize<NFLTeam>(json, options);

Console.WriteLine($"Team.City={team.City}");
Code language: C# (cs)

Output:

Team.City=DetroitCode language: plaintext (plaintext)

Case insensitive deserialization – using Newtonsoft

Newtonsoft uses case insensitive deserialization by default, so this is easy:

using Newtonsoft.Json;

var json = "{\"city\": \"Detroit\",\"Name\": \"Lions\",\"Conference\": \"NFC\",\"Division\": \"North\"}";

var team = JsonConvert.DeserializeObject<NFLTeam>(json);

Console.WriteLine($"Team.City={team.City}");
Code language: C# (cs)

Output:

Team.City=DetroitCode language: plaintext (plaintext)

Case sensitive deserialization – using System.Text.Json

System.Text.Json does case sensitive deserialization by default.

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

var json = "{\"city\": \"Detroit\",\"Name\": \"Lions\",\"Conference\": \"NFC\",\"Division\": \"North\"}";

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

var team = JsonSerializer.Deserialize<NFLTeam>(json, options);

Console.WriteLine($"Team.City={team.City}");
Code language: C# (cs)

Output:

Team.City=Code language: plaintext (plaintext)

It’s null because the “city” key does not match the “City” property name, therefore it skips this property and it’s left null.

Case sensitive deserialization – using Newtonsoft

Case insensitive matching is hardcoded in Newtonsoft, and making this configurable has been an open request since 2016. I think it’s safe to say this won’t be changed any time soon.

If you require case sensitive deserialization, then I suggest using System.Text.Json since it’s already supported. However, if you must continue using Newtonsoft, then here are a few customization options.

Assuming you want this to work like System.Text.Json – where it simply ignores properties with mismatching casing – then you have two options:

  • Write a custom JSON converter that filters out properties with mismatching casing.
  • Fork Newtonsoft and change the hardcoded case insensitive matching.

Option 1 – Write a custom converter that ignores properties with mismatching casing

To write a custom converter, you need to inherit from JsonConverter and then override three methods – CanConvert(), ReadJson(), and WriteJson(). The main focus of this custom converter is in ReadJson() – where deserialization happens.

The following custom converter filters out properties with mismatching casing.

Note: This is not recursive, so it only works on top-level properties. You can use this as a starting point, and add recursion for more complex cases if you need to.

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

public class CaseSensitiveDeserializer : 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);
	}
}
Code language: C# (cs)

Now use the custom converter by passing it in during deserialization:

using Newtonsoft.Json;

var json = "{\"city\": \"Detroit\",\"Name\": \"Lions\",\"Conference\": \"AFC\",\"Division\": \"North\"}";

var team = JsonConvert.DeserializeObject<NFLTeam>(json, new CaseSensitiveDeserializer());

Console.WriteLine($"Team.City={team.City}");
Code language: C# (cs)

Output:

Team.City=Code language: plaintext (plaintext)

The “city” key is skipped, because there’s no matching property, therefore the City property is left null.

Option 2 – Fork the Newtonsoft repository and change it to do case sensitive matching

Newtonsoft is open source. Therefore you can fork the Newtonsoft repository from GitHub, and make the specific change you need.

In this case, you’ll need to change the method that hardcodes it to do case sensitive matching. The method you’ll want to change is JsonPropertyCollection.GetClosestMatchProperty().

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

	return property;
}
Code language: C# (cs)

After you’ve made this change, you’ll need to deal with generating a nuget package, and updating your own project to use this package.

2 thoughts on “C# – Case sensitivity in JSON deserialization”

  1. Just a heads up, there’s a typo in the first paragraph under “Option 1 – Write a custom converter […]”.
    You wrote that one of the three methods that needs to be overridden is “CanConverter()” Gotta admit, it’s a funny name. :p

    Reply

Leave a Comment