I need to read older JSON with [Obsolete] properties, but when writing JSON only the [Required] properties should be written.
I still have a mix of older Newtonsoft.Json and newer Text.Json in .NET 8, and I'm looking for a solution in both.
I am using custom settings with a ContractResolver in Newtonsoft.Json and a TypeInfoResolver in Text.Json.
Questions:
- The Newtonsoft.Json logic does not read nor write the [Obsolete]attribute. Is there a mistake in the code, or do I need different settings for read and write?
- Is the Text.Json the correct/best way to disable serialization on write?
- In both cases, can I detect if the serialization is to read or write and change behavior, or do I need different config for reading and for writing?
Example code:
// Text.Json settings
var jsonSerializerSettings = new JsonSerializerSettings()
{
    Formatting = Formatting.Indented,
    StringEscapeHandling = StringEscapeHandling.EscapeNonAscii,
    NullValueHandling = NullValueHandling.Ignore,
    ObjectCreationHandling = ObjectCreationHandling.Reuse,
    ContractResolver = new ExcludeObsoletePropertiesResolver()
};
// Newtonsoft.Json options
var jsonSerializerOptions = new JsonSerializerOptions()
{
    WriteIndented = true, // Formatting = Formatting.Indented,
    Encoder = null, // StringEscapeHandling = StringEscapeHandling.EscapeNonAscii,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // NullValueHandling = NullValueHandling.Ignore,
    PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, // ObjectCreationHandling = ObjectCreationHandling.Reuse,
    TypeInfoResolver = new DefaultJsonTypeInfoResolver()
        .WithAddedModifier(JsonExtensions.ExcludeObsoleteProperties),
    ReadCommentHandling = JsonCommentHandling.Skip,
    AllowTrailingCommas = true
};
// JSON string
var jsonString = "{\"Required\":\"Required\",\"Obsolete\":\"Obsolete\"}";
// Newtonsoft
// Error, Obsolete is not read nor written
var schemaNewtonJson = Newtonsoft.Json.JsonConvert.DeserializeObject<Schema>(jsonString, jsonSerializerSettings);
var jsonNewtonJson = Newtonsoft.Json.JsonConvert.SerializeObject(schemaNewtonJson, jsonSerializerSettings);
// Text.Json
// Seems to work, Obsolete is read but not written
var schemaTextJson = System.Text.Json.JsonSerializer.Deserialize<Schema>(jsonString, jsonSerializerOptions);
var jsonTextJson = System.Text.Json.JsonSerializer.Serialize<Schema>(schemaTextJson, jsonSerializerOptions);
public record Schema
{
    // Always read and write
    [Required]
    public string Required { get; set; } = "";
    // Read but do not write
    [Obsolete]
    public string Obsolete { get; set; } = "";
}
public static class JsonExtensions
{
    public static void ExcludeObsoleteProperties(JsonTypeInfo typeInfo)
    {
        if (typeInfo.Kind != JsonTypeInfoKind.Object)
            return;
        foreach (var property in typeInfo.Properties)
        {
            // Do not serialize Obsolete items
            if (property.AttributeProvider?.IsDefined(typeof(ObsoleteAttribute), true) == true)
                property.ShouldSerialize = (object _, object? _) => { return false; };
        }
    }
}
public class ExcludeObsoletePropertiesResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        var memberList = base.GetSerializableMembers(objectType);
        // Remove all Obsolete items
        memberList.RemoveAll(item => item.IsDefined(typeof(ObsoleteAttribute), true));
        return memberList;
    }
}