Skip to content

Commit f70ab07

Browse files
authored
chore: Update YamlDotNet version to 16.3.0 (#10541)
* chore: update yamldotnet version * chore: update ITypeInspector's derived method * chore: update IPropertyDescriptor derived methods * chore: update YamlDeserializer * chore: update YamlSerializer * chore: update IObjectGraphTraversalStrategy derived methods * chore: update INodeDeserializer derived methods * chore: update IObjectGraphVisitor derived methods
1 parent 7d095d6 commit f70ab07

17 files changed

+602
-211
lines changed

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<PackageVersion Include="System.Composition" Version="9.0.2" />
2323
<PackageVersion Include="System.Formats.Asn1" Version="9.0.2" />
2424
<PackageVersion Include="System.Text.Json" Version="9.0.2" />
25-
<PackageVersion Include="YamlDotNet" Version="15.3.0" />
25+
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
2626
</ItemGroup>
2727

2828
<!-- .slnx solution format is supported Microsoft.Build 17.13.9 or later. -->

src/Docfx.YamlSerialization/Helpers/ReflectionExtensions.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ internal static class ReflectionExtensions
1111
/// Determines whether the specified type has a default constructor.
1212
/// </summary>
1313
/// <param name="type">The type.</param>
14+
/// <param name="allowPrivateConstructors">Allow private constructor.</param>
1415
/// <returns>
1516
/// <c>true</c> if the type has a default constructor; otherwise, <c>false</c>.
1617
/// </returns>
17-
public static bool HasDefaultConstructor(this Type type)
18+
public static bool HasDefaultConstructor(this Type type, bool allowPrivateConstructors)
1819
{
19-
return type.IsValueType || type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null) != null;
20+
var bindingFlags = BindingFlags.Public | BindingFlags.Instance;
21+
if (allowPrivateConstructors)
22+
{
23+
bindingFlags |= BindingFlags.NonPublic;
24+
}
25+
return type.IsValueType || type.GetConstructor(bindingFlags, null, Type.EmptyTypes, null) != null;
2026
}
2127

2228
public static IEnumerable<PropertyInfo> GetPublicProperties(this Type type)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Concurrent;
5+
using System.Diagnostics.CodeAnalysis;
6+
using YamlDotNet.Serialization;
7+
8+
namespace Docfx.YamlSerialization.Helpers;
9+
10+
/// <summary>
11+
/// A cache / map for <see cref="IYamlTypeConverter"/> instances.
12+
/// </summary>
13+
/// <remarks>
14+
/// This class is copied from following YamlDotNet implementation.
15+
/// https://github.com/aaubry/YamlDotNet/blob/master/YamlDotNet/Serialization/Utilities/TypeConverterCache.cs
16+
/// </remarks>
17+
internal sealed class TypeConverterCache
18+
{
19+
private readonly IYamlTypeConverter[] typeConverters;
20+
private readonly ConcurrentDictionary<Type, (bool HasMatch, IYamlTypeConverter? TypeConverter)> cache = new();
21+
22+
public TypeConverterCache(IEnumerable<IYamlTypeConverter>? typeConverters)
23+
: this(typeConverters?.ToArray() ?? [])
24+
{
25+
}
26+
27+
public TypeConverterCache(IYamlTypeConverter[] typeConverters)
28+
{
29+
this.typeConverters = typeConverters;
30+
}
31+
32+
/// <summary>
33+
/// Returns the first <see cref="IYamlTypeConverter"/> that accepts the given type.
34+
/// </summary>
35+
/// <param name="type">The <see cref="Type"/> to lookup.</param>
36+
/// <param name="typeConverter">The <see cref="IYamlTypeConverter" /> that accepts this type or <see langword="false" /> if no converter is found.</param>
37+
/// <returns><see langword="true"/> if a type converter was found; <see langword="false"/> otherwise.</returns>
38+
public bool TryGetConverterForType(Type type, [NotNullWhen(true)] out IYamlTypeConverter? typeConverter)
39+
{
40+
var result = cache.GetOrAdd(type, static (t, tc) => LookupTypeConverter(t, tc), typeConverters);
41+
42+
typeConverter = result.TypeConverter;
43+
return result.HasMatch;
44+
}
45+
46+
/// <summary>
47+
/// Returns the <see cref="IYamlTypeConverter"/> of the given type.
48+
/// </summary>
49+
/// <param name="converter">The type of the converter.</param>
50+
/// <returns>The <see cref="IYamlTypeConverter"/> of the given type.</returns>
51+
/// <exception cref="ArgumentException">If no type converter of the given type is found.</exception>
52+
/// <remarks>
53+
/// Note that this method searches on the type of the <see cref="IYamlTypeConverter"/> itself. If you want to find a type converter
54+
/// that accepts a given <see cref="Type"/>, use <see cref="TryGetConverterForType(Type, out IYamlTypeConverter?)"/> instead.
55+
/// </remarks>
56+
public IYamlTypeConverter GetConverterByType(Type converter)
57+
{
58+
// Intentially avoids LINQ as this is on a hot path
59+
foreach (var typeConverter in typeConverters)
60+
{
61+
if (typeConverter.GetType() == converter)
62+
{
63+
return typeConverter;
64+
}
65+
}
66+
67+
throw new ArgumentException($"{nameof(IYamlTypeConverter)} of type {converter.FullName} not found", nameof(converter));
68+
}
69+
70+
private static (bool HasMatch, IYamlTypeConverter? TypeConverter) LookupTypeConverter(Type type, IYamlTypeConverter[] typeConverters)
71+
{
72+
// Intentially avoids LINQ as this is on a hot path
73+
foreach (var typeConverter in typeConverters)
74+
{
75+
if (typeConverter.Accepts(type))
76+
{
77+
return (true, typeConverter);
78+
}
79+
}
80+
81+
return (false, null);
82+
}
83+
}

src/Docfx.YamlSerialization/NodeDeserializers/EmitArrayNodeDeserializer.cs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,22 @@ namespace Docfx.YamlSerialization.NodeDeserializers;
1212

1313
public class EmitArrayNodeDeserializer : INodeDeserializer
1414
{
15+
private readonly INamingConvention _enumNamingConvention;
16+
private readonly ITypeInspector _typeDescriptor;
17+
1518
private static readonly MethodInfo DeserializeHelperMethod =
1619
typeof(EmitArrayNodeDeserializer).GetMethod(nameof(DeserializeHelper))!;
17-
private static readonly ConcurrentDictionary<Type, Func<IParser, Type, Func<IParser, Type, object?>, object?>> _funcCache =
20+
21+
private static readonly ConcurrentDictionary<Type, Func<IParser, Type, Func<IParser, Type, object?>, INamingConvention, ITypeInspector, object?>> _funcCache =
1822
new();
1923

20-
bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value)
24+
public EmitArrayNodeDeserializer(INamingConvention enumNamingConvention, ITypeInspector typeDescriptor)
25+
{
26+
_enumNamingConvention = enumNamingConvention;
27+
_typeDescriptor = typeDescriptor;
28+
}
29+
30+
bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer)
2131
{
2232
if (!expectedType.IsArray)
2333
{
@@ -26,27 +36,44 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IPars
2636
}
2737

2838
var func = _funcCache.GetOrAdd(expectedType, AddItem);
29-
value = func(reader, expectedType, nestedObjectDeserializer);
39+
value = func(reader, expectedType, nestedObjectDeserializer, _enumNamingConvention, _typeDescriptor);
3040
return true;
3141
}
3242

3343
[EditorBrowsable(EditorBrowsableState.Never)]
34-
public static TItem[] DeserializeHelper<TItem>(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer)
44+
public static TItem[] DeserializeHelper<TItem>(
45+
IParser reader,
46+
Type expectedType,
47+
Func<IParser, Type, object?> nestedObjectDeserializer,
48+
INamingConvention enumNamingConvention,
49+
ITypeInspector typeDescriptor)
3550
{
3651
var items = new List<TItem>();
37-
EmitGenericCollectionNodeDeserializer.DeserializeHelper(reader, expectedType, nestedObjectDeserializer, items);
52+
EmitGenericCollectionNodeDeserializer.DeserializeHelper(reader, expectedType, nestedObjectDeserializer, items, enumNamingConvention, typeDescriptor);
3853
return items.ToArray();
3954
}
4055

41-
private static Func<IParser, Type, Func<IParser, Type, object?>, object?> AddItem(Type expectedType)
56+
private static Func<IParser, Type, Func<IParser, Type, object?>, INamingConvention, ITypeInspector, object?> AddItem(Type expectedType)
4257
{
43-
var dm = new DynamicMethod(string.Empty, typeof(object), [typeof(IParser), typeof(Type), typeof(Func<IParser, Type, object>)]);
58+
var dm = new DynamicMethod(
59+
string.Empty,
60+
returnType: typeof(object),
61+
parameterTypes:
62+
[
63+
typeof(IParser), // reader
64+
typeof(Type), // expectedType
65+
typeof(Func<IParser, Type, object?>), // nestedObjectDeserializer
66+
typeof(INamingConvention), // enumNamingConvention
67+
typeof(ITypeInspector), // typeDescriptor
68+
]);
4469
var il = dm.GetILGenerator();
4570
il.Emit(OpCodes.Ldarg_0);
4671
il.Emit(OpCodes.Ldarg_1);
4772
il.Emit(OpCodes.Ldarg_2);
73+
il.Emit(OpCodes.Ldarg_3);
74+
il.Emit(OpCodes.Ldarg_S, (byte)4);
4875
il.Emit(OpCodes.Call, DeserializeHelperMethod.MakeGenericMethod(expectedType.GetElementType()!));
4976
il.Emit(OpCodes.Ret);
50-
return (Func<IParser, Type, Func<IParser, Type, object?>, object?>)dm.CreateDelegate(typeof(Func<IParser, Type, Func<IParser, Type, object?>, object?>));
77+
return (Func<IParser, Type, Func<IParser, Type, object?>, INamingConvention, ITypeInspector, object?>)dm.CreateDelegate(typeof(Func<IParser, Type, Func<IParser, Type, object?>, INamingConvention, ITypeInspector, object?>));
5178
}
5279
}

src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericCollectionNodeDeserializer.cs

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using YamlDotNet.Core;
88
using YamlDotNet.Core.Events;
99
using YamlDotNet.Serialization;
10-
using YamlDotNet.Serialization.NamingConventions;
1110
using YamlDotNet.Serialization.Utilities;
1211
using EditorBrowsable = System.ComponentModel.EditorBrowsableAttribute;
1312
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
@@ -19,15 +18,21 @@ public class EmitGenericCollectionNodeDeserializer : INodeDeserializer
1918
private static readonly MethodInfo DeserializeHelperMethod =
2019
typeof(EmitGenericCollectionNodeDeserializer).GetMethod(nameof(DeserializeHelper))!;
2120
private readonly IObjectFactory _objectFactory;
22-
private readonly Dictionary<Type, Type?> _gpCache = [];
23-
private readonly Dictionary<Type, Action<IParser, Type, Func<IParser, Type, object?>, object?>> _actionCache = [];
21+
private readonly INamingConvention _enumNamingConvention;
22+
private readonly ITypeInspector _typeDescriptor;
23+
private readonly Dictionary<Type, Type?> _gpCache =
24+
new();
25+
private readonly Dictionary<Type, Action<IParser, Type, Func<IParser, Type, object?>, object?, INamingConvention, ITypeInspector>> _actionCache =
26+
new();
2427

25-
public EmitGenericCollectionNodeDeserializer(IObjectFactory objectFactory)
28+
public EmitGenericCollectionNodeDeserializer(IObjectFactory objectFactory, INamingConvention enumNamingConvention, ITypeInspector typeDescriptor)
2629
{
2730
_objectFactory = objectFactory;
31+
_enumNamingConvention = enumNamingConvention;
32+
_typeDescriptor = typeDescriptor;
2833
}
2934

30-
bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value)
35+
bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer)
3136
{
3237
if (!_gpCache.TryGetValue(expectedType, out var gp))
3338
{
@@ -55,39 +60,58 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IPars
5560
value = _objectFactory.Create(expectedType);
5661
if (!_actionCache.TryGetValue(gp, out var action))
5762
{
58-
var dm = new DynamicMethod(string.Empty, typeof(void), [typeof(IParser), typeof(Type), typeof(Func<IParser, Type, object>), typeof(object)]);
63+
var dm = new DynamicMethod(
64+
string.Empty,
65+
returnType: typeof(void),
66+
[
67+
typeof(IParser),
68+
typeof(Type),
69+
typeof(Func<IParser, Type, object?>),
70+
typeof(object),
71+
typeof(INamingConvention),
72+
typeof(ITypeInspector)
73+
]);
74+
5975
var il = dm.GetILGenerator();
60-
il.Emit(OpCodes.Ldarg_0);
61-
il.Emit(OpCodes.Ldarg_1);
62-
il.Emit(OpCodes.Ldarg_2);
63-
il.Emit(OpCodes.Ldarg_3);
76+
il.Emit(OpCodes.Ldarg_0); // reader
77+
il.Emit(OpCodes.Ldarg_1); // expectedType
78+
il.Emit(OpCodes.Ldarg_2); // nestedObjectDeserializer
79+
il.Emit(OpCodes.Ldarg_3); // result
6480
il.Emit(OpCodes.Castclass, typeof(ICollection<>).MakeGenericType(gp));
81+
il.Emit(OpCodes.Ldarg_S, (byte)4); // enumNamingConvention
82+
il.Emit(OpCodes.Ldarg_S, (byte)5); // typeDescriptor
6583
il.Emit(OpCodes.Call, DeserializeHelperMethod.MakeGenericMethod(gp));
6684
il.Emit(OpCodes.Ret);
67-
action = (Action<IParser, Type, Func<IParser, Type, object?>, object?>)dm.CreateDelegate(typeof(Action<IParser, Type, Func<IParser, Type, object?>, object?>));
85+
action = (Action<IParser, Type, Func<IParser, Type, object?>, object?, INamingConvention, ITypeInspector>)dm.CreateDelegate(typeof(Action<IParser, Type, Func<IParser, Type, object?>, object?, INamingConvention, ITypeInspector>));
6886
_actionCache[gp] = action;
6987
}
7088

71-
action(reader, expectedType, nestedObjectDeserializer, value);
89+
action(reader, expectedType, nestedObjectDeserializer, value, _enumNamingConvention, _typeDescriptor);
7290
return true;
7391
}
7492

7593
[EditorBrowsable(EditorBrowsableState.Never)]
76-
public static void DeserializeHelper<TItem>(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, ICollection<TItem> result)
94+
public static void DeserializeHelper<TItem>(
95+
IParser reader,
96+
Type expectedType,
97+
Func<IParser, Type, object?> nestedObjectDeserializer,
98+
ICollection<TItem> result,
99+
INamingConvention enumNamingConvention,
100+
ITypeInspector typeDescriptor)
77101
{
78102
reader.Consume<SequenceStart>();
79103
while (!reader.Accept<SequenceEnd>(out _))
80104
{
81105
var value = nestedObjectDeserializer(reader, typeof(TItem));
82106
if (value is not IValuePromise promise)
83107
{
84-
result.Add(TypeConverter.ChangeType<TItem>(value, NullNamingConvention.Instance));
108+
result.Add(TypeConverter.ChangeType<TItem>(value, enumNamingConvention, typeDescriptor));
85109
}
86110
else if (result is IList<TItem> list)
87111
{
88112
var index = list.Count;
89113
result.Add(default!);
90-
promise.ValueAvailable += v => list[index] = TypeConverter.ChangeType<TItem>(v, NullNamingConvention.Instance);
114+
promise.ValueAvailable += v => list[index] = TypeConverter.ChangeType<TItem>(v, enumNamingConvention, typeDescriptor);
91115
}
92116
else
93117
{

src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericDictionaryNodeDeserializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public EmitGenericDictionaryNodeDeserializer(IObjectFactory objectFactory)
2424
_objectFactory = objectFactory;
2525
}
2626

27-
bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value)
27+
bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer)
2828
{
2929
if (!_gpCache.TryGetValue(expectedType, out var gp))
3030
{

src/Docfx.YamlSerialization/NodeDeserializers/ExtensibleObjectNodeDeserializer.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using YamlDotNet.Core;
55
using YamlDotNet.Core.Events;
66
using YamlDotNet.Serialization;
7-
using YamlDotNet.Serialization.NamingConventions;
87
using YamlDotNet.Serialization.Utilities;
98

109
namespace Docfx.YamlSerialization.NodeDeserializers;
@@ -14,15 +13,19 @@ public sealed class ExtensibleObjectNodeDeserializer : INodeDeserializer
1413
private readonly IObjectFactory _objectFactory;
1514
private readonly ITypeInspector _typeDescriptor;
1615
private readonly bool _ignoreUnmatched;
16+
private readonly bool _caseInsensitivePropertyMatching;
17+
private readonly INamingConvention _enumNamingConvention;
1718

18-
public ExtensibleObjectNodeDeserializer(IObjectFactory objectFactory, ITypeInspector typeDescriptor, bool ignoreUnmatched)
19+
public ExtensibleObjectNodeDeserializer(IObjectFactory objectFactory, ITypeInspector typeDescriptor, INamingConvention enumNamingConvention, bool ignoreUnmatched, bool caseInsensitivePropertyMatching)
1920
{
2021
_objectFactory = objectFactory;
2122
_typeDescriptor = typeDescriptor;
23+
_enumNamingConvention = enumNamingConvention;
2224
_ignoreUnmatched = ignoreUnmatched;
25+
_caseInsensitivePropertyMatching = caseInsensitivePropertyMatching;
2326
}
2427

25-
bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value)
28+
bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer)
2629
{
2730
if (!reader.TryConsume<MappingStart>(out _))
2831
{
@@ -34,7 +37,7 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IPars
3437
while (!reader.Accept<MappingEnd>(out _))
3538
{
3639
var propertyName = reader.Consume<Scalar>();
37-
var property = _typeDescriptor.GetProperty(expectedType, value, propertyName.Value, _ignoreUnmatched);
40+
var property = _typeDescriptor.GetProperty(expectedType, value, propertyName.Value, _ignoreUnmatched, _caseInsensitivePropertyMatching);
3841
if (property == null)
3942
{
4043
reader.SkipThisAndNestedEvents();
@@ -44,15 +47,15 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func<IPars
4447
var propertyValue = nestedObjectDeserializer(reader, property.Type);
4548
if (propertyValue is not IValuePromise propertyValuePromise)
4649
{
47-
var convertedValue = TypeConverter.ChangeType(propertyValue, property.Type, NullNamingConvention.Instance);
50+
var convertedValue = TypeConverter.ChangeType(propertyValue, property.Type, _enumNamingConvention, _typeDescriptor);
4851
property.Write(value, convertedValue);
4952
}
5053
else
5154
{
5255
var valueRef = value;
5356
propertyValuePromise.ValueAvailable += v =>
5457
{
55-
var convertedValue = TypeConverter.ChangeType(v, property.Type, NullNamingConvention.Instance);
58+
var convertedValue = TypeConverter.ChangeType(v, property.Type, _enumNamingConvention, _typeDescriptor);
5659
property.Write(valueRef, convertedValue);
5760
};
5861
}

0 commit comments

Comments
 (0)