Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,12 @@ public JsonSerializerOptions(System.Text.Json.JsonSerializerOptions options) { }
public bool IncludeFields { get { throw null; } set { } }
public int MaxDepth { get { throw null; } set { } }
public System.Text.Json.Serialization.JsonNumberHandling NumberHandling { get { throw null; } set { } }
public System.Func<Type, bool> SupportedPolymorphicTypes { get { throw null; } set { } }
public bool PropertyNameCaseInsensitive { get { throw null; } set { } }
public System.Text.Json.JsonNamingPolicy? PropertyNamingPolicy { get { throw null; } set { } }
public System.Text.Json.JsonCommentHandling ReadCommentHandling { get { throw null; } set { } }
public System.Text.Json.Serialization.ReferenceHandler? ReferenceHandler { get { throw null; } set { } }
public System.Collections.Generic.IList<System.Text.Json.Serialization.TypeDiscriminatorConfiguration> TypeDiscriminatorConfigurations { get { throw null; } }
public System.Text.Json.Serialization.JsonUnknownTypeHandling UnknownTypeHandling { get { throw null; } set { } }
public bool WriteIndented { get { throw null; } set { } }
public void AddContext<TContext>() where TContext : System.Text.Json.Serialization.JsonSerializerContext, new() { }
Expand Down Expand Up @@ -796,6 +798,11 @@ public sealed partial class JsonNumberHandlingAttribute : System.Text.Json.Seria
public JsonNumberHandlingAttribute(System.Text.Json.Serialization.JsonNumberHandling handling) { }
public System.Text.Json.Serialization.JsonNumberHandling Handling { get { throw null; } }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
public sealed partial class JsonPolymorphicTypeAttribute : System.Text.Json.Serialization.JsonAttribute
{
public JsonPolymorphicTypeAttribute() { }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple=false)]
public sealed partial class JsonPropertyNameAttribute : System.Text.Json.Serialization.JsonAttribute
{
Expand Down Expand Up @@ -846,6 +853,27 @@ public enum JsonUnknownTypeHandling
JsonElement = 0,
JsonNode = 1,
}
[System.AttributeUsageAttribute(System.AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)]
public partial class JsonKnownTypeAttribute : System.Text.Json.Serialization.JsonAttribute
{
public JsonKnownTypeAttribute(System.Type subtype, string identifier) { }
public string Identifier { get { throw null; } }
public System.Type Subtype { get { throw null; } }
}
public partial class TypeDiscriminatorConfiguration : System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.Type, string>>, System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<System.Type, string>>, System.Collections.IEnumerable
{
public TypeDiscriminatorConfiguration(System.Type baseType) { }
public System.Type BaseType { get { throw null; } }
int System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<System.Type, string>>.Count { get { throw null; } }
System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<System.Type, string>> System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.Type, string>>.GetEnumerator() { throw null; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
public System.Text.Json.Serialization.TypeDiscriminatorConfiguration WithKnownType(System.Type derivedType, string identifier) { throw null; }
}
public partial class TypeDiscriminatorConfiguration<TBaseType> : System.Text.Json.Serialization.TypeDiscriminatorConfiguration where TBaseType : class
{
public TypeDiscriminatorConfiguration() : base(default(System.Type)) { }
public System.Text.Json.Serialization.TypeDiscriminatorConfiguration<TBaseType> WithKnownType<TDerivedType>(string identifier) where TDerivedType : TBaseType { throw null; }
}
public abstract partial class ReferenceHandler
{
protected ReferenceHandler() { }
Expand Down
7 changes: 6 additions & 1 deletion src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,24 @@
<Compile Include="System\Text\Json\Serialization\Attributes\JsonIgnoreAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\Attributes\JsonIncludeAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\Attributes\JsonNumberHandlingAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\Attributes\JsonPolymorphicTypeAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\Attributes\JsonPropertyNameAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\Attributes\JsonSerializableAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\JsonMetadataServicesConverter.cs" />
<Compile Include="System\Text\Json\Serialization\IgnoreReferenceResolver.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializerContext.cs" />
<Compile Include="System\Text\Json\Serialization\Attributes\JsonKnownTypeAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\PolymorphicSerializationState.cs" />
<Compile Include="System\Text\Json\Serialization\TypeDiscriminatorConfiguration.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonMetadataServices.Collections.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonMetadataServices.Converters.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonTypeInfoInternalOfT.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonTypeInfoOfT.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonMetadataServices.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\TypeDiscriminatorResolver.cs" />
<Compile Include="System\Text\Json\Serialization\ReferenceEqualsWrapper.cs" />
<Compile Include="System\Text\Json\Serialization\ConverterStrategy.cs" />
<Compile Include="System\Text\Json\Serialization\ConverterList.cs" />
<Compile Include="System\Text\Json\Serialization\ConfigurationList.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\ArrayConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\ConcurrentQueueOfTConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\ConcurrentStackOfTConverter.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Text.Json.Serialization
{
/// <summary>
/// When placed on a type, indicates that the specified subtype should
/// be serialized polymorphically using type discriminator identifiers.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)]
public class JsonKnownTypeAttribute : JsonAttribute
{
/// <summary>
/// Initializes a new attribute with specified parameters.
/// </summary>
/// <param name="subtype">The known subtype that should be serialized polymorphically.</param>
/// <param name="identifier">The string identifier to be used for the serialization of the subtype.</param>
public JsonKnownTypeAttribute(Type subtype, string identifier)
{
Subtype = subtype;
Identifier = identifier;
}

/// <summary>
/// The known subtype that should be serialized polymorphically.
/// </summary>
public Type Subtype { get; }

/// <summary>
/// The string identifier to be used for the serialization of the subtype.
/// </summary>
public string Identifier { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Text.Json.Serialization
{
/// <summary>
/// When placed on a type, indicates that values should
/// be serialized using the schema of their runtime types.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
public sealed class JsonPolymorphicTypeAttribute : JsonAttribute
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,28 @@
namespace System.Text.Json.Serialization
{
/// <summary>
/// A list of JsonConverters that respects the options class being immuttable once (de)serialization occurs.
/// A list of configuration items that respects the options class being immutable once (de)serialization occurs.
/// </summary>
internal sealed class ConverterList : IList<JsonConverter>
internal sealed class ConfigurationList<TItem> : IList<TItem>
{
private readonly List<JsonConverter> _list = new List<JsonConverter>();
private readonly List<TItem> _list;
private readonly JsonSerializerOptions _options;

public ConverterList(JsonSerializerOptions options)
public Action<TItem>? OnElementAdded { get; set; }

public ConfigurationList(JsonSerializerOptions options)
{
_options = options;
_list = new();
}

public ConverterList(JsonSerializerOptions options, ConverterList source)
public ConfigurationList(JsonSerializerOptions options, IList<TItem> source)
{
_options = options;
_list = new List<JsonConverter>(source._list);
_list = new List<TItem>(source is ConfigurationList<TItem> cl ? cl._list : source);
}

public JsonConverter this[int index]
public TItem this[int index]
{
get
{
Expand All @@ -47,7 +50,7 @@ public JsonConverter this[int index]

public bool IsReadOnly => false;

public void Add(JsonConverter item)
public void Add(TItem item)
{
if (item == null)
{
Expand All @@ -56,6 +59,7 @@ public void Add(JsonConverter item)

_options.VerifyMutable();
_list.Add(item);
OnElementAdded?.Invoke(item);
}

public void Clear()
Expand All @@ -64,27 +68,27 @@ public void Clear()
_list.Clear();
}

public bool Contains(JsonConverter item)
public bool Contains(TItem item)
{
return _list.Contains(item);
}

public void CopyTo(JsonConverter[] array, int arrayIndex)
public void CopyTo(TItem[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}

public IEnumerator<JsonConverter> GetEnumerator()
public IEnumerator<TItem> GetEnumerator()
{
return _list.GetEnumerator();
}

public int IndexOf(JsonConverter item)
public int IndexOf(TItem item)
{
return _list.IndexOf(item);
}

public void Insert(int index, JsonConverter item)
public void Insert(int index, TItem item)
{
if (item == null)
{
Expand All @@ -93,9 +97,10 @@ public void Insert(int index, JsonConverter item)

_options.VerifyMutable();
_list.Insert(index, item);
OnElementAdded?.Invoke(item);
}

public bool Remove(JsonConverter item)
public bool Remove(TItem item)
{
_options.VerifyMutable();
return _list.Remove(item);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value,
int index = state.Current.EnumeratorIndex;

JsonConverter<TElement> elementConverter = GetElementConverter(ref state);
if (elementConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
if (GetElementTypeInfo(ref state).CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
{
// Fast path that avoids validation and extra indirection.
for (; index < array.Length; index++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ internal sealed override bool OnTryRead(
{
JsonTypeInfo elementTypeInfo = state.Current.JsonTypeInfo.ElementTypeInfo!;

if (state.UseFastPath)
if (state.UseFastPath && !state.CanContainPolymorphismMetadata)
{
// Fast path that avoids maintaining state variables and dealing with preserved references.

Expand All @@ -67,7 +67,7 @@ internal sealed override bool OnTryRead(
CreateCollection(ref reader, ref state);

_valueConverter ??= GetConverter<TValue>(elementTypeInfo);
if (_valueConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
if (elementTypeInfo.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
{
// Process all elements.
while (true)
Expand Down Expand Up @@ -132,8 +132,11 @@ internal sealed override bool OnTryRead(
}

// Handle the metadata properties.
bool preserveReferences = options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve;
if (preserveReferences && state.Current.ObjectState < StackFrameObjectState.PropertyValue)
bool canContainMetadata =
options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve ||
state.CanContainPolymorphismMetadata;

if (canContainMetadata && state.Current.ObjectState < StackFrameObjectState.PropertyValue)
{
if (JsonSerializer.ResolveMetadataForJsonObject<TCollection>(ref reader, ref state, options))
{
Expand Down Expand Up @@ -188,7 +191,7 @@ internal sealed override bool OnTryRead(

state.Current.PropertyState = StackFramePropertyState.Name;

if (preserveReferences)
if (canContainMetadata)
{
ReadOnlySpan<byte> propertyName = reader.GetSpan();
if (propertyName.Length > 0 && propertyName[0] == '$')
Expand Down Expand Up @@ -278,9 +281,9 @@ internal sealed override bool OnTryWrite(
{
state.Current.ProcessedStartToken = true;
writer.WriteStartObject();
if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve)
if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve || state.PolymorphicTypeDiscriminator is not null)
{
if (JsonSerializer.WriteReferenceForObject(this, dictionary, ref state, writer) == MetadataPropertyName.Ref)
if (JsonSerializer.WriteMetadataForObject(this, dictionary, ref state, writer, options.ReferenceHandlingStrategy) == MetadataPropertyName.Ref)
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected internal override bool OnWriteResume(
_keyConverter ??= GetConverter<TKey>(typeInfo.KeyTypeInfo!);
_valueConverter ??= GetConverter<TValue>(typeInfo.ElementTypeInfo!);

if (!state.SupportContinuation && _valueConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
if (!state.SupportContinuation && typeInfo.ElementTypeInfo!.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
{
// Fast path that avoids validation and extra indirection.
do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac

if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
if (!TypeToConvert.IsAssignableFrom(typeof(List<TElement>)))
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
Expand Down Expand Up @@ -96,17 +96,7 @@ protected override bool OnWriteResume(
return true;
}

internal override Type RuntimeType
{
get
{
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
return typeof(List<TElement>);
}

return TypeToConvert;
}
}
internal override JsonTypeInfo.ConstructorDelegate? ConstructorDelegate =>
MemberAccessor.CreateConstructor<List<TElement>>(TypeToConvert);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac

if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
if (!TypeToConvert.IsAssignableFrom(typeof(Dictionary<string, object?>)))
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
Expand Down Expand Up @@ -115,17 +115,7 @@ protected internal override bool OnWriteResume(Utf8JsonWriter writer, TCollectio
return true;
}

internal override Type RuntimeType
{
get
{
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
return typeof(Dictionary<string, object>);
}

return TypeToConvert;
}
}
internal override JsonTypeInfo.ConstructorDelegate? ConstructorDelegate =>
MemberAccessor.CreateConstructor<Dictionary<string, object?>>(TypeToConvert);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac

if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
if (!TypeToConvert.IsAssignableFrom(typeof(Dictionary<TKey, TValue>)))
{
ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state);
}
Expand Down Expand Up @@ -110,17 +110,7 @@ protected internal override bool OnWriteResume(
return true;
}

internal override Type RuntimeType
{
get
{
if (TypeToConvert.IsAbstract || TypeToConvert.IsInterface)
{
return typeof(Dictionary<TKey, TValue>);
}

return TypeToConvert;
}
}
internal override JsonTypeInfo.ConstructorDelegate? ConstructorDelegate =>
MemberAccessor.CreateConstructor<Dictionary<TKey, TValue>>(TypeToConvert);
}
}
Loading