Skip to content

Commit 32d0360

Browse files
Refactor JsonPropertyInfo initialization infrastructure and implement JsonPropertyInfo.AttributeProvider (#71514)
* Implement JsonPropertyInfo.AttributeProvider and refactor initialization infrastructure * Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs * Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs * _shouldSerializeIsExplicitlySet set to false when setting IgnoreCondition * Reinstante _converterIsExternalAndPolymorphic * Move unique attribute resolution logic to reflection extensions * address feedback
1 parent 7496163 commit 32d0360

16 files changed

+337
-493
lines changed

src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,24 @@ public static bool IsAssignableFromInternal(this Type type, Type from)
4343

4444
private static bool HasJsonConstructorAttribute(ConstructorInfo constructorInfo)
4545
=> constructorInfo.GetCustomAttribute<JsonConstructorAttribute>() != null;
46+
47+
public static TAttribute? GetUniqueCustomAttribute<TAttribute>(this MemberInfo memberInfo, bool inherit)
48+
where TAttribute : Attribute
49+
{
50+
object[] attributes = memberInfo.GetCustomAttributes(typeof(TAttribute), inherit);
51+
52+
if (attributes.Length == 0)
53+
{
54+
return null;
55+
}
56+
57+
if (attributes.Length == 1)
58+
{
59+
return (TAttribute)attributes[0];
60+
}
61+
62+
ThrowHelper.ThrowInvalidOperationException_SerializationDuplicateAttribute(typeof(TAttribute), memberInfo);
63+
return null;
64+
}
4665
}
4766
}

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ internal virtual void ReadElementAndSetProperty(
6767
throw new InvalidOperationException(SR.NodeJsonObjectCustomConverterNotAllowedOnExtensionProperty);
6868
}
6969

70-
internal abstract JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo parentTypeInfo);
70+
internal abstract JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo declaringTypeInfo, Type propertyType, JsonSerializerOptions options);
7171

7272
internal abstract JsonParameterInfo CreateJsonParameterInfo();
7373

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ protected JsonConverterFactory() { }
3232
/// </returns>
3333
public abstract JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options);
3434

35-
internal override JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo parentTypeInfo)
35+
internal override JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo declaringTypeInfo, Type propertyType, JsonSerializerOptions options)
3636
{
3737
Debug.Fail("We should never get here.");
3838

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ public override bool CanConvert(Type typeToConvert)
6969

7070
internal override ConverterStrategy ConverterStrategy => ConverterStrategy.Value;
7171

72-
internal sealed override JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo parentTypeInfo)
72+
internal sealed override JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo declaringTypeInfo, Type propertyType, JsonSerializerOptions options)
7373
{
74-
return new JsonPropertyInfo<T>(parentTypeInfo);
74+
return new JsonPropertyInfo<T>(declaringTypeInfo.Type, propertyType, declaringTypeInfo, options);
7575
}
7676

7777
internal sealed override JsonParameterInfo CreateJsonParameterInfo()

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs

Lines changed: 17 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,15 @@ public sealed partial class JsonSerializerOptions
2727
public IList<JsonConverter> Converters => _converters;
2828

2929
// This may return factory converter
30-
internal JsonConverter? GetCustomConverterFromMember(Type? parentClassType, Type typeToConvert, MemberInfo? memberInfo)
30+
internal JsonConverter? GetCustomConverterFromMember(Type typeToConvert, MemberInfo memberInfo)
3131
{
32+
Debug.Assert(memberInfo.DeclaringType != null, "Properties and fields always have a declaring type.");
3233
JsonConverter? converter = null;
3334

34-
if (memberInfo != null)
35+
JsonConverterAttribute? converterAttribute = memberInfo.GetUniqueCustomAttribute<JsonConverterAttribute>(inherit: false);
36+
if (converterAttribute != null)
3537
{
36-
Debug.Assert(parentClassType != null);
37-
38-
JsonConverterAttribute? converterAttribute = (JsonConverterAttribute?)
39-
GetAttributeThatCanHaveMultiple(parentClassType!, typeof(JsonConverterAttribute), memberInfo);
40-
41-
if (converterAttribute != null)
42-
{
43-
converter = GetConverterFromAttribute(converterAttribute, typeToConvert, classTypeAttributeIsOn: parentClassType!, memberInfo);
44-
}
38+
converter = GetConverterFromAttribute(converterAttribute, typeToConvert, memberInfo);
4539
}
4640

4741
return converter;
@@ -180,12 +174,10 @@ private JsonConverter GetConverterFromOptionsOrReflectionConverter(Type typeToCo
180174
// Priority 2: Attempt to get converter from [JsonConverter] on the type being converted.
181175
if (converter == null)
182176
{
183-
JsonConverterAttribute? converterAttribute = (JsonConverterAttribute?)
184-
GetAttributeThatCanHaveMultiple(typeToConvert, typeof(JsonConverterAttribute));
185-
177+
JsonConverterAttribute? converterAttribute = typeToConvert.GetUniqueCustomAttribute<JsonConverterAttribute>(inherit: false);
186178
if (converterAttribute != null)
187179
{
188-
converter = GetConverterFromAttribute(converterAttribute, typeToConvert: typeToConvert, classTypeAttributeIsOn: typeToConvert, memberInfo: null);
180+
converter = GetConverterFromAttribute(converterAttribute, typeToConvert: typeToConvert, memberInfo: null);
189181
}
190182
}
191183

@@ -215,29 +207,30 @@ private JsonConverter GetConverterFromOptionsOrReflectionConverter(Type typeToCo
215207
// This suppression needs to be removed. https://github.com/dotnet/runtime/issues/68878
216208
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "The factory constructors are only invoked in the context of reflection serialization code paths " +
217209
"and are marked RequiresDynamicCode")]
218-
private JsonConverter GetConverterFromAttribute(JsonConverterAttribute converterAttribute, Type typeToConvert, Type classTypeAttributeIsOn, MemberInfo? memberInfo)
210+
private JsonConverter GetConverterFromAttribute(JsonConverterAttribute converterAttribute, Type typeToConvert, MemberInfo? memberInfo)
219211
{
220212
JsonConverter? converter;
221213

222-
Type? type = converterAttribute.ConverterType;
223-
if (type == null)
214+
Type declaringType = memberInfo?.DeclaringType ?? typeToConvert;
215+
Type? converterType = converterAttribute.ConverterType;
216+
if (converterType == null)
224217
{
225218
// Allow the attribute to create the converter.
226219
converter = converterAttribute.CreateConverter(typeToConvert);
227220
if (converter == null)
228221
{
229-
ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(classTypeAttributeIsOn, memberInfo, typeToConvert);
222+
ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(declaringType, memberInfo, typeToConvert);
230223
}
231224
}
232225
else
233226
{
234-
ConstructorInfo? ctor = type.GetConstructor(Type.EmptyTypes);
235-
if (!typeof(JsonConverter).IsAssignableFrom(type) || ctor == null || !ctor.IsPublic)
227+
ConstructorInfo? ctor = converterType.GetConstructor(Type.EmptyTypes);
228+
if (!typeof(JsonConverter).IsAssignableFrom(converterType) || ctor == null || !ctor.IsPublic)
236229
{
237-
ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeInvalid(classTypeAttributeIsOn, memberInfo);
230+
ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeInvalid(declaringType, memberInfo);
238231
}
239232

240-
converter = (JsonConverter)Activator.CreateInstance(type)!;
233+
converter = (JsonConverter)Activator.CreateInstance(converterType)!;
241234
}
242235

243236
Debug.Assert(converter != null);
@@ -255,38 +248,10 @@ private JsonConverter GetConverterFromAttribute(JsonConverterAttribute converter
255248
return NullableConverterFactory.CreateValueConverter(underlyingType, converter);
256249
}
257250

258-
ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(classTypeAttributeIsOn, memberInfo, typeToConvert);
251+
ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(declaringType, memberInfo, typeToConvert);
259252
}
260253

261254
return converter;
262255
}
263-
264-
private static Attribute? GetAttributeThatCanHaveMultiple(Type classType, Type attributeType, MemberInfo memberInfo)
265-
{
266-
object[] attributes = memberInfo.GetCustomAttributes(attributeType, inherit: false);
267-
return GetAttributeThatCanHaveMultiple(attributeType, classType, memberInfo, attributes);
268-
}
269-
270-
internal static Attribute? GetAttributeThatCanHaveMultiple(Type classType, Type attributeType)
271-
{
272-
object[] attributes = classType.GetCustomAttributes(attributeType, inherit: false);
273-
return GetAttributeThatCanHaveMultiple(attributeType, classType, null, attributes);
274-
}
275-
276-
private static Attribute? GetAttributeThatCanHaveMultiple(Type attributeType, Type classType, MemberInfo? memberInfo, object[] attributes)
277-
{
278-
if (attributes.Length == 0)
279-
{
280-
return null;
281-
}
282-
283-
if (attributes.Length == 1)
284-
{
285-
return (Attribute)attributes[0];
286-
}
287-
288-
ThrowHelper.ThrowInvalidOperationException_SerializationDuplicateAttribute(attributeType, classType, memberInfo);
289-
return default;
290-
}
291256
}
292257
}

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/CustomJsonTypeInfoOfT.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,7 @@ internal CustomJsonTypeInfo(JsonSerializerOptions options)
3131
private static JsonConverter GetConverter(JsonSerializerOptions options)
3232
{
3333
DefaultJsonTypeInfoResolver.RootDefaultInstance();
34-
return GetEffectiveConverter(
35-
typeof(T),
36-
parentClassType: null, // A TypeInfo never has a "parent" class.
37-
memberInfo: null, // A TypeInfo never has a "parent" property.
38-
options);
34+
return options.GetConverterForType(typeof(T));
3935
}
4036

4137
internal override JsonParameterInfoValues[] GetParameterInfoValues()

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public virtual JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options
5050

5151
_mutable = false;
5252

53-
JsonTypeInfo.ValidateType(type, null, null, options);
53+
JsonTypeInfo.ValidateType(type);
5454
JsonTypeInfo typeInfo = CreateJsonTypeInfo(type, options);
5555

5656
if (_modifiers != null)

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ public static JsonPropertyInfo CreatePropertyInfo<T>(JsonSerializerOptions optio
4848
throw new InvalidOperationException(SR.Format(SR.FieldCannotBeVirtual, nameof(propertyInfo.IsProperty), nameof(propertyInfo.IsVirtual)));
4949
}
5050

51-
JsonPropertyInfo<T> jsonPropertyInfo = new JsonPropertyInfo<T>(parentTypeInfo: null);
52-
jsonPropertyInfo.InitializeForSourceGen(options, propertyInfo);
53-
return jsonPropertyInfo;
51+
return new JsonPropertyInfo<T>(options, propertyInfo);
5452
}
5553

5654
/// <summary>

0 commit comments

Comments
 (0)