-
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 Serializer Behavior Changes Depending on Object Interfaces #64302
Comments
Tagging subscribers to this area: @dotnet/area-system-text-json Issue DetailsDescription
Reproduction Stepsusing System.Text.Json;
using System.Text.Json.Serialization;
var opts = new JsonSerializerOptions
{
Converters =
{
new JsonStringEnumConverter()
},
WriteIndented = true,
};
var thing = new EnrichedThing
{
Key = "A",
Metadata = "Source=Test",
Name = "EnrichedThing",
Number = 123.45,
Expires = DateTime.Now,
Extra = "Extra"
};
var proxy = new ProxiedThing
{
Key = "B",
Metadata = "Source=Test",
Name = "ProxiedThing",
Number = 123.45,
Expires = DateTime.Now,
Extra = "Extra"
};
Console.WriteLine(JsonSerializer.Serialize(thing, opts));
Console.WriteLine(JsonSerializer.Serialize(proxy, opts));
public interface IKey
{
string Key { get; set; }
string Metadata { get; set; }
}
public class Model : IKey
{
public virtual string Key { get; set; }
public virtual string Metadata { get; set; }
}
public interface IEnriched
{
double Number { get; set; }
DateTime Expires { get; set; }
}
public class ModelBase : Model, IEnriched
{
public double Number { get; set; }
public DateTime Expires { get; set; }
public string Extra { get; set; }
}
public class EnrichedBase : ModelBase, IEnriched // implement IEnriched at this level
{
[JsonIgnore]
public new double Number { get; set; }
[JsonIgnore]
public new DateTime Expires { get; set; }
[JsonIgnore]
public new string Extra { get; set; }
}
public class EnrichedThing : EnrichedBase
{
public string Name { get; set; }
}
public class ProxiedBase : ModelBase // allow IEnriched implementation to come from ModelBase
{
[JsonIgnore]
public new double Number { get; set; }
[JsonIgnore]
public new DateTime Expires { get; set; }
[JsonIgnore]
public new string Extra { get; set; }
}
public class ProxiedThing : ProxiedBase
{
public string Name { get; set; }
} Expected behaviorBoth serialized payloads should look the same, preferably like Actual behaviorThe payloads are different for Thing: {
"Name": "EnrichedThing",
"Extra": null,
"Key": "A",
"Metadata": "Source=Test"
} Proxy: {
"Name": "ProxiedThing",
"Number": 0,
"Expires": "0001-01-01T00:00:00",
"Extra": null,
"Key": "B",
"Metadata": "Source=Test"
} Regression?I don't know. Known WorkaroundsMarking ModelBase properties as virtual and overriding in derived types resolves the issue. ConfigurationWhich version of .NET is the code running on? 6.0.100 Other informationNo response
|
This is by design, the serialization contract employed is type directed and does not reflect the runtime type of the serialized object. You should still be able to influence the contract by the changing the type of either the root value or types of members like so: Console.WriteLine(JsonSerializer.Serialize<ModelBase>(thing, opts));
Console.WriteLine(JsonSerializer.Serialize<ModelBase>(proxy, opts)); producing {
"Number": 0,
"Expires": "0001-01-01T00:00:00",
"Extra": null,
"Key": "A",
"Metadata": "Source=Test"
}
{
"Number": 0,
"Expires": "0001-01-01T00:00:00",
"Extra": null,
"Key": "B",
"Metadata": "Source=Test"
} Your example also showcases a known issue with how |
Description
JsonSerializer.Serialize<T>(T value, [JsonSerializerOptions options = null])
behavior changes depending on interface implementation level within inheritance hierarchy when hiding parent type properties.Reproduction Steps
Expected behavior
Both serialized payloads should look the same, preferably like
Thing
below. I understand System.Text.Json doesn't support polymorphic hierarchies at the moment so I won't get into the correctness of theExtra
property appearing in the first place. I would not expect the level at which an interface is satisfied to be relevant with regards to serialization, especially when the value passed to the serializer is the most specific concrete type and it is never held as an implementation of that interface.Actual behavior
The payloads are different for
thing
andproxy
.Thing:
Proxy:
Regression?
I don't know.
Known Workarounds
Marking ModelBase properties as virtual and overriding in derived types resolves the issue.
Configuration
Which version of .NET is the code running on? 6.0.100
What OS and version, and what distro if applicable? Windows 10
What is the architecture (x64, x86, ARM, ARM64)? x64
Do you know whether it is specific to that configuration? I don't know.
Other information
No response
The text was updated successfully, but these errors were encountered: