I am trying to use the JsonPropertyName in combination with the JsonConstuctor attributes from System.Text.Json to add json serialization to an existing library.
Each type has a constructor which mostly matches the readonly fields (and/or get only properties). The issue is that the names don't always match and I don't want to rename properties/arguments on public members.
So a class/struct may look like:
public struct Person
{
public Person(string Name)
{
FirstName = Name
}
public string FirstName { get; }
}
I would have thought this would have worked:
public struct Person
{
[JsonConstructor]
public Person(string Name)
{
FirstName = Name
}
[JsonPropertyName("Name")]
public string FirstName { get; }
}
the json output looks correct
{
"Name": "Joe"
}
But on deserialization the following error is thrown.
'Each parameter in the deserialization constructor on type 'Person' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. The match can be case-insensitive.'
As i write this i am now thinking that the [JsonConstructor] attribute is not forcing the deserializer to call the constructor and matching json keys with argument names but rather using the names from the constructor to set only those properties which match in name. In any case is there are way without modifying the Person struct (ideally using attributes) to support System.Text.Json serialization?
CodePudding user response:
I have never seen System.Text.Json.JsonConstructor working properly, when I see even one example that is working I will let you know. So I recommend you to send an email to Microsoft to thank them for an amazing parser and use Newtonsoft.Json
using Newtonsoft.Json;
Person person =Newtonsoft.Json.JsonConvert.DeserializeObject<Person>(json);
public struct Person
{
[Newtonsoft.Json.JsonConstructor]
public Person(string Name)
{
FirstName = Name;
}
[JsonProperty("Name")]
public string FirstName { get; }
}
CodePudding user response:
If you are able to change the existing library, then using C# 9 you can add a private init;
and do the following:
public struct Person
{
public Person(string Name)
{
this.FirstName = Name;
}
[JsonInclude]
[JsonPropertyName("Name")]
public string FirstName { get; private init; }
}
You need the [JsonInclude]
to allow the deserializer to use the private init
.
This won't change the public api of the struct, which it seems from your question you might be okay with.
Prior to C# 9 you can use private set;
but this opens the api internally a little more.