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
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,7 @@ private static void GenerateTypeInfoFactoryHeader(SourceWriter writer, TypeGener
public {{typeInfoFQN}} {{typeInfoPropertyName}}
#nullable enable annotations
{
get => _{{typeInfoPropertyName}} ??= ({{typeInfoFQN}}){{OptionsInstanceVariableName}}.GetTypeInfo(typeof({{typeFQN}}));
get => _{{typeInfoPropertyName}} ??= {{OptionsInstanceVariableName}}.GetTypeInfo<{{typeFQN}}>();
}

private {{typeInfoFQN}} {{CreateTypeInfoMethodName(typeMetadata)}}({{JsonSerializerOptionsTypeRef}} {{OptionsLocalVariableName}})
Expand Down
2 changes: 2 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 @@ -457,11 +457,13 @@ public JsonSerializerOptions(System.Text.Json.JsonSerializerOptions options) { }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Getting a converter for a type may require reflection which depends on unreferenced code.")]
public System.Text.Json.Serialization.JsonConverter GetConverter(System.Type typeToConvert) { throw null; }
public System.Text.Json.Serialization.Metadata.JsonTypeInfo GetTypeInfo(System.Type type) { throw null; }
public System.Text.Json.Serialization.Metadata.JsonTypeInfo<T> GetTypeInfo<T>() { throw null; }
public void MakeReadOnly() { }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Populating unconfigured TypeInfoResolver properties with the reflection resolver requires runtime code generation.")]
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Populating unconfigured TypeInfoResolver properties with the reflection resolver requires unreferenced code.")]
public void MakeReadOnly(bool populateMissingResolver) { }
public bool TryGetTypeInfo(System.Type type, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Text.Json.Serialization.Metadata.JsonTypeInfo? typeInfo) { throw null; }
public bool TryGetTypeInfo<T>([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Text.Json.Serialization.Metadata.JsonTypeInfo<T>? typeInfo) { throw null; }
}
public enum JsonTokenType : byte
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ internal void AssignParent(JsonNode parent)
return JsonNodeConverter.Create(element, options);
}

var jsonTypeInfo = (JsonTypeInfo<T>)JsonSerializerOptions.Default.GetTypeInfo(typeof(T));
var jsonTypeInfo = JsonSerializerOptions.Default.GetTypeInfo<T>();
return JsonValue.CreateFromTypeInfo(value, jsonTypeInfo, options);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private protected JsonValue(JsonNodeOptions? options) : base(options) { }
return CreateFromElement(ref element, options);
}

var jsonTypeInfo = (JsonTypeInfo<T>)JsonSerializerOptions.Default.GetTypeInfo(typeof(T));
var jsonTypeInfo = JsonSerializerOptions.Default.GetTypeInfo<T>();
return CreateFromTypeInfo(value, jsonTypeInfo, options);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,29 @@ public JsonTypeInfo GetTypeInfo(Type type)
return GetTypeInfoInternal(type, resolveIfMutable: true);
}

/// <summary>
/// Gets the <see cref="JsonTypeInfo"/> contract metadata resolved by the current <see cref="JsonSerializerOptions"/> instance.
/// </summary>
/// <typeparam name="T">The type to resolve contract metadata for.</typeparam>
/// <returns>The contract metadata resolved for <typeparamref name="T"/>.</returns>
/// <exception cref="ArgumentException"><typeparamref name="T"/> is not valid for serialization.</exception>
/// <remarks>
/// Returned metadata can be downcast to <see cref="JsonTypeInfo{T}"/> and used with the relevant <see cref="JsonSerializer"/> overloads.
///
/// If the <see cref="JsonSerializerOptions"/> instance is locked for modification, the method will return a cached instance for the metadata.
/// </remarks>
public JsonTypeInfo<T> GetTypeInfo<T>()
{
Type type = typeof(T);

if (JsonTypeInfo.IsInvalidForSerialization(type))
{
ThrowHelper.ThrowArgumentException_CannotSerializeInvalidType(nameof(type), type, null, null);
}

return (JsonTypeInfo<T>)GetTypeInfoInternal(type, resolveIfMutable: true);
}

/// <summary>
/// Tries to get the <see cref="JsonTypeInfo"/> contract metadata resolved by the current <see cref="JsonSerializerOptions"/> instance.
/// </summary>
Expand All @@ -90,6 +113,31 @@ public bool TryGetTypeInfo(Type type, [NotNullWhen(true)] out JsonTypeInfo? type
return typeInfo is not null;
}

/// <summary>
/// Tries to get the <see cref="JsonTypeInfo"/> contract metadata resolved by the current <see cref="JsonSerializerOptions"/> instance.
/// </summary>
/// <typeparam name="T">The type to resolve contract metadata for.</typeparam>
/// <param name="typeInfo">The resolved contract metadata, or <see langword="null" /> if not contract could be resolved.</param>
/// <returns><see langword="true"/> if a contract for <typeparamref name="T"/> was found, or <see langword="false"/> otherwise.</returns>
/// <exception cref="ArgumentException"><typeparamref name="T"/> is not valid for serialization.</exception>
/// <remarks>
/// Returned metadata can be downcast to <see cref="JsonTypeInfo{T}"/> and used with the relevant <see cref="JsonSerializer"/> overloads.
///
/// If the <see cref="JsonSerializerOptions"/> instance is locked for modification, the method will return a cached instance for the metadata.
/// </remarks>
public bool TryGetTypeInfo<T>([NotNullWhen(true)] out JsonTypeInfo<T>? typeInfo)
{
Type type = typeof(T);

if (JsonTypeInfo.IsInvalidForSerialization(type))
{
ThrowHelper.ThrowArgumentException_CannotSerializeInvalidType(nameof(type), type, null, null);
}

typeInfo = (JsonTypeInfo<T>?)GetTypeInfoInternal(type, ensureNotNull: null, resolveIfMutable: true);
return typeInfo is not null;
}

/// <summary>
/// Same as GetTypeInfo but without validation and additional knobs.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ private sealed class CreationHandlingSetWithAttribute_PopulatedPropertyDeseriali
public readonly static CreationHandlingSetWithAttribute_PopulatedPropertyDeserializeInitiallyNull_TestBody Instance = new();
public async Task Visit<T>(JsonSerializerWrapper serializer)
{
JsonTypeInfoKind kind = serializer.DefaultOptions.GetTypeInfo(typeof(T)).Kind;
JsonTypeInfoKind kind = serializer.DefaultOptions.GetTypeInfo<T>().Kind;
Assert.True(kind is JsonTypeInfoKind.Enumerable or JsonTypeInfoKind.Dictionary);
string json = kind is JsonTypeInfoKind.Enumerable ? """{"Property":[1,2,3]}""" : """{"Property":{"a":1,"b":2,"c":3}}""";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ public virtual async Task ClassWithIgnoredAndPrivateMembers_DoesNotIncludeIgnore
{
JsonSerializerOptions options = Serializer.CreateOptions(includeFields: true);

JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(ClassWithIgnoredAndPrivateMembers));
JsonTypeInfo typeInfo = options.GetTypeInfo<ClassWithIgnoredAndPrivateMembers>();

// The contract surfaces the ignored properties but not the private ones
Assert.Equal(2, typeInfo.Properties.Count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,7 @@ ClassWithPublicGetterAndPrivateSetter obj
public async Task ProtectedMembers()
{
var options = Serializer.CreateOptions(includeFields: true);
JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(ClassWithProtectedMembers));
JsonTypeInfo typeInfo = options.GetTypeInfo<ClassWithProtectedMembers>();
Assert.Empty(typeInfo.Properties);

var value = new ClassWithProtectedMembers();
Expand Down Expand Up @@ -1171,7 +1171,7 @@ public async Task ProtectedSetter()
public async Task PrivateProtectedMembers()
{
var options = Serializer.CreateOptions(includeFields: true);
JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(ClassWithPrivateProtectedMembers));
JsonTypeInfo typeInfo = options.GetTypeInfo<ClassWithPrivateProtectedMembers>();
Assert.Empty(typeInfo.Properties);

var value = new ClassWithPrivateProtectedMembers();
Expand Down Expand Up @@ -1215,7 +1215,7 @@ public async Task PrivateProtectedSetter()
public async Task InternalProtectedMembers()
{
var options = Serializer.CreateOptions(includeFields: true);
JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(ClassWithInternalProtectedMembers));
JsonTypeInfo typeInfo = options.GetTypeInfo<ClassWithInternalProtectedMembers>();
Assert.Empty(typeInfo.Properties);

var value = new ClassWithInternalProtectedMembers();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public async Task InheritedPersonWithRequiredMembersWorksAsExpected()
var options = new JsonSerializerOptions(Serializer.DefaultOptions);
options.MakeReadOnly();

JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(InheritedPersonWithRequiredMembers));
JsonTypeInfo typeInfo = options.GetTypeInfo<InheritedPersonWithRequiredMembers>();
Assert.Equal(3, typeInfo.Properties.Count);

AssertJsonTypeInfoHasRequiredProperties(GetTypeInfo<InheritedPersonWithRequiredMembers>(options),
Expand All @@ -168,7 +168,7 @@ public async Task InheritedPersonWithRequiredMembersWithAdditionalRequiredMember
var options = new JsonSerializerOptions(Serializer.DefaultOptions);
options.MakeReadOnly();

JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(InheritedPersonWithRequiredMembersWithAdditionalRequiredMembers));
JsonTypeInfo typeInfo = options.GetTypeInfo<InheritedPersonWithRequiredMembersWithAdditionalRequiredMembers>();
Assert.Equal(4, typeInfo.Properties.Count);

AssertJsonTypeInfoHasRequiredProperties(GetTypeInfo<InheritedPersonWithRequiredMembersWithAdditionalRequiredMembers>(options),
Expand All @@ -191,7 +191,7 @@ public async Task InheritedPersonWithRequiredMembersSetsRequiredMembersWorksAsEx
var options = new JsonSerializerOptions(Serializer.DefaultOptions);
options.MakeReadOnly();

JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(InheritedPersonWithRequiredMembersSetsRequiredMembers));
JsonTypeInfo typeInfo = options.GetTypeInfo<InheritedPersonWithRequiredMembersSetsRequiredMembers>();
Assert.Equal(3, typeInfo.Properties.Count);

AssertJsonTypeInfoHasRequiredProperties(GetTypeInfo<InheritedPersonWithRequiredMembersSetsRequiredMembers>(options));
Expand Down Expand Up @@ -476,7 +476,7 @@ public async Task ClassWithRequiredFieldWorksAsExpected()
var options = new JsonSerializerOptions(Serializer.DefaultOptions) { IncludeFields = true };
options.MakeReadOnly();

JsonTypeInfo typeInfo = options.GetTypeInfo(typeof(ClassWithRequiredField));
JsonTypeInfo typeInfo = options.GetTypeInfo<ClassWithRequiredField>();
Assert.Equal(1, typeInfo.Properties.Count);

JsonPropertyInfo jsonPropertyInfo = typeInfo.Properties[0];
Expand Down Expand Up @@ -750,7 +750,7 @@ private static JsonTypeInfo GetTypeInfo<T>(JsonSerializerOptions options)
{
options.TypeInfoResolver ??= JsonSerializerOptions.Default.TypeInfoResolver;
options.MakeReadOnly();
return options.GetTypeInfo(typeof(T));
return options.GetTypeInfo<T>();
}

private static void AssertJsonTypeInfoHasRequiredProperties(JsonTypeInfo typeInfo, params string[] requiredProperties)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,15 +255,15 @@ public static void ChainedContexts_ResolveJsonTypeInfo()
{
var options = new JsonSerializerOptions { TypeInfoResolverChain = { NestedContext.Default, PersonJsonContext.Default } };

JsonTypeInfo messageInfo = options.GetTypeInfo(typeof(JsonMessage));
JsonTypeInfo messageInfo = options.GetTypeInfo<JsonMessage>();
Assert.IsAssignableFrom<JsonTypeInfo<JsonMessage>>(messageInfo);
Assert.Same(options, messageInfo.Options);

JsonTypeInfo personInfo = options.GetTypeInfo(typeof(Person));
JsonTypeInfo personInfo = options.GetTypeInfo<Person>();
Assert.IsAssignableFrom<JsonTypeInfo<Person>>(personInfo);
Assert.Same(options, personInfo.Options);

NotSupportedException exn = Assert.Throws<NotSupportedException>(() => options.GetTypeInfo(typeof(MyStruct)));
NotSupportedException exn = Assert.Throws<NotSupportedException>(() => options.GetTypeInfo<MyStruct>());
Assert.Contains(typeof(NestedContext).FullName, exn.Message);
Assert.Contains(typeof(PersonJsonContext).FullName, exn.Message);
}
Expand Down Expand Up @@ -297,7 +297,7 @@ public static void FastPathSerialization_ResolvingJsonTypeInfo()
{
JsonSerializerOptions options = FastPathSerializationContext.Default.Options;

JsonTypeInfo<JsonMessage> jsonMessageInfo = (JsonTypeInfo<JsonMessage>)options.GetTypeInfo(typeof(JsonMessage));
JsonTypeInfo<JsonMessage> jsonMessageInfo = options.GetTypeInfo<JsonMessage>();
Assert.NotNull(jsonMessageInfo.SerializeHandler);

var value = new JsonMessage { Message = "Hi" };
Expand Down Expand Up @@ -325,7 +325,7 @@ public static void FastPathSerialization_AppendedResolver_WorksAsExpected(IJsonT
TypeInfoResolver = JsonTypeInfoResolver.Combine(fastPathContext, appendedResolver, new DefaultJsonTypeInfoResolver())
};

JsonTypeInfo<PocoWithInteger> jsonMessageInfo = (JsonTypeInfo<PocoWithInteger>)options.GetTypeInfo(typeof(PocoWithInteger));
JsonTypeInfo<PocoWithInteger> jsonMessageInfo = options.GetTypeInfo<PocoWithInteger>();
Assert.NotNull(jsonMessageInfo.SerializeHandler);

var value = new PocoWithInteger { Value = 42 };
Expand All @@ -339,7 +339,7 @@ public static void FastPathSerialization_AppendedResolver_WorksAsExpected(IJsonT
Assert.Equal(expectedJson, json);
Assert.Equal(2, fastPathContext.FastPathInvocationCount);

JsonTypeInfo<ContainingClass> classInfo = (JsonTypeInfo<ContainingClass>)options.GetTypeInfo(typeof(ContainingClass));
JsonTypeInfo<ContainingClass> classInfo = options.GetTypeInfo<ContainingClass>();
Assert.Null(classInfo.SerializeHandler);

var largerValue = new ContainingClass { Message = value };
Expand Down Expand Up @@ -367,7 +367,7 @@ public static void FastPathSerialization_PrependedResolver_CompatibleResolvers_W
TypeInfoResolver = JsonTypeInfoResolver.Combine(prependedResolver, fastPathContext, new DefaultJsonTypeInfoResolver())
};

JsonTypeInfo<PocoWithInteger> jsonMessageInfo = (JsonTypeInfo<PocoWithInteger>)options.GetTypeInfo(typeof(PocoWithInteger));
JsonTypeInfo<PocoWithInteger> jsonMessageInfo = options.GetTypeInfo<PocoWithInteger>();
Assert.NotNull(jsonMessageInfo.SerializeHandler);

var value = new PocoWithInteger { Value = 42 };
Expand All @@ -381,7 +381,7 @@ public static void FastPathSerialization_PrependedResolver_CompatibleResolvers_W
Assert.Equal(expectedJson, json);
Assert.Equal(2, fastPathContext.FastPathInvocationCount);

JsonTypeInfo<ContainingClass> classInfo = (JsonTypeInfo<ContainingClass>)options.GetTypeInfo(typeof(ContainingClass));
JsonTypeInfo<ContainingClass> classInfo = options.GetTypeInfo<ContainingClass>();
Assert.Null(classInfo.SerializeHandler);

var largerValue = new ContainingClass { Message = value };
Expand Down Expand Up @@ -409,7 +409,7 @@ public static void FastPathSerialization_PrependedResolver_IncompatibleResolvers
TypeInfoResolver = JsonTypeInfoResolver.Combine(prependedResolver, fastPathContext, new DefaultJsonTypeInfoResolver())
};

JsonTypeInfo<PocoWithInteger> jsonMessageInfo = (JsonTypeInfo<PocoWithInteger>)options.GetTypeInfo(typeof(PocoWithInteger));
JsonTypeInfo<PocoWithInteger> jsonMessageInfo = options.GetTypeInfo<PocoWithInteger>();
Assert.NotNull(jsonMessageInfo.SerializeHandler);

var value = new PocoWithInteger { Value = 42 };
Expand All @@ -423,7 +423,7 @@ public static void FastPathSerialization_PrependedResolver_IncompatibleResolvers
Assert.Equal(expectedJson, json);
Assert.Equal(0, fastPathContext.FastPathInvocationCount);

JsonTypeInfo<ContainingClass> classInfo = (JsonTypeInfo<ContainingClass>)options.GetTypeInfo(typeof(ContainingClass));
JsonTypeInfo<ContainingClass> classInfo = options.GetTypeInfo<ContainingClass>();
Assert.Null(classInfo.SerializeHandler);

var largerValue = new ContainingClass { Message = value };
Expand Down Expand Up @@ -564,7 +564,7 @@ public static void ChainedContexts_Serialization<T>(T value, string expectedJson
{
var options = new JsonSerializerOptions { TypeInfoResolverChain = { NestedContext.Default, PersonJsonContext.Default } };

JsonTypeInfo<T> typeInfo = (JsonTypeInfo<T>)options.GetTypeInfo(typeof(T))!;
JsonTypeInfo<T> typeInfo = options.GetTypeInfo<T>()!;

string json = JsonSerializer.Serialize(value, typeInfo);
JsonTestHelper.AssertJsonEqual(expectedJson, json);
Expand Down Expand Up @@ -822,7 +822,7 @@ public TestResolver(Func<Type, JsonSerializerOptions, JsonTypeInfo?> getTypeInfo
public static void FastPathSerialization_EvaluatePropertyOnlyOnceWhenIgnoreNullOrDefaultIsSpecified()
{
JsonSerializerOptions options = FastPathSerializationContext.Default.Options;
JsonTypeInfo<AllocatingOnPropertyAccess> allocatingOnPropertyAccessInfo = (JsonTypeInfo<AllocatingOnPropertyAccess>)options.GetTypeInfo(typeof(AllocatingOnPropertyAccess));
JsonTypeInfo<AllocatingOnPropertyAccess> allocatingOnPropertyAccessInfo = options.GetTypeInfo<AllocatingOnPropertyAccess>();
Assert.NotNull(allocatingOnPropertyAccessInfo.SerializeHandler);

var value = new AllocatingOnPropertyAccess();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public override async Task TestCollectionWithPrivateElementType()
CollectionWithPrivateElementType collection = CollectionWithPrivateElementType.CreatePopulatedInstance();
string json = collection.GetExpectedJson();

Assert.True(Serializer.DefaultOptions.TryGetTypeInfo(typeof(CollectionWithPrivateElementType), out _));
Assert.True(Serializer.DefaultOptions.TryGetTypeInfo<CollectionWithPrivateElementType>(out _));

await Assert.ThrowsAsync<NotSupportedException>(() => Serializer.SerializeWrapper(collection));
await Assert.ThrowsAsync<NotSupportedException>(() => Serializer.DeserializeWrapper<CollectionWithPrivateElementType>(json));
Expand All @@ -165,7 +165,7 @@ public override async Task TestDictionaryWithPrivateKeyAndValueType()
DictionaryWithPrivateKeyAndValueType dictionary = DictionaryWithPrivateKeyAndValueType.CreatePopulatedInstance();
string json = dictionary.GetExpectedJson();

Assert.True(Serializer.DefaultOptions.TryGetTypeInfo(typeof(DictionaryWithPrivateKeyAndValueType), out _));
Assert.True(Serializer.DefaultOptions.TryGetTypeInfo<DictionaryWithPrivateKeyAndValueType>(out _));

await Assert.ThrowsAsync<NotSupportedException>(() => Serializer.SerializeWrapper(dictionary));
await Assert.ThrowsAsync<NotSupportedException>(() => Serializer.DeserializeWrapper<DictionaryWithPrivateKeyAndValueType>(json));
Expand Down
Loading
Loading