2

This question is related to this question. I managed to get one step further, but I am now unable to initialize my whole object with default values in order to prevent it from being null at list level. The goal of this is to hand down the "null" values to my SQL query. Ultimately what I want is one record in my DB that will express: This row has been recorded, but the related values were "null".

I have tried Brian's fiddle and it does not seem to work for me to initialize the whole model with standard values.

Expectation: Upon object initialisation the "null" values should be used and then overwritten in case there is a value coming through JSON deserialisation.

Here is what I have tried. None of this will have the desired effect. I receive this error:

Application_Error: System.ArgumentNullException: Value cannot be null.

Every time I try to access one of the lists in the data model.

namespace Project.MyJob
{
 public class JsonModel
 {
    public JsonModel()
    {
        Type_X type_x = new Type_X(); // This works fine.
        List<Actions> action = new List<Actions>(); // This is never visible 
        /*in my object either before I initialise JObject or after. So whatever 
        gets initialised here never makes it to my object. Only Type_X appears 
        to be working as expected. */
        action.Add(new Actions {number = "null", time = "null", station = 
        "null", unitState = "null"}) // This also does not prevent my 
        //JsonModel object from being null.
    }
    public string objectNumber { get; set; }
    public string objectFamily { get; set; }
    public string objectOrder { get; set; }
    public string location { get; set; }
    public string place { get; set; }
    public string inventionTime { get; set; }
    public string lastUpdate { get; set; }
    public string condition { get; set; }

    public Type_X Type_X { get; set; }
    public List<Actions> actions { get; set; }         
 }

 public class Actions
 {
    public Actions()
    {
      // None of this seems to play a role at inititialisation.
      count = "null";
      datetime = "null";
      place = "null";
      status = "null";
    }

    // public string count { get; set; } = "null"; should be the same as above 
      // but also does not do anything.
    public string count { get; set; }
    public string datetime { get; set; }
    public string place { get; set; }
    public string status { get; set; }
 }

 public class Type_X
 {  
    public Type_X
    {
      partA = "null"; // This works.
    }

    public string partA { get; set; }

    public string PartB { get; set; }

    public string partC { get; set; }

    public string partD { get; set; }

    public string partE { get; set; }
  }
}

This is how I now initialize the object based on Brian's answer.

JObject = JsonConvert.DeserializeObject< JsonModel >(json.ToString(), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore});

When I try to iterate over Actions' content, it (logically) gives me above mentioned null error.

for (int i = 0, len = JObject.actions.Count(); i < len; i++)

My current understanding of constructor initialisations:

  • If I define values such as count = "null"; they should appear in any new object that is created.
  • If default values are present I would then also expect that a list that has items with default values (such as count for ex.) would be of Count() 1 and not null. How is that even possible?
Rickless
  • 1,377
  • 3
  • 17
  • 36
Just_Stacking
  • 395
  • 3
  • 13
  • In JsonModel's constructor, you're doing: `List action = new List(); ` - did you mean `this.actions = new List();` – ProgrammingLlama Dec 25 '17 at 05:37
  • It must be this ^ [fiddle](https://dotnetfiddle.net/IIuqje) – ProgrammingLlama Dec 25 '17 at 05:54
  • @John: Thanks, but I already tried "Objects = new List(); Objects.Add(new TestModel() { Name = "default" });" before as mentioned in initial post. Joshua's post got me one step further, but some variables are still null. – Just_Stacking Dec 25 '17 at 06:39
  • Please can you provide a fiddle that demonstrates the issue? – ProgrammingLlama Dec 25 '17 at 06:57
  • Accepted @Joshua's answer. Thanks. I found another problem in my code, where I initialised another model and apparently that threw .net off its rails, even though I did my JSON object deserialisation straight after. – Just_Stacking Dec 25 '17 at 09:08

2 Answers2

3

This will get you out of your bind:

private List<Actions> _actions = new List<Actions>();
public List<Actions> actions { get => _actions; set => _actions = value ?? _actions; }

This causes trying to set actions to null to set it to the previous value, and it is initially not null so it can never be null.

I'm not absolutely sure I'm reading your question right, so here's the same fragment for partA:

private string _partA = "null";
public string partA { get => _partA; set => _partA = value ?? _partA; }
Joshua
  • 40,822
  • 8
  • 72
  • 132
  • The `NullReferenceHandling` in the settings should stop it trying to set null anyway. I think it's because he's assigning the new list to a local variable in the `JsonModel` constructor. I'm uncertain if this issue was introduced when he created his SO question, however. – ProgrammingLlama Dec 25 '17 at 05:39
  • @Joshua: Thanks for that! I just tried it and interestingly it seems to work for the lists (I only need the first part not the _partA section) but **NOT** for any of the items like objectNumber or objectFamily. Any ideas? – Just_Stacking Dec 25 '17 at 06:33
0

I have found that in some cases, initializing generic lists with their default constructor on your model increases ease of use. Otherwise you will always want to validate they are not null before applying any logic(even something as simple as checking list length). Especially if the entity is being hydrated outside of user code, i.e. database, webapi, etc...

One option is to break up your initialization into two parts. Part 1 being the basic initialization via default constructor, and part 2 being the rest of your hydration logic:

JObject = new List < YourModel >();

... < deserialization code here >

Alternatively you could do this in your deserialization code, but it would add a bit of complexity. Following this approach will allow you to clean up your code in other areas since each access will not need to be immediately proceeded by a null check.

iHazCode
  • 622
  • 4
  • 15
  • I found factory methods are easier to read when doing two stage initialization. I don't understand quite a bit of your answer so I can't upvote it. – Joshua Dec 25 '17 at 15:31