-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
System.Text.Json deserialization failing on classes with field encapsulation #55318
Comments
Tagging subscribers to this area: @eiriktsarpalis, @layomia Issue DetailsDescriptionWhen creating class that represents domain entity when we want to encapsulate collection so that it is impossible to add item to the collection without executing code that is responsible for validating domain invariants, we get exception on deserialization. Exception is thrown despite all names in the one and only constructor being correct. Classes being serialized:
Serialization code:
Exception:
Configuration.NET SDK version 5.0.202 Regression?I have no idea if this worked in previous version of Other informationI assume that problem lies in constructor parameter ( I know that
|
You can do something like this, as STJ uses lists for deserialization of public Invoice(List<InvoiceItem> items)
{
this.items = items;
}
[JsonConstructor]
[Obsolete]
public Invoice(IReadOnlyCollection<InvoiceItem> items)
{
this.items = items as List<InvoiceItem> ?? this.items.ToList();
} |
Actually in the codebase where I have this problem the type of the property is ROL. I used ROC in the reproduction sample to make the problem even more apparent but I expect this to work exactly same way no matter if my public property is ROL, ROC or IEnumerable. Agree that this limitation can be overcome with some additional code that developer needs to remember about but I have project that is big enough (in terms of number of developers and size of codebase) that I can't give the go-ahead to make this change because it is simply not worth extra comotion and potential bugs. |
Not saying you should, but if you want to reduce the developer overhead and need go that route, go look the source generator direction, imho that's a classic example for that, adding boilerplate code. |
Yes it can be done with source generators but I'm not a fan of having source generators as part of domain model, especially in a case where this boilerplate isn't really justified by anything other than a missing feature in a framework that we don't really need to use. We can stay with Newtonsoft.Json and avoid unnecessary boilerplate (generated or not). Just wanted to point out this unsupported scenario to appropriate team. If the team responsible wants to increase adoption of this serializer then this scenario should be seriously considered as everyone who is doing DDD (or actually any kind of proper OOP) and is using serializer to serialize the domain model (not just DTOs) will stumble on it. |
That would certainly fix the particular example, since both type and subtype happen to utilize identical JSON constracts. However it might not be roundtrippable general case, as might be demonstrated in this (artificial) example: public class MyClass
{
public object MyProperty => "string";
public MyClass(int myProperty) { }
} We've received related feedback in #43563 and #54854. I'm wondering if it might make sense to expose a setting that switches off constructor parameter validation, as there are many valid use cases where it might be obstructing users cc @layomia. |
I believe the requirements of this issue are generally covered by #44428. I'll close this issue as a duplicate. @eiriktsarpalis yes I believe such a switch would be beneficial as an "I know what I'm doing" mode. This is considered in #44428. |
Description
When creating class that represents domain entity when we want to encapsulate collection so that it is impossible to add item to the collection without executing code that is responsible for validating domain invariants, we get exception on deserialization. Exception is thrown despite all names in the one and only constructor being correct.
Classes being serialized:
Serialization code:
Exception:
Configuration
.NET SDK version 5.0.202
Not listing OS or processor architecture as I don't think this is related to specific OS/hardware configuration.
Regression?
I have no idea if this worked in previous version of
System.Text.Json
so I don't know if it is regression. It definitely works in Newtonsoft.Json without any special configuration.Other information
I assume that problem lies in constructor parameter (
List<InvoiceItem>
) having different type than the public property (IReadOnlyCollection<InvoiceItem>
) that is exposing encapsulated list. So theoretically I can change constructor parameter to beIReadOnlyCollection<InvoiceItem>
and doToList()
but this will do an extra allocations and defeat the whole purpose of migrating from Newtonsoft.Json as the purpose is performance improvements (because it's clearly not the features :)).I know that
System.Text.Json
was never meant to be one to one replacement forNewtonsoft.Json
but this is not some egzotic feature I'm describing here. It is relevant to everyone who wants to model their Aggregates according to DDD best practices and needs to serialize them for persistence in DB. Also I never saw in any docs/articles stating that the only scenario in mind when designingSystem.Text.Json
was serializing DTOs.The text was updated successfully, but these errors were encountered: