Skip to content
Merged
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
2 changes: 1 addition & 1 deletion src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ public JsonSerializerOptions() { }
public System.Text.Json.JsonCommentHandling ReadCommentHandling { get { throw null; } set { } }
public System.Text.Json.Serialization.ReferenceHandling ReferenceHandling { get { throw null; } set { } }
public bool WriteIndented { get { throw null; } set { } }
public System.Text.Json.Serialization.JsonConverter? GetConverter(System.Type typeToConvert) { throw null; }
public System.Text.Json.Serialization.JsonConverter GetConverter(System.Type typeToConvert) { throw null; }
}
public sealed partial class JsonString : System.Text.Json.JsonNode, System.IEquatable<System.Text.Json.JsonString>
{
Expand Down
9 changes: 6 additions & 3 deletions src/libraries/System.Text.Json/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,6 @@
<data name="SerializationNotSupportedType" xml:space="preserve">
<value>The type '{0}' is not supported.</value>
</data>
<data name="SerializationNotSupported" xml:space="preserve">
<value>The type '{0}' on '{1}' is not supported.</value>
</data>
<data name="InvalidCharacterAtStartOfComment" xml:space="preserve">
<value>'{0}' is invalid after '/' at the beginning of the comment. Expected either '/' or '*'.</value>
</data>
Expand Down Expand Up @@ -485,4 +482,10 @@
<data name="MetadataInvalidPropertyWithLeadingDollarSign" xml:space="preserve">
<value>Properties that start with '$' are not allowed on preserve mode, either escape the character or turn off preserve references by setting ReferenceHandling to ReferenceHandling.Default.</value>
</data>
<data name="SerializerConverterFactoryReturnsNull" xml:space="preserve">
<value>The converter '{0}' cannot return a null value.</value>
</data>
<data name="SerializationNotSupportedParentType" xml:space="preserve">
<value>The unsupported member type is located on type '{0}'.</value>
</data>
</root>
4 changes: 2 additions & 2 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
<Compile Include="System\Text\Json\Serialization\JsonConverterAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\JsonConverterFactory.cs" />
<Compile Include="System\Text\Json\Serialization\JsonConverterOfT.cs" />
<Compile Include="System\Text\Json\Serialization\JsonConverterOfT.ReadCore.cs" />
<Compile Include="System\Text\Json\Serialization\JsonConverterOfT.WriteCore.cs" />
<Compile Include="System\Text\Json\Serialization\JsonDefaultNamingPolicy.cs" />
<Compile Include="System\Text\Json\Serialization\JsonDictionaryConverter.cs" />
<Compile Include="System\Text\Json\Serialization\JsonExtensionDataAttribute.cs" />
Expand All @@ -129,7 +131,6 @@
<Compile Include="System\Text\Json\Serialization\JsonPropertyInfoOfTTypeToConvert.cs" />
<Compile Include="System\Text\Json\Serialization\JsonPropertyNameAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\JsonResumableConverterOfT.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.HandleMetadata.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.HandlePropertyName.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.Helpers.cs" />
Expand All @@ -138,7 +139,6 @@
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.String.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.Utf8JsonReader.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.ByteArray.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.HandleMetadata.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Helpers.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Stream.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ public static bool TryGetUnescapedBase64Bytes(ReadOnlySpan<byte> utf8Source, int
// TODO: Similar to escaping, replace the unescaping logic with publicly shipping APIs from https://github.com/dotnet/runtime/issues/27919
public static string GetUnescapedString(ReadOnlySpan<byte> utf8Source, int idx)
{
byte[]? unescapedArray = null;
// The escaped name is always >= than the unescaped, so it is safe to use escaped name for the buffer length.
int length = utf8Source.Length;
byte[]? pooledName = null;

Span<byte> utf8Unescaped = utf8Source.Length <= JsonConstants.StackallocThreshold ?
stackalloc byte[utf8Source.Length] :
(unescapedArray = ArrayPool<byte>.Shared.Rent(utf8Source.Length));
Span<byte> utf8Unescaped = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
(pooledName = ArrayPool<byte>.Shared.Rent(length));

Unescape(utf8Source, utf8Unescaped, idx, out int written);
Debug.Assert(written > 0);
Expand All @@ -56,15 +58,40 @@ public static string GetUnescapedString(ReadOnlySpan<byte> utf8Source, int idx)

string utf8String = TranscodeHelper(utf8Unescaped);

if (unescapedArray != null)
if (pooledName != null)
{
utf8Unescaped.Clear();
ArrayPool<byte>.Shared.Return(unescapedArray);
ArrayPool<byte>.Shared.Return(pooledName);
}

return utf8String;
}

public static ReadOnlySpan<byte> GetUnescapedSpan(ReadOnlySpan<byte> utf8Source, int idx)
{
// The escaped name is always >= than the unescaped, so it is safe to use escaped name for the buffer length.
int length = utf8Source.Length;
byte[]? pooledName = null;

Span<byte> utf8Unescaped = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
(pooledName = ArrayPool<byte>.Shared.Rent(length));

Unescape(utf8Source, utf8Unescaped, idx, out int written);
Debug.Assert(written > 0);

ReadOnlySpan<byte> propertyName = utf8Unescaped.Slice(0, written).ToArray();
Debug.Assert(!propertyName.IsEmpty);

if (pooledName != null)
{
new Span<byte>(pooledName, 0, written).Clear();
ArrayPool<byte>.Shared.Return(pooledName);
}

return propertyName;
}

public static bool UnescapeAndCompare(ReadOnlySpan<byte> utf8Source, ReadOnlySpan<byte> other)
{
Debug.Assert(utf8Source.Length >= other.Length && utf8Source.Length / JsonConstants.MaxExpansionFactorWhileEscaping <= other.Length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ namespace System.Text.Json
/// </remarks>
internal enum ClassType : byte
{
// Default - no class type.
None = 0x0,
// JsonObjectConverter<> - objects with properties.
Object = 0x1,
// JsonConverter<> - simple values.
Expand All @@ -23,7 +25,5 @@ internal enum ClassType : byte
Enumerable = 0x8,
// JsonDictionaryConverter<,> - dictionary types.
Dictionary = 0x10,
// Invalid (not used directly for serialization)
Invalid = 0x20
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace System.Text.Json.Serialization.Converters
/// <summary>
/// Converter factory for all IEnumerable types.
/// </summary>
internal class JsonIEnumerableConverterFactory : JsonConverterFactory
internal class IEnumerableConverterFactory : JsonConverterFactory
{
private static readonly IDictionaryConverter<IDictionary> s_converterForIDictionary = new IDictionaryConverter<IDictionary>();
private static readonly IEnumerableConverter<IEnumerable> s_converterForIEnumerable = new IEnumerableConverter<IEnumerable>();
Expand Down Expand Up @@ -43,10 +43,9 @@ public override bool CanConvert(Type typeToConvert)
[PreserveDependency(".ctor", "System.Text.Json.Serialization.Converters.ListOfTConverter`2")]
[PreserveDependency(".ctor", "System.Text.Json.Serialization.Converters.QueueOfTConverter`2")]
[PreserveDependency(".ctor", "System.Text.Json.Serialization.Converters.StackOfTConverter`2")]
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
JsonConverter? converter = null;
Type converterType;
Type converterType = null!;
Type[] genericArgs;
Type? elementType = null;
Type? actualTypeToConvert;
Expand All @@ -57,7 +56,7 @@ public override bool CanConvert(Type typeToConvert)
// Verify that we don't have a multidimensional array.
if (typeToConvert.GetArrayRank() > 1)
{
return null;
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(typeToConvert);
}

converterType = typeof(ArrayConverter<,>);
Expand All @@ -80,7 +79,7 @@ public override bool CanConvert(Type typeToConvert)
}
else
{
return null;
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(typeToConvert);
}
}
// Immutable dictionaries from System.Collections.Immutable, e.g. ImmutableDictionary<string, TValue>
Expand All @@ -94,7 +93,7 @@ public override bool CanConvert(Type typeToConvert)
}
else
{
return null;
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(typeToConvert);
}
}
// IDictionary<string,> or deriving from IDictionary<string,>
Expand All @@ -108,7 +107,7 @@ public override bool CanConvert(Type typeToConvert)
}
else
{
return null;
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(typeToConvert);
}
}
// IReadOnlyDictionary<string,> or deriving from IReadOnlyDictionary<string,>
Expand All @@ -122,7 +121,7 @@ public override bool CanConvert(Type typeToConvert)
}
else
{
return null;
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(typeToConvert);
}
}
// Immutable non-dictionaries from System.Collections.Immutable, e.g. ImmutableStack<T>
Expand Down Expand Up @@ -213,25 +212,24 @@ public override bool CanConvert(Type typeToConvert)
converterType = typeof(IEnumerableConverter<>);
}

if (converterType != null)
{
Type genericType;
if (converterType.GetGenericArguments().Length == 1)
{
genericType = converterType.MakeGenericType(typeToConvert);
}
else
{
genericType = converterType.MakeGenericType(typeToConvert, elementType!);
}
Debug.Assert(converterType != null);

converter = (JsonConverter)Activator.CreateInstance(
genericType,
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: null,
culture: null)!;
Type genericType;
if (converterType.GetGenericArguments().Length == 1)
{
genericType = converterType.MakeGenericType(typeToConvert);
}
else
{
genericType = converterType.MakeGenericType(typeToConvert, elementType!);
}

JsonConverter converter = (JsonConverter)Activator.CreateInstance(
genericType,
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: null,
culture: null)!;

return converter;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace System.Text.Json.Serialization.Converters
/// <summary>
/// Default base class implementation of <cref>JsonObjectConverter{T}</cref>.
/// </summary>
internal sealed class ObjectDefaultConverter<T> : JsonObjectConverter<T>
internal sealed class ObjectDefaultConverter<T> : JsonObjectConverter<T> where T : notnull
{
internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, [MaybeNullWhen(false)] out T value)
{
Expand Down Expand Up @@ -273,16 +273,10 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert,
internal override bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state)
{
// Minimize boxing for structs by only boxing once here
object? objectValue = value;
object objectValue = value!;

if (!state.SupportContinuation)
{
if (objectValue == null)
{
writer.WriteNullValue();
return true;
}

writer.WriteStartObject();

if (options.ReferenceHandling.ShouldWritePreservedReferences())
Expand Down Expand Up @@ -338,12 +332,6 @@ internal override bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializer
{
if (!state.Current.ProcessedStartToken)
{
if (objectValue == null)
{
writer.WriteNullValue();
return true;
}

writer.WriteStartObject();

if (options.ReferenceHandling.ShouldWritePreservedReferences())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,8 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer

Type valueTypeToConvert = typeToConvert.GetGenericArguments()[0];

JsonConverter? valueConverter = options.GetConverter(valueTypeToConvert);
if (valueConverter == null)
{
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(valueTypeToConvert);
}
JsonConverter valueConverter = options.GetConverter(valueTypeToConvert);
Debug.Assert(valueConverter != null);

return CreateValueConverter(valueTypeToConvert, valueConverter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,19 @@ private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInf
return JsonPropertyInfo.CreateIgnoredPropertyPlaceholder(propertyInfo, options);
}

JsonConverter? converter;
ClassType classType = GetClassType(
JsonConverter converter = GetConverter(
propertyType,
parentClassType,
propertyInfo,
out Type? runtimeType,
out Type? _,
out converter,
out Type runtimeType,
options);

if (converter == null)
{
ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(propertyType, parentClassType, propertyInfo);
}

return CreateProperty(
declaredPropertyType: propertyType,
runtimePropertyType: runtimeType,
propertyInfo,
parentClassType,
converter,
classType,
options);
}

Expand All @@ -48,7 +39,6 @@ internal static JsonPropertyInfo CreateProperty(
PropertyInfo? propertyInfo,
Type parentClassType,
JsonConverter converter,
ClassType classType,
JsonSerializerOptions options)
{
// Create the JsonPropertyInfo instance.
Expand All @@ -58,7 +48,7 @@ internal static JsonPropertyInfo CreateProperty(
parentClassType,
declaredPropertyType,
runtimePropertyType,
runtimeClassType: classType,
runtimeClassType: converter.ClassType,
propertyInfo,
converter,
options);
Expand All @@ -74,9 +64,8 @@ internal static JsonPropertyInfo CreateProperty(
/// </summary>
internal static JsonPropertyInfo CreatePolicyProperty(
Type declaredPropertyType,
Type? runtimePropertyType,
Type runtimePropertyType,
JsonConverter converter,
ClassType classType,
JsonSerializerOptions options)
{
return CreateProperty(
Expand All @@ -85,7 +74,6 @@ internal static JsonPropertyInfo CreatePolicyProperty(
propertyInfo: null, // Not a real property so this is null.
parentClassType: typeof(object), // a dummy value (not used)
converter : converter,
classType : classType,
options);
}
}
Expand Down
Loading