Reputation: 770
I have upgraded my project to netcore 3.0 and I am in the middle of refactoring a project to use the new nullable references types feature, but got stuck pretty quickly because of the following issue.
Lets say I consume a REST api which returns the following JSON:
{
"Name": "Volvo 240",
"Year": 1989
}
This api always returns the name/year, so they are non-nullable.
I would use this simple class for deserialization:
public class Car
{
public string Name {get; set;}
public int Year {get; set;}
}
And I would deserialize this to a Car
instance using the new System.Text.Json
var car = JsonSerializer.Deserialize<Car>(json);
This all works, but when enabling nullable reference types I get a warning in the Car
class that Name
is declared as non-nullable but can be null. I understand why I get this since it is possible to instantiate this object without initializing the Name
property.
So ideally Car
should look like this:
public class Car
{
public string Name { get; }
public int Year { get; }
public Car(string name, int year)
{
Name = name;
Year = year;
}
}
But this doesn't work because System.Text.Json
serializer doesn't support constructors with parameters.
So my question is: How would I declare Car
so that Name
is non-nullable and get it to work with System.Text.Json
without getting "non-nullable" warning?`
I don't want to make it nullable because I would have to do null-checks on basically everything when enabling nullable reference types, and since the REST API in my example says that they are always provided they shouldn't be nullable.
Upvotes: 27
Views: 13197
Reputation: 1378
Update
If you're on net5
, use the parameterized constructor support now offer as @langen points out. Else below can still be useful.
Original
Slightly alternative approach. System.Text.Json
appears to have no problems using a private parameterless constructor. So you can at least do the following:
public class Car
{
public string Name { get; set; }
public int Year { get; set; }
// For System.Text.Json deserialization only
#pragma warning disable CS8618 // Non-nullable field is uninitialized.
private Car() { }
#pragma warning restore CS8618
public Car(string name, int year)
{
Name = name
?? throw new ArgumentNullException(nameof(name));
Year = year;
}
}
Benefits being:
= null!;
on each property.Remaining downside with S.T.Json and nullable reference types:
Upvotes: 2
Reputation: 985
Another option, for those who want to handle missing properties with meaningful exceptions:
using System;
public class Car
{
private string? name;
private int? year;
public string Name
{
get => this.name ?? throw new InvalidOperationException($"{nameof(this.Name)} was not set.");
set => this.name = value;
}
public int Year
{
get => this.year ?? throw new InvalidOperationException($"{nameof(this.Year)} was not set.");
set => this.year = value;
}
}
Upvotes: 0
Reputation: 770
UPDATE
System.Text.Json
for .NET 5 now supports parameterized constructors, so this should not be a problem anymore.
Old answer below
After reading the msdocs I found out how I could solve this issue.
So until System.Text.Json
cannot instantiate classes with parameters in their constructor, the Car
class will have to look like this:
public class Car
{
public string Name { get; set; } = default!;
public int Year { get; set; }
}
Upvotes: 7