Search code examples
c#json.net-core.net-3.0

AspNet 3.1 - Collides with another property : ThrowInvalidOperationException_SerializerPropertyNameConflict


My Error is that the controller is not able to map the value; I have this situation that explains how to replicate the error:

    public class FooA {
      public string Property1 { set; get; }
      public virtual string Property2 { set; get; }
    }
    
    public class FooB : FooA {
      public new string Property2 { set; get; } 
      public string Property3 { set; get; }
    }

As you know, the property Property2 is common for both classes, thereby when you are using in any controller this action:

    public async task<string> ActionA([FromBody] FooA fooA)
    {
      return string.Empty;
    }


    // The error is thrown in this unwrapping.
    public async task<string> ActionB([FromBody] FooB fooB)
    {
      return string.Empty;
    }

The payload for FooA is of the request is:

    {
      "Property1" : "abc",
      "Property2" : "def"
    }

The payload for FooB is of the request is:

    {
      "Property2" : "abc",
      "Property3" : "def"
    }

This Exception will be thrown:

    System.InvalidOperationException: The JSON property name for 'FooB' collides with another property.
       at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(JsonClassInfo jsonClassInfo, JsonPropertyInfo jsonPropertyInfo)
       at System.Text.Json.JsonClassInfo..ctor(Type type, JsonSerializerOptions options)

I have added Attributes such as [JsonIgnore] but it fails with a payload like the first one.

or :

            services.AddControllers()
                .AddJsonOptions(options =>
                    {
                        options.JsonSerializerOptions.PropertyNamingPolicy = null;
                        options.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
                    });

But it has not possible, my idea is to be SOLID and avoid to change the entire solution. It means Open Extensions (it means that extensions will solve future issues ) Closed to (already implemented) changes.

Do you have a particular setting in the AddJsonOptions to allow that conflicts by inheritences will be autoresolved using the child class always?

Notes 01: Even, when it is added the virtual ans new reserver keyword the controller throws the same exceptions.


Solution

  • you have to fix the classes, you have 2 choices

    the common one

    public class FooA
    {
        public string Property1 { set; get; }
        public virtual string Property2 { set; get; }
    }
    
    public class FooB : FooA
    {
        public override string Property2 { set; get; }
        public string Property3 { set; get; }
    }
    

    or if you want to have an access to 2 properties

    public class FooA
    {
        public string Property1 { set; get; }
        public string Property2 { set; get; }
    }
    
    public class FooB : FooA
    {
        public new string Property2 { set; get; }
        public string Property3 { set; get; }
    }
    

    test

    var json = @"{
          ""Property2"" : ""abc"",
          ""Property3"" : ""def""
        }";
    
    var jsonDeserialized=System.Text.Json.JsonSerializer.Deserialize<FooB>(json);
    

    test reslut (in a json format)

    {
      "Property2": "abc",
      "Property3": "def",
      "Property1": null
    }
    

    but I recommend you to install Newtonsoft.Json serializer just config it in startup

    using Newtonsoft.Json.Serialization;
    
        services.AddControllersWithViews()
        .AddNewtonsoftJson(options =>
               options.SerializerSettings.ContractResolver =
                  new CamelCasePropertyNamesContractResolver());
    

    or if you use just controllers

        services.AddControllers()
        .AddNewtonsoftJson(options =>
               options.SerializerSettings.ContractResolver =
                  new CamelCasePropertyNamesContractResolver());