-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
For polymorphic scenarios, the new modifier does not always work with JsonIgnore. This affects both the reflection-based serializer and the source-gen serializer. See also #58985 (comment)
The semantics of the new modifier need to be documented and deterministic. For example, when [JsonIgnore] is applied to a derived type's property that has new and [JsonIgnore], does the base class property (which is a different property / has its own vtable slot) get serialized and deserialized?
Also verification is need when [JsonIgnore] and new are used along other attributes including [JsonPropertyOrder] on a derived type's property. The serializer should used the [JsonPropertyOrder] on the property that is actually serialized, and not the one that is ignored.
Test case failure
string json = JsonSerializer.Serialize(new ConcreteDerivedClass());
// Throws System.ArgumentException: 'An item with the same key has already been added. Key: Ignored_New_Property'
public class ConcreteDerivedClass : AbstractBaseClass
{
[JsonIgnore]
public new int Ignored_New_Property { get; set; } = 1; // Also test with making this new slot method virtual
}
public abstract class AbstractBaseClass
{
[JsonIgnore]
public int Ignored_New_Property { get; set; } = 2;
}Test case success 1 (serialize)
string json = JsonSerializer.Serialize(new ConcreteDerivedClass());
// Correctly returns "{\"Ignored_New_Property\":1}"
public class ConcreteDerivedClass : AbstractBaseClass
{
public new int Ignored_New_Property { get; set; } = 1;
}
public abstract class AbstractBaseClass
{
[JsonIgnore]
public int Ignored_New_Property { get; set; } = 2;
}Test case success 2 (serialize)
string json = JsonSerializer.Serialize(new ConcreteDerivedClass());
// Correctly returns "{\"Ignored_New_Property\":2}"
public class ConcreteDerivedClass : AbstractBaseClass
{
[JsonIgnore]
public new int Ignored_New_Property { get; set; } = 1;
}
public abstract class AbstractBaseClass
{
public int Ignored_New_Property { get; set; } = 2;
}Test case success 3 (deserialize)
ConcreteDerivedClass obj = JsonSerializer.Deserialize<ConcreteDerivedClass>("{\"Ignored_New_Property\":42}");
Debug.Assert(obj.Ignored_New_Property == 1);
Debug.Assert(((AbstractBaseClass)obj).Ignored_New_Property == 42);
public abstract class AbstractBaseClass
{
public int Ignored_New_Property { get; set; } = 2;
}
public class ConcreteDerivedClass : AbstractBaseClass
{
[JsonIgnore]
public new virtual int Ignored_New_Property { get; set; } = 1;
}Test case success 4 (deserialize)
ConcreteDerivedClass obj = JsonSerializer.Deserialize<ConcreteDerivedClass>("{\"Ignored_New_Property\":42}");
Debug.Assert(obj.Ignored_New_Property == 42);
Debug.Assert(((AbstractBaseClass)obj).Ignored_New_Property == 2);
public abstract class AbstractBaseClass
{
[JsonIgnore]
public int Ignored_New_Property { get; set; } = 2;
}
public class ConcreteDerivedClass : AbstractBaseClass
{
public new virtual int Ignored_New_Property { get; set; } = 1;
}