0

I have an object with one constructor that has a parameter name that does not match the property name. Because the parameter name doesn't match, Newtonsoft deserializes it to an object with the default value. I get why that happens and I know I can fix it by changing the parameter name or by making the property { get; set; }, but I'm surprised that MissingMemberHandling.Error does not catch the issue. Is there another setting that will cause this to throw an exception?

using Newtonsoft.Json;
// Runnable in LINQPad

void Main()
{
    JsonSerializerSettings settings = new JsonSerializerSettings();
    settings.MissingMemberHandling = MissingMemberHandling.Error;

    // Expected: throw an exception
    var foo = Newtonsoft.Json.JsonConvert.DeserializeObject<Foo>("{a:123,c:456}", settings);

    // Actual: foo.A is 0 and C is 456
    Console.WriteLine(foo);
}


public class Foo
{
    public Foo(int b, int c)
    {
        A = b;
        C = c;
    }
    
    [JsonProperty(Required = Required.Always)]
    public int A { get; }
    public int C { get; }
}
Andrew
  • 1,839
  • 14
  • 25
  • But thats just an argument to your constructor - what are you *really* trying to achieve? Anyone can call their constructor arguments anything. – Ermiya Eskandary Oct 05 '21 at 19:18
  • @ErmiyaEskandary Exactly, anyone can (and does) call their constructor arguments anything. I want to throw an exception if deserialization had to pick a default value in case someone names something in a way that the library doesn't like. That way, we catch the error early and don't have to spend time tracking down a bug. – Andrew Oct 05 '21 at 19:23
  • But your argument is for **you** - nobody else even cares about it other than passing arguments in the right order – Ermiya Eskandary Oct 05 '21 at 19:25
  • I don't understand what you mean. I just finished tracking down a bug caused by this issue and wish Newtonsoft would have warned me it was picking a default value because it couldn't correlate the constructor parameter with the property name. – Andrew Oct 05 '21 at 19:27
  • Serializers almost always use the simple/parameterless constructor mainly because they do not know what those parameters (*not* properties*) do. FGor one thing, using properties as parameters would require 'reading ahead' to get values(s). Properties are then set individually. – Ňɏssa Pøngjǣrdenlarp Oct 05 '21 at 19:27
  • Newtonsoft can and does use a constructor when you have get-only properties. If the parameter name was `a` instead of `b` this would work. – Andrew Oct 05 '21 at 19:29
  • @ŇɏssaPøngjǣrdenlarp I added a bit more to the example to show how it can work correctly. – Andrew Oct 05 '21 at 19:33
  • why bother with a constructor that has no logic? why not eliminate the constructor and the let the MissingMemberHandling enum do its thing? – Jonathan Alfaro Oct 05 '21 at 19:37
  • Because I want an immutable object. Without the constructor I would need `{ get; set; }` properties. – Andrew Oct 05 '21 at 19:41
  • 1
    Looks like a duplicate of [How to throw an exception when JsonConstructor parameter name doesn't match JSON?](https://stackoverflow.com/q/63898984/3744182), agree? That question deals specifically with an unmatched constructor parameter that the OP wants to mark as required. – dbc Oct 05 '21 at 20:52
  • @ŇɏssaPøngjǣrdenlarp - *using properties as parameters would require 'reading ahead' to get values(s).* - this is in fact implemented by Json.NET. When a parameterized constructor is used, Json.NET reads ahead, deserializes all the properties and constructor arguments, then finally constructs the object. See e.g. [How does JSON deserialization in C# work](https://stackoverflow.com/a/41871975/3744182) or [JSON.net: how to deserialize without using the default constructor?](https://stackoverflow.com/q/23017716/3744182). – dbc Oct 05 '21 at 20:56
  • 1
    @dbc I think you're right, that does answer my question. Thanks! – Andrew Oct 05 '21 at 21:13

0 Answers0