-
DescriptionSwitching (trying) a project from The project uses versioned and inheriting record classes for schema management, with deserialization intantiating the correct version of the class, e.g. Schema2 : Schema3 : Schema1, and properties of the classes follows a similar inheritance history. After several rounds of troubleshooting I have a reproducible case when a property in a class is overloaded in a derived class. I reviewed the Example code at dotnetfiddle. Reproduction Stepsusing System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
public class Program
{
public static void Main()
{
// Create test object and populate it with some data
var parent = new Parent2()
{
Child = new Child2()
{
List = [
new() { Foo = "foo1", Bar = "bar1" },
new() { Foo = "foo2", Bar = "bar2" }
]
}
};
// Convert to and from JSON using Newtonsoft
var json1 = Newtonsoft.Json.JsonConvert.SerializeObject(parent, JsonSerializerSettingsWrite);
Console.WriteLine("Newtonsoft : Schema to JSON:");
Console.WriteLine(json1);
var schema1 = Newtonsoft.Json.JsonConvert.DeserializeObject<Parent2>(json1, JsonSerializerSettingsRead);
Console.WriteLine("Newtonsoft : JSON to Schema:");
Console.WriteLine(schema1);
// Convert to and from JSON using Text.Json
var json2 = JsonSerializer.Serialize(parent, JsonSerializerOptionsWrite);
Console.WriteLine("Text.Json : Schema to JSON:");
Console.WriteLine(json2);
var schema2 = JsonSerializer.Deserialize<Parent2>(json2, JsonSerializerOptionsRead);
Console.WriteLine("Text.Json : JSON to Schema:");
Console.WriteLine(schema2);
// Convert to and from JSON using Text.Json (without special settings)
json2 = JsonSerializer.Serialize(parent);
Console.WriteLine("Text.Json : Schema to JSON (no settings):");
Console.WriteLine(json2);
schema2 = JsonSerializer.Deserialize<Parent2>(json2);
Console.WriteLine("Text.Json : JSON to Schema (no settings):");
Console.WriteLine(schema2);
}
private static readonly Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettingsRead = new Newtonsoft.Json.JsonSerializerSettings
{
ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Reuse
};
private static readonly Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettingsWrite = new Newtonsoft.Json.JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented,
StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeNonAscii,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
ContractResolver = new ExcludeObsoletePropertiesResolver()
};
private static readonly JsonSerializerOptions JsonSerializerOptionsRead = new JsonSerializerOptions
{
PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true
};
private static readonly JsonSerializerOptions JsonSerializerOptionsWrite = new JsonSerializerOptions
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.WithAddedModifier(ExcludeObsoleteProperties)
};
private static void ExcludeObsoleteProperties(JsonTypeInfo typeInfo)
{
if (typeInfo.Kind != JsonTypeInfoKind.Object)
return;
foreach (var property in typeInfo.Properties)
{
if (property.AttributeProvider?.IsDefined(typeof(ObsoleteAttribute), true) == true)
property.ShouldSerialize = (_, _) => false;
}
}
}
public record Parent1
{
// Child1 replaced by Child2
[Obsolete]
public Child1 Child { internal get; set; } = new();
}
public record Parent2 : Parent1
{
public Parent2() { }
public Parent2(Parent1 parent) : base(parent) { }
// Child2 replaced Child1
// This seems to make Text.Json unhappy
[Required]
public new Child2 Child { get; set; } = new();
}
public record Sub
{
public string Foo;
public string Bar;
}
public record Child1
{
[Obsolete]
public string CSV { internal get; set; } = "";
}
public record Child2 : Child1
{
public Child2() { }
public Child2(Child1 child) : base(child) { }
[Required]
public List<Sub> List { get; set; } = [];
}
public class ExcludeObsoletePropertiesResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var memberList = base.GetSerializableMembers(objectType);
memberList.RemoveAll(item => item.IsDefined(typeof(ObsoleteAttribute), true));
return memberList;
}
} Expected behavior
Actual behavior
Regression?No response Known WorkaroundsRevert changes and continue using Configuration.NET 8, Windows 11, x64 Other informationNo response |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 7 replies
-
System.Text.Json doesn't support field serialization by default, which impacts how your |
Beta Was this translation helpful? Give feedback.
@ptr727
since the
get
accessor is not public, you need to annotate it with the[JsonInclude]
attribute to include it in (de)serialization. E.g.:The documentation does not seem to be clear about this, but i suspect that the deserializer notices the lack of a public getter and thus excludes the property from deserialization. And because the deserialization logic doesn't acknowledge the public setter without considering the getter (similarily to it also not acknowledging setter-only properties), it therefore neither attempts creating a new
List<Sub>
insta…