Skip to content

Commit

Permalink
Address review feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
layomia committed Apr 29, 2021
1 parent 050a476 commit 5d5b563
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Converters;
Expand All @@ -26,16 +25,6 @@ public sealed partial class JsonSerializerOptions
// The cached converters (custom or built-in).
private readonly ConcurrentDictionary<Type, JsonConverter?> _converters = new ConcurrentDictionary<Type, JsonConverter?>();

[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
internal void RootBuiltInConvertersAndTypeInfoCreator()
{
RootBuiltInConverters();
_typeInfoCreationFunc ??= CreateJsonTypeInfo;

[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options) => new JsonTypeInfo(type, options);
}

internal void RootBuiltInConverters()
{
s_defaultSimpleConverters ??= GetDefaultSimpleConverters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Encodings.Web;
using System.Text.Json.Node;
Expand Down Expand Up @@ -569,6 +570,16 @@ internal MemberAccessor MemberAccessorStrategy
}
}

[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
internal void RootBuiltInConvertersAndTypeInfoCreator()
{
RootBuiltInConverters();
_typeInfoCreationFunc ??= CreateJsonTypeInfo;

[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options) => new JsonTypeInfo(type, options);
}

internal JsonTypeInfo GetOrAddClass(Type type)
{
_haveTypesBeenCreated = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json.SourceGeneration.Tests;
using System.Text.Json.SourceGeneration.Tests.JsonSourceGeneration;
using Xunit;
Expand Down Expand Up @@ -468,17 +470,82 @@ public class ClassWithEnumAndNullable
}

[Fact]
public static void PolymorphicSerializationTypeNotIndicated()
public static void Converters_AndTypeInfoCreator_NotRooted_WhenMetadataNotPresent()
{
MyStruct myStruct = new();
object[] objArr = new object[] { new MyStruct() };

// Metadata not generated for MyStruct without JsonSerializableAttribute.
NotSupportedException ex = Assert.Throws<NotSupportedException>(
() => JsonSerializer.Serialize(new object[] { myStruct }, JsonContext.Default.ObjectArray));
() => JsonSerializer.Serialize(objArr, JsonContext.Default.ObjectArray));
string exAsStr = ex.ToString();
Assert.Contains(typeof(MyStruct).ToString(), exAsStr);
Assert.Contains("JsonSerializerOptions", exAsStr);

// This test uses reflection to:
// - Access JsonSerializerOptions.s_defaultSimpleConverters
// - Access JsonSerializerOptions.s_defaultFactoryConverters
// - Access JsonSerializerOptions._typeInfoCreationFunc
//
// If any of them changes, this test will need to be kept in sync.

// Confirm built-in converters not set.
AssertFieldNull("s_defaultSimpleConverters", optionsInstance: null);
AssertFieldNull("s_defaultFactoryConverters", optionsInstance: null);

// Confirm type info dynamic creator not set.
AssertFieldNull("_typeInfoCreationFunc", JsonContext.Default.Options);

static void AssertFieldNull(string fieldName, JsonSerializerOptions? optionsInstance)
{
BindingFlags bindingFlags = BindingFlags.NonPublic | (optionsInstance == null ? BindingFlags.Static : BindingFlags.Instance);
FieldInfo fieldInfo = typeof(JsonSerializerOptions).GetField(fieldName, bindingFlags);
Assert.NotNull(fieldInfo);
Assert.Null(fieldInfo.GetValue(optionsInstance));
}
}

private const string ExceptionMessageFromCustomContext = "Exception thrown from custom context.";

[Fact]
public static void GetTypeInfoCalledDuringPolymorphicSerialization()
{
CustomContext context = new(new JsonSerializerOptions());

// Empty array is fine since we don't need metadata for children.
Assert.Equal("[]", JsonSerializer.Serialize(Array.Empty<object>(), context.ObjectArray));
Assert.Equal("[]", JsonSerializer.Serialize(Array.Empty<object>(), typeof(object[]), context));

// GetTypeInfo method called to get metadata for element run-time type.
object[] objArr = new object[] { new MyStruct() };

InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(objArr, context.ObjectArray));
Assert.Contains(ExceptionMessageFromCustomContext, ex.ToString());

ex = Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(objArr, typeof(object[]), context));
Assert.Contains(ExceptionMessageFromCustomContext, ex.ToString());
}

internal struct MyStruct { }

internal class CustomContext : JsonSerializerContext
{
public CustomContext(JsonSerializerOptions options) : base(options) { }

private JsonTypeInfo<object> _object;
public JsonTypeInfo<object> Object => _object ??= JsonMetadataServices.CreateValueInfo<object>(Options, JsonMetadataServices.ObjectConverter);

private JsonTypeInfo<object[]> _objectArray;
public JsonTypeInfo<object[]> ObjectArray => _objectArray ??= JsonMetadataServices.CreateArrayInfo<object>(Options, Object, default);

public override JsonTypeInfo GetTypeInfo(Type type)
{
if (type == typeof(object[]))
{
return ObjectArray;
}

throw new InvalidOperationException(ExceptionMessageFromCustomContext);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,26 @@ public static void VerifyObjectConverterWithPreservedReferences()
Assert.IsType<bool>(obj);
Assert.Equal(true, obj);
}

[Fact]
public static void GetConverterRootsBuiltInConverters()
{
JsonSerializerOptions options = new();
RunTest<DateTime>();
RunTest<Point_2D>();

void RunTest<TConverterReturn>()
{
JsonConverter converter = options.GetConverter(typeof(TConverterReturn));
Assert.NotNull(converter);
Assert.True(converter is JsonConverter<TConverterReturn>);
}
}

[Fact]
public static void GetConverterTypeToConvertNull()
{
Assert.Throws<ArgumentNullException>(() => (new JsonSerializerOptions()).GetConverter(typeToConvert: null!));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -738,26 +738,5 @@ public static void ConverterRead_VerifyInvalidTypeToConvertFails()
}
}
}

[Fact]
public static void GetConverterRootsBuiltInConverters()
{
JsonSerializerOptions options = new();
RunTest<DateTime>();
RunTest<Point_2D>();

void RunTest<TConverterReturn>()
{
JsonConverter converter = options.GetConverter(typeof(TConverterReturn));
Assert.NotNull(converter);
Assert.True(converter is JsonConverter<TConverterReturn>);
}
}

[Fact]
public static void GetConverterTypeToConvertNull()
{
Assert.Throws<ArgumentNullException>(() => (new JsonSerializerOptions()).GetConverter(typeToConvert: null!));
}
}
}

0 comments on commit 5d5b563

Please sign in to comment.