dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.98k stars 4.66k forks source link

JsonSerializer.Serialize produces invalid JSON for [JsonExtensionData] property #97225

Open KalininAndreyVictorovich opened 8 months ago

KalininAndreyVictorovich commented 8 months ago

Description

When serializing object containing property marked with [JsonExtensionData] attribute, serializer produces invalid JSON like this one: {"Id":1,{"nested":true}}

Reproduction Steps

Run the following code

var mix = new Mix
{
    Id = 1,
    Extra = new() { ["nested"] = true, }
};

var text = System.Text.Json.JsonSerializer.Serialize(mix);
Console.WriteLine(text);
// output {"Id":1,{"nested":true}}

public class Mix
{
    public int Id { get; set; }

    [System.Text.Json.Serialization.JsonExtensionData]
    public System.Text.Json.Nodes.JsonObject? Extra { get; set; }
}

Expected behavior

Correct JSON like {"Id":1,"nested":true} or at least valid JSON as if there was not [JsonExtensionData] attribute ({"Id":1,"Extra":{"nested":true}} ).

Actual behavior

Invalid JSON {"Id":1,{"nested":true}}

Regression?

Reproducible at least on .Net 6 to .Net 8

Known Workarounds

No response

Configuration

.Net 8 Windows 11 x64

Other information

No response

ghost commented 8 months ago

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis See info in area-owners.md if you want to be subscribed.

Issue Details
### Description When serializing object containing property marked with `[JsonExtensionData]` attribute, serializer produces invalid JSON like this one: `{"Id":1,{"nested":true}}` ### Reproduction Steps Run the following code ```C# var mix = new Mix { Id = 1, Extra = new() { ["nested"] = true, } }; var text = System.Text.Json.JsonSerializer.Serialize(mix); Console.WriteLine(text); // output {"Id":1,{"nested":true}} public class Mix { public int Id { get; set; } [System.Text.Json.Serialization.JsonExtensionData] public System.Text.Json.Nodes.JsonObject? Extra { get; set; } } ``` ### Expected behavior Correct JSON like `{"Id":1,"nested":true}` or at least valid JSON as if there was not `[JsonExtensionData]` attribute (`{"Id":1,"Extra":{"nested":true}}` ). ### Actual behavior Invalid JSON `{"Id":1,{"nested":true}}` ### Regression? Reproducible at least on .Net 6 to .Net 8 ### Known Workarounds _No response_ ### Configuration .Net 8 Windows 11 x64 ### Other information _No response_
Author: KalininAndreyVictorovich
Assignees: -
Labels: `area-System.Text.Json`
Milestone: -
eiriktsarpalis commented 8 months ago

Can confirm that this occurs. It seems we never added testing for the serialization scenario -- deserialization appears to be working as expected.

elgonzo commented 8 months ago

There is a related question as to whether this scenario is supposed to be allowed or not. Because the documentation for JsonExtensionDataAttribute states:

The dictionary's TKey value must be String, and TValue must be JsonElement or Object.

However, the example code in the report uses JsonObject as extension data property - an IDictionary<string, JsonNode>, which according to the JsonExtensionDataAttribute documentation is not an allowed dictionary type.

So, basically the problem is either


@eiriktsarpalis

deserialization appears to be working as expected

Does this then mean the documentation for JsonExtensionDataAttribute is wrong about the types allowed for extension data properties?

eiriktsarpalis commented 8 months ago

The documentation is correctly stating that JsonObject is one of the supported types. However there is a bug specifically impacting serialization for the particular type (FWIW JsonExtensionData is a feature primarily oriented towards dserialization).

elgonzo commented 8 months ago

The documentation is correctly stating that JsonObject is one of the supported types. [...]

You seem to be mistaken. Where does the documentation for JsonExtensionDataAttribute state this?

The related documentation page "How to handle overflow JSON or use JsonElement or JsonNode in System.Text.Json" also does not support your claim:

To capture extra data such as these properties, apply the [JsonExtensionData] attribute to a property of type Dictionary<string,object> or Dictionary<string,JsonElement>.

eiriktsarpalis commented 8 months ago

You're right, I misread the documentation which appears to be out of date. The correct statement on supported types can actually be found in the error messages of the implementation itself:

https://github.com/dotnet/runtime/blob/2d751cac7311b344c237df3b3c63b33434b52217/src/libraries/System.Text.Json/gen/Resources/Strings.resx#L150-L152

In other words, what I mentioned earlier holds. JsonObject is supported and there is a bug specifically concerning serialization.

elgonzo commented 8 months ago

Okay. Regarding the documentation being out of date, should i file an issue report with the docs repo, or do you handle this internally between teams?

eiriktsarpalis commented 8 months ago

It would help if you could file a separate issue in dotnet-api-docs. Thanks!

RutulPatel8 commented 8 months ago

Can confirm that this occurs. It seems we never added testing for the serialization scenario -- deserialization appears to be working as expected.

Yes I have tested with Dot net core 6 version. It is reproducible. image