Search code examples
c#asp.net-corejson.net

.NET Core - The JSON property name for collides with another property


I'm migrating an old API to .net core web api and one of the responses includes the same value twice, so I'm using the native Json library of .NET 5 and I'm trying to get the same value twice in the JSON response, 'Id' and 'id'

{
...

"Id": "10",

"id": "10"

...
}

In my Startup, ConfigurationServices I configured the Json Option like this:

services.AddControllers().AddJsonOptions(options =>
    { options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; });

My action method

[HttpGet]
public async Task<ActionResult<IEnumerable<object>>> GetContacts(string projectID)
{
    Project project = _context.Projects.Where(a => a.Name == projectID)
        .FirstOrDefault();
    var contacts = await _context.Contacts.Where(a => a.ProjectId == project.Id)
        .Select(o => new { id = o.Id, ID = o.Id}).ToListAsync();
    return contacts;
}

While serializing, I am getting the "The JSON property name for collides with another property." I think I'm missing something, and I'm stuck in this.


Solution

  • According to docs for PropertyNameCaseInsensitive:

    Gets or sets a value that determines whether a property's name uses a case-insensitive comparison during deserialization.

    So this flag is not about serialization and API output formatting. In the example code it is set to true. Hence, during deserialization a JSON property name should be matched with a single property of a target class in a case-insensitive manner. However, there is a clash - there are two candidate properties - Id and id. So it does not make sense.

    Internally it's implemented as a case-insensitive dictionary for property lookup (decompiled .Net 5 by Rider):

    public JsonClassInfo(Type type, JsonSerializerOptions options)
    {
        // ...
        Dictionary<string, JsonPropertyInfo> cache = new Dictionary<string, JsonPropertyInfo>(
            Options.PropertyNameCaseInsensitive
                ? StringComparer.OrdinalIgnoreCase
                : StringComparer.Ordinal);
        // ...
    }
    

    So the solution is to set PropertyNameCaseInsensitive to false and use PropertyNamingPolicy = JsonNamingPolicy.CamelCase (which is the default value and is omitted below):

    public class SomeObject
    {
        [JsonPropertyName("Id")]
        public int Id { get; set; }
       
        public int id { get; set; }
    
        public string SomeString { get; set; }
    }
    

    Startup:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers().AddJsonOptions(options => 
            options.JsonSerializerOptions.PropertyNameCaseInsensitive = false);
        // ...
    }
    

    This should give:

    {
        "Id": 2,
        "id": 3,
        "someString": "..."
    }