Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
May 1, 2021 05:39 am GMT

[C ] Some scenarios for deserializing a JSON to a type with read-only properties by "System.Text.Json"

Recently I feel that using the System.Text.Json library for serializing/deserializing with JSON is increasing in C# programming.

And using immutable object - that has read-only properties, and those properties are initialized in a constructor - is also increasing, I feel too.
For example, like the C# class as below:

class PersonClass{  public string? Name { get; }  public int Age { get; }  public PersonClass(string name, int age)  {    Name = name;    Age = age;  }}

Of course, the Deserialize<T>() static method of the JsonSerializer class in the System.Text.Json library can deserialize a JSON to an immutable object such as the above C# class.

var json = @"{""Name"":""Taro"",""Age"":23}";var person = JsonSerializer.Deserialize<PersonClass>(json);// person.Name -> "Taro"// person.Age -> 23

The Deserialize<T>() static method will detect that the class has the constructor with arguments usable for deserializing.
So by using that constructor, the Deserialize<T>() static method can deserialize a JSON to an immutable object like that.

To the Deserialize<T>() static method can detect the constructor that can use for deserializing, that constructor must have arguments that are the same-named its properties.

If it doesn't that, the exception will throw when the Deserialize<T>() static method invoked.

public PersonClass(string foo, int age){  Name = foo; // The argument name isn't the same as the property.              // This will cause an unhandled exception.  ...

Deserializing will fail if the class has multiple constructors.

In some rare cases, the immutable object class has to have multiple constructors.

But if the class to be deserialized has multiple constructors, the Deserialize<T>() static method will not work expectedly.
even if the class has the appropriate constructor overload version for deserializing.

The Deserialize<T>() static method will return the object instance with no errors, but the properties values still default value.

class PersonClass{  ...  public PersonClass() { } //  Add this, then...  public PersonClass(string name, int age) {...}}...var json = @"{""Name"":""Taro"",""Age"":23}";var person = JsonSerializer.Deserialize<PersonClass>(json);// person.Name -> null ... is not "Taro"!// person.Age -> 0 ... is not 23!

Because the Deserialize<T>() static method will try to use the default constructor if the class has it.

But any properties are read-only, so the properties of the object that deserialized are not written.

To resolve this problem, use the [JsonConstructor] attribute.

We can annotate the appropriate constructor for deserialization with [JsonConstructor] attribute to resolve this problem.

After doing this, the Deserialize<T>() static method will use the [JsonConstructor] annotated constructor to instantiate the object. So the deserialization will work fine as the developers expected.

class PersonClass{  ...  public PersonClass() { }  [JsonConstructor] //  Adding this to the constructor for                    //     deserialization, will resolve the problem.  public PersonClass(string name, int age) {...}  ...

If the property has an init-only setter...

If the property has an init-only setter, the Deserialize<T>() static method will work fine as we expected, even if it has no [JsonConstructor] annotations.

class PersonClass{  public string Name { get; init; }  public int Age { get; init; }  public PersonClass() { }  public PersonClass(string name, int age) {...}  ...

In this case, the Deserialize<T>() static method will use the no-arguments default constructor to instantiate the object.

And the Deserialize<T>() static method understands how to treat init-only setter.

So the Deserialize<T>() static method can write back the value from JSON to the init-setter only property.

If the property has a private setter...

If the property has a private setter, the Deserialize<T>() static method will not work as fine as expected.

class PersonClass{  public string Name { get; private set; }  public int Age { get; private set; }  public PersonClass() { }  public PersonClass(string name, int age) {...}  ...

The Deserialize<T>() static method will not write back the value from a JSON to that property via the private setter without explicit instruction.

In this scenario, of course, we can use the [JsonConstructor] approach to resolve it.

But another way, we can also apply the [JsonInclude] attribute to the private setter property to resolve it.

If the private setter property is annotated the [JsonInclude] attribute, the Deserialize<T>() static method will write back the value from a JSON to that property via the private setter.

class PersonClass{  [JsonInclude] //  Adding this attribute allows to write back                //     the value from a JSON to this property.  public string Name { get; private set; }  [JsonInclude]  public int Age { get; private set; }  public PersonClass() { }  public PersonClass(string name, int age) {...}  ...

A little something extra

When using the "constructor initialized read-only properties" implementation pattern, considering using the "record" type is also one of a good option, I think.

Happy Coding! :)


Original Link: https://dev.to/j_sakamoto/c-some-scenarios-for-deserializing-a-json-to-a-type-with-read-only-properties-by-system-text-json-3lc

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To