2

Is it possible to adjust JsonSerializerSettings that way?

(I guess not) but still have some hope, cause I'm not very experienced with their API.

By default and with all settings I've tried missing int either deserialized to 0 or causing exception.

Example

   {
      "defaultCaptionLineNum": 6,
      "worksheets": {
        "0": { "name": "Ws1", "caption_ln": 6 },
        "1": { "name": "Ws2", "caption_ln": null },
        "2": { "name": "Ws3" },
        "3": { "name": "Ws4", "caption_ln": 5 },
      }
}

Currently by default caption line (caption_ln) of Ws3 evaluated to 0, I want it to be evaluated to null (as in Ws2).

I'm using

jObject.ToObject<MyClass>(JsonSerializer.Create(settings));

to get object with worksheet(Ws) information from JSON (tried it without serializer as well)

and any variations of Include and Ignore here don't make jObject deserialize missing ints in json differently (with the exception of throwing error right away for missing values)

var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Include,
MissingMemberHandling = MissingMemberHandling.Ignore
};

Other serializer settings seem to be irrelevant.

UPD: Listing of MyObject MyClass.

public class MyClass
{
    public class WorkSheet
    {
        [JsonProperty("name")]
        string wsName;

        [JsonProperty("caption_ln")]
        int? captionLineNumber;

        public WorkSheet(string name, int captionln)
        {
            wsName = name;
            captionLineNumber = captionln;
        }
    }
    private int _defaultCaptionLineNum;

    [JsonProperty("defaultCaptionLineNum")]
    public int DefaultCaptionLineNum { get => _defaultCaptionLineNum; }

    private Dictionary<int, WorkSheet> _worksheets = new Dictionary<int, WorkSheet>();

    [JsonProperty("worksheets")]
    public Dictionary<int, WorkSheet> Worksheets { get => _worksheets; set => _worksheets = value; }

    public MyClass()
    {           
        Console.WriteLine("New MyClass object created!");
    }
}
dbc
  • 104,963
  • 20
  • 228
  • 340
Vsevolod
  • 619
  • 1
  • 7
  • 14
  • 3
    Can you please add the listing for `MyObject`? – zaitsman Sep 04 '18 at 15:20
  • 3
    Is your `caption_ln` defined as an `int` or nullable int (`int?`)? – Ron Beyer Sep 04 '18 at 15:22
  • 1
    If a value is missing Json.Net will do a `default(T)` `default(int)` is `0` `default(int?)` is `null`. It's probably the .Net framework that does this when the class is instanciated but the point is the same – Liam Sep 04 '18 at 15:24
  • @RonBeyer As nullable ```int``` otherwise null in json cause error by default. – Vsevolod Sep 04 '18 at 17:01

1 Answers1

7

Your problem is that your type WorkSheet has only one constructor, which is parameterized:

public class WorkSheet
{
    public WorkSheet(string name, int captionln)
    {
        wsName = name;
        captionLineNumber = captionln;
    }

    // Remainder omitted
}

As explained in the documentation page ConstructorHandling Enumeration, Json.NET will fall back to a single parameterized constructor when there is no parameterless constructor, which is the case here. For the constructor arguments, Json.NET will match the JSON parameters to constructor arguments by name (modulo case), deserializing to the argument type if present or passing a default value if not present. And since there is no "captionln" parameter present in your JSON, captionLineNumber gets initialized to default(int), which is 0. This explains the problem you are seeing.

To fix the problem, you can change captionln to be of type int?:

public class WorkSheet
{
    public WorkSheet(string name, int? captionln)
    {
        wsName = name;
        captionLineNumber = captionln;
    }

    // Remainder omitted
}

Working fiddle #1 here.

Alternatively, you could introduce a private serialization-specific constructor and mark it with [JsonConstructor]:

public class WorkSheet
{
    [JsonConstructor]
    private WorkSheet() { }

    public WorkSheet(string name, int captionln)
    {
        wsName = name;
        captionLineNumber = captionln;
    }

    // Remainder omitted
}

Sample fiddle #2 here.

Related questions on constructor use in Json.NET:

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Thank you so much! I tend to blame technology, while making mistakes myself, of course I needed to make captionln parameter optional in constructor as well, especially as class properties marked private. – Vsevolod Sep 04 '18 at 20:35
  • Interesting side question though: how it's possible for Json.NET to use non-public default constructor? – Vsevolod Sep 04 '18 at 20:43
  • 1
    @SMetana - they use reflection, e.g. as shown in [How to instantiate an object with a private constructor in C#?](https://stackoverflow.com/q/708952). (Though I believe they actually do some run-time code generation behind the scenes to speed things up. See e.g. [`DynamicReflectionDelegateFactory.CreateDefaultConstructor(Type type)`](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs).) – dbc Sep 04 '18 at 20:54