Custom Property Mapping with Newtonsoft.Json: Solving Naming Mismatches in JSON Deserialization

Nov 21, 2025 · Programming · 16 views · 7.8

Keywords: JSON Deserialization | Property Mapping | Newtonsoft.Json

Abstract: This article explores how to resolve property name mismatches during JSON deserialization in .NET using the Newtonsoft.Json library. Through practical examples, it demonstrates mapping JSON data from external APIs to custom-named C# classes, including class renaming and property name standardization. The article compares alternative mapping approaches and provides complete code samples with best practices.

Problem Background and Challenges

In modern software development, data exchange with external systems is common, and JSON is widely used as a lightweight data interchange format. However, when the JSON structure returned by external systems doesn't match internal class designs, deserialization challenges arise. Specifically, when JSON property names don't match C# property names, using JsonConvert.DeserializeObject directly results in null property values because default naming mapping rules cannot correctly match them.

Core Solution: JsonPropertyAttribute

The Newtonsoft.Json library provides the JsonPropertyAttribute to explicitly define mapping relationships between JSON properties and C# properties. By applying this attribute to target properties and setting its PropertyName parameter to the actual JSON property name, custom mapping is achieved.

Below is the refactored code based on the problem scenario:

public class TeamScore
{
    [JsonProperty("eighty_min_score")]
    public string EightyMinScore { get; set; }
    
    [JsonProperty("home_or_away")]
    public string HomeOrAway { get; set; }
    
    [JsonProperty("score")]
    public string Score { get; set; }
    
    [JsonProperty("team_id")]
    public string TeamId { get; set; }
}

public class Team
{
    public string v1 { get; set; }
    
    [JsonProperty("attributes")]
    public TeamScore TeamScores { get; set; }
}

public class RootObject
{
    public List<Team> Team { get; set; }
}

In this code:

Deserialization Execution

With the modified class structure, the deserialization operation remains unchanged:

RootObject root = JsonConvert.DeserializeObject<RootObject>(jsonText);

Now, root.Team[0].TeamScores.Score correctly returns "22", and root.Team[1].TeamScores.Score returns "30", achieving the expected data mapping.

Alternative Approach: Custom ContractResolver

Although JsonPropertyAttribute is the recommended solution, in scenarios where avoiding extensive attribute annotations in model classes is desired, a custom ContractResolver can be considered. This method dynamically maps property names by overriding the ResolvePropertyName method.

Example code:

public class CustomContractResolver : DefaultContractResolver
{
    private Dictionary<string, string> PropertyMappings { get; set; }

    public CustomContractResolver()
    {
        this.PropertyMappings = new Dictionary<string, string> 
        {
            {"TeamScores", "attributes"},
            {"EightyMinScore", "eighty_min_score"},
            {"HomeOrAway", "home_or_away"},
            {"Score", "score"},
            {"TeamId", "team_id"}
        };
    }

    protected override string ResolvePropertyName(string propertyName)
    {
        string resolvedName = null;
        var resolved = this.PropertyMappings.TryGetValue(propertyName, out resolvedName);
        return resolved ? resolvedName : base.ResolvePropertyName(propertyName);
    }
}

Usage:

var settings = new JsonSerializerSettings
{
    ContractResolver = new CustomContractResolver()
};
RootObject root = JsonConvert.DeserializeObject<RootObject>(jsonText, settings);

Comparison with System.Text.Json

In .NET Core 3.0 and later, Microsoft introduced System.Text.Json as the official JSON processing library. While this article focuses on Newtonsoft.Json, understanding its alternative helps comprehensively grasp the .NET ecosystem.

System.Text.Json uses JsonPropertyNameAttribute for similar functionality:

public class TeamScore
{
    [JsonPropertyName("eighty_min_score")]
    public string EightyMinScore { get; set; }
    
    // Other properties similarly
}

Key differences include:

Best Practices and Considerations

Following these best practices in actual development can avoid common issues:

  1. Consistency Principle: Unify naming conventions within the team to reduce mapping needs.
  2. Attribute Override Priority: JsonPropertyAttribute overrides any global naming policy, ensuring local configuration takes precedence.
  3. Performance Considerations: For high-performance scenarios, System.Text.Json may be a better choice; for complex mappings, Newtonsoft.Json offers more flexibility.
  4. Error Handling: Include exception handling during deserialization to address JSON format errors or mapping failures.

Conclusion

Using JsonPropertyAttribute, developers can flexibly handle mismatches between JSON property names and C# property names, achieving clean data model design. Although custom ContractResolver provides an alternative solution, attribute annotation is generally more intuitive and maintainable. As the .NET ecosystem evolves, System.Text.Json is becoming a viable option, but Newtonsoft.Json remains important in existing projects due to its maturity and feature richness.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.