By reading the announcements and documentation regarding .NET 8 serialization I was under the impression that we could serialize into private fields.
I was trying to do the following:
public class User
{
[JsonInclude]
[JsonPropertyName("name")]
private string _name = null!;
public int Age { get; }
[JsonConstructor]
private User(string name, int age)
{
_name = name;
Age = age;
}
private User(){}
}
...
var userStr = @"{""name"": ""juan"", ""age"": 31}";
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
IncludeFields = true,
IgnoreReadOnlyFields = false
};
var user = JsonSerializer.Deserialize<User>(userStr, options)!;
Which throws
Unhandled exception. System.InvalidOperationException: Each parameter in the deserialization constructor on type 'User' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. Fields are only considered when 'JsonSerializerOptions.IncludeFields' is enabled. The match can be case-insensitive.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ConstructorParameterIncompleteBinding(Type parentType)...
Changing the private field to a property which exposes a getter like
public string Name { get; } works, but the thing is I wanted it to be a private field. In my real scenario I have a private List<T> but i just expose a public IReadOnlyCollection<T> whose getter returns the list as readonly.
Is there a way to tell the serialization to use the private field?
The problem is not with properties/fields but with constructor parameter names, they should match corresponding properties/fields names. Change ctor to:
[JsonConstructor]
private User(string _name, int age)
{
this._name = _name;
Age = age;
}
From the Use immutable types and properties: Parameterized constructors doc:
The parameter names of a parameterized constructor must match the property names and types. Matching is case-insensitive, and the constructor parameter must match the actual property name even if you use
[JsonPropertyName]to rename a property.
Note that adding the JsonIncludeAttribute should be enough. If you can make your Age property init-only then you can completely skip the constructor:
public class User1
{
[JsonInclude]
[JsonPropertyName("name")]
private string _name = null!;
public int Age { get; init; }
public override string ToString() => $"{_name}: {Age}";
}
var userStr = @"{""name"": ""juan"", ""age"": 31}";
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
var user = JsonSerializer.Deserialize<User1>(userStr, options)!;
Console.WriteLine(user); // prints "juan: 31"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With