-
Notifications
You must be signed in to change notification settings - Fork 228
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
Order of meta JSON attributes changed on persisting #2621
Comments
@jonatanschneider that's expected, and is how the PostgreSQL It's generally not recommended to rely on the ordering of object keys in JSON. |
@roji Thanks for the quick answer. However, System.Text.Json relies on the specific order: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-7-0 In my short tests with Newtonsoft, I could observe the same behaviour, only that they don't crash but rather ignore all type values in such a case. Therefore my only option is to use a JSON (de-)serializer which provides a read-ahead logic (as Newtonsoft does with an extra setting), am I correct? |
Thanks for pointing that out - I wasn't aware this was added for 7.0. This problem seems to be tracked on the System.Text.Json side in dotnet/runtime#72604 - I there are some workarounds listed there which may be workable for you. I hope you they do fix this properly though. Note that you can still use the I'm going to go ahead and close this as I don't think there's anything Npgsql- or EF- related, but I'd be happy to continue the conversation so don't hesitate to post back here. |
Since retrieving data using npgsql already is entirely buffered (per row) you can switch easily to an intermediary JsonNode and fix the ordering there first, before deserializing the node to final objects. public static JsonNode? EnsureTypeDiscriminatorIsFirstProperty(JsonNode? node, string discriminatorName = "$type")
{
switch ( node )
{
case JsonArray array:
{
foreach ( var subNode in array )
EnsureTypeDiscriminatorIsFirstProperty(subNode, discriminatorName);
break;
}
case JsonObject obj:
{
if ( obj.Count == 0 || obj.First().Key == discriminatorName )
break;
var sorted = obj.OrderBy(kv => kv.Key != discriminatorName).ToList();
obj.Clear();
foreach ( var (propName, propNode) in sorted )
{
if ( propNode != null )
EnsureTypeDiscriminatorIsFirstProperty(propNode, discriminatorName);
obj.Add(propName, propNode);
}
break;
}
}
return node;
} |
Good point. That does introduce an additional perf overhead, but it's certainly a workaround. |
Hi,
I'm currently using npgsql with the entity framework core adapter to persist data into a JSONB column in a postgres database.
For my use-case I need polymorphic (de-)serialization. As of .NET 7 System.Text.Json introduced type discriminators for this purpose.
The problem:
The persisted object in the database is different from the output System.Text.Json produces when directly calling the Serializer. In more detail, meta-elements are not the first attributes anymore, which leads to an exception on deserialization.
Consider the following example:
Calling the JsonSerializer directly with Serialize produces the following output:
Take note that $id and $type are always the first elements in an object.
However after calling SaveChanges, the following data is stored in the database:
As you can see the inner object of "Child1" has a different order of the meta-elements. This leads (of course) to an error on deserialization:
Here is my code, used for this example:
The model classes:
Database setup
For additional needed configuration of the System.Text.Json serializer I use the workaround as described in #1107 (comment)
Is this a known bug or did I configure something wrong?
The text was updated successfully, but these errors were encountered: