Skip to content

Add APIs to JSON Contract customization #71123

Closed
@krwq

Description

@krwq

Continuation of #63686 - some API additions we wanted to make in this release.

API changes

namespace System.Text.Json.Serialization.Metadata;

public class JsonTypeInfo
{
    // Maps to IJsonOnSerializing
    public Action<object>? OnSerializing { get; set; } = null;
    // Maps to IJsonOnSerialized
    public Action<object>? OnSerialized { get; set; } = null;
    // Maps to IJsonOnDeserializing
    public Action<object>? OnDeserializing { get; set; } = null;
    // Maps to IJsonOnDeserialized
    public Action<object>? OnDeserialized { get; set; } = null;
}

public abstract partial class JsonPropertyInfo
{
    // Abstracts backing member, DefaultJsonTypeInfoResolver will return instance of PropertyInfo/FieldInfo
    public System.Reflection.ICustomAttributeProvider? AttributeProvider { get; set; } = null;

    // Maps to JsonPropertyOrderAttribute
    public int Order { get; set; } = 0;

    // Maps to JsonExtensionDataAttribute
    // Alternatively this could be a nullable JsonPropertyInfo on JsonTypeInfo.
    public bool IsExtensionData { get; set; } = false;
}

[EditorBrowsable(EditorBrowsableState.Never)]
public static partial class JsonMetadataServices
{
    public static JsonConverter<T?> GetNullableConverter<T>(JsonSerializerOptions options) where T : struct { throw null; }
}

namespace System.Text.Json;

public partial class JsonSerializerOptions
{
     // Existing GetConverter API
     public JsonConverter GetConverter(Type type);

     // Gets metadata that has been cached by this options instance
     // Can be used to plug metadata directly into JsonSerializer overloads that accept JsonTypeInfo<T>
     public JsonTypeInfo GetTypeInfo(Type type);
}

Sample

public static void DataContractAttributeModifier(JsonTypeInfo jsonTypeInfo)
{
    if (jsonTypeInfo.Kind == JsonTypeInfoKind.Object &&
        type.GetCustomAttribute<DataContractAttribute>() is not null)
    {
        foreach (JsonPropertyInfo propertyInfo in jsonTypeInfo.Properties)
        {
            DataMemberAttribute? dataMemberAttribute = (DataMemberAttribute?)propertyInfo.AttributeProvider.GetCustomAttributes(typeof(DataMemberAttribute), inherit: false).FirstOrDefault()
            if (dataMemberAttribute is not null)
            {
                propertyInfo.Name = dataMemberAttribute.Name;
                propertyInfo.Order = dataMemberAttribute.Order;
            }
        }
    }
}

Remaining work

  • Remaining comments in JSON contract customization #70435
  • Adjust polymorphism APIs to match approved API shape
  • check trimmed app size before and after contract customization changes
  • JsonTypeInfo instances should never encapsulate mutable JsonSerializerOptions instances.
  • Investigate all logic consuming the JsonSerializerOptions.SerializerContext property as combined resolvers might invalidate their intentions.
  • More JsonSerializeContext + JsonTypeInfoResolver.Combine tests
  • More functional tests, review issues and implement scenarios
  • Re-iterate on the behavior in StaticInitialization_DeserializationWithJsonTypeInfoWithoutSettingTypeInfoResolverThrows and StaticInitialization_SerializationWithJsonTypeInfoWithoutSettingTypeInfoResolverThrows
  • once everything above is done ensure no regressions are made in performance suite (we've done checkpoint run already for JSON contract customization #70435 and no regressions)
  • XML docs in public APIs need improvement, e.g. <summary> sections should be one-liners, should use <see /> elements where applicable, should use <remarks> sections where applicable, etc. Wording seems to deviate with how we document elsewhere.
  • Regressions in System.Text.Json.Serialization.Tests.ColdStartSerialization<SimpleStructWithProperties> #71392
  • Unify options/sourcegen root-level serialization helpers to use JsonTypeInfo<T>.
  • TODOs in the code/tests
  • Tests with RemoteExecute, make sure static initialization works correctly
  • Examine nullability/linkability of JsonSerializerOptions.TypeInfoResolver
  • Walk through all existing APIs and double check they will cooperate well with the design

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions