<PackageReference Include="NJsonSchema" Version="8.5.6255.20253" />

XmlDocumentationExtensions

public static class XmlDocumentationExtensions
Provides extension methods for reading XML comments from reflected members.
using Microsoft.CSharp.RuntimeBinder; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; namespace NJsonSchema.Infrastructure { public static class XmlDocumentationExtensions { private static readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private static readonly Dictionary<string, XDocument> Cache = new Dictionary<string, XDocument>(StringComparer.OrdinalIgnoreCase); public static async Task<string> GetXmlSummaryAsync(this MemberInfo member) { ConfiguredTaskAwaiter val = AwaitExtensions.ConfigureAwait<string>(member.GetXmlDocumentationTagAsync("summary"), false).GetAwaiter(); if (!val.get_IsCompleted()) { await val; ConfiguredTaskAwaiter val2 = default(ConfiguredTaskAwaiter); val = val2; } !0 result = val.GetResult(); val = default(ConfiguredTaskAwaiter); return (string)result; } public static async Task<string> GetXmlRemarksAsync(this MemberInfo member) { ConfiguredTaskAwaiter val = AwaitExtensions.ConfigureAwait<string>(member.GetXmlDocumentationTagAsync("remarks"), false).GetAwaiter(); if (!val.get_IsCompleted()) { await val; ConfiguredTaskAwaiter val2 = default(ConfiguredTaskAwaiter); val = val2; } !0 result = val.GetResult(); val = default(ConfiguredTaskAwaiter); return (string)result; } public static async Task<XElement> GetXmlDocumentationAsync(this MemberInfo member) { if (!DynamicApis.SupportsXPathApis || !DynamicApis.SupportsFileApis || !DynamicApis.SupportsPathApis) return null; AssemblyName name = member.Module.Assembly.GetName(); _lock.Wait(); try { if (Cache.ContainsKey(name.FullName) && Cache[name.FullName] == null) return null; } finally { _lock.Release(); } ConfiguredTaskAwaiter val = AwaitExtensions.ConfigureAwait<string>(GetXmlDocumentationPathAsync(member.Module.Assembly), false).GetAwaiter(); if (!val.get_IsCompleted()) { await val; ConfiguredTaskAwaiter val2 = default(ConfiguredTaskAwaiter); val = val2; } !0 result = val.GetResult(); val = default(ConfiguredTaskAwaiter); string pathToXmlFile = (string)result; ConfiguredTaskAwaiter val3 = AwaitExtensions.ConfigureAwait<XElement>(member.GetXmlDocumentationAsync(pathToXmlFile), false).GetAwaiter(); if (!val3.get_IsCompleted()) { await val3; ConfiguredTaskAwaiter val4 = default(ConfiguredTaskAwaiter); val3 = val4; } !0 result2 = val3.GetResult(); val3 = default(ConfiguredTaskAwaiter); return (XElement)result2; } public static async Task<string> GetXmlDocumentationTagAsync(this MemberInfo member, string tagName) { if (!DynamicApis.SupportsXPathApis || !DynamicApis.SupportsFileApis || !DynamicApis.SupportsPathApis) return string.Empty; AssemblyName name = member.Module.Assembly.GetName(); _lock.Wait(); try { if (Cache.ContainsKey(name.FullName) && Cache[name.FullName] == null) return string.Empty; } finally { _lock.Release(); } ConfiguredTaskAwaiter val = AwaitExtensions.ConfigureAwait<string>(GetXmlDocumentationPathAsync(member.Module.Assembly), false).GetAwaiter(); if (!val.get_IsCompleted()) { await val; ConfiguredTaskAwaiter val2 = default(ConfiguredTaskAwaiter); val = val2; } !0 result = val.GetResult(); val = default(ConfiguredTaskAwaiter); string pathToXmlFile = (string)result; ConfiguredTaskAwaiter val3 = AwaitExtensions.ConfigureAwait<XElement>(member.GetXmlDocumentationAsync(pathToXmlFile), false).GetAwaiter(); if (!val3.get_IsCompleted()) { await val3; ConfiguredTaskAwaiter val4 = default(ConfiguredTaskAwaiter); val3 = val4; } !0 result2 = val3.GetResult(); val3 = default(ConfiguredTaskAwaiter); return RemoveLineBreakWhiteSpaces(((XContainer)result2)?.Element((XName)tagName)?.Value); } public static async Task<string> GetXmlDocumentationAsync(this ParameterInfo parameter) { if (!DynamicApis.SupportsXPathApis || !DynamicApis.SupportsFileApis || !DynamicApis.SupportsPathApis) return string.Empty; AssemblyName name = parameter.Member.Module.Assembly.GetName(); _lock.Wait(); try { if (Cache.ContainsKey(name.FullName) && Cache[name.FullName] == null) return string.Empty; } finally { _lock.Release(); } ConfiguredTaskAwaiter val = AwaitExtensions.ConfigureAwait<string>(GetXmlDocumentationPathAsync(parameter.Member.Module.Assembly), false).GetAwaiter(); if (!val.get_IsCompleted()) { await val; ConfiguredTaskAwaiter val2 = default(ConfiguredTaskAwaiter); val = val2; } !0 result = val.GetResult(); val = default(ConfiguredTaskAwaiter); string pathToXmlFile = (string)result; ConfiguredTaskAwaiter val3 = AwaitExtensions.ConfigureAwait<XElement>(parameter.GetXmlDocumentationAsync(pathToXmlFile), false).GetAwaiter(); if (!val3.get_IsCompleted()) { await val3; ConfiguredTaskAwaiter val4 = default(ConfiguredTaskAwaiter); val3 = val4; } !0 result2 = val3.GetResult(); val3 = default(ConfiguredTaskAwaiter); return RemoveLineBreakWhiteSpaces(((XElement)result2)?.Value); } public static Task<XElement> GetXmlDocumentationAsync(this Type type, string pathToXmlFile) { return ((MemberInfo)type.GetTypeInfo()).GetXmlDocumentationAsync(pathToXmlFile); } public static async Task<XElement> GetXmlDocumentationAsync(this MemberInfo member, string pathToXmlFile) { try { if (pathToXmlFile != null && DynamicApis.SupportsXPathApis && DynamicApis.SupportsFileApis && DynamicApis.SupportsPathApis) { AssemblyName assemblyName = member.Module.Assembly.GetName(); _lock.Wait(); try { if (Cache.ContainsKey(assemblyName.FullName) && Cache[assemblyName.FullName] == null) return null; ConfiguredTaskAwaiter val = AwaitExtensions.ConfigureAwait<bool>(DynamicApis.FileExistsAsync(pathToXmlFile), false).GetAwaiter(); if (!val.get_IsCompleted()) { await val; ConfiguredTaskAwaiter val2 = default(ConfiguredTaskAwaiter); val = val2; } !0 result = val.GetResult(); val = default(ConfiguredTaskAwaiter); if (result == null) { Cache[assemblyName.FullName] = null; return null; } if (!Cache.ContainsKey(assemblyName.FullName)) { string fullName = assemblyName.FullName; ConfiguredTaskAwaiter val3 = AwaitExtensions.ConfigureAwait<XDocument>(Task.Factory.StartNew(() => XDocument.Load(pathToXmlFile)), false).GetAwaiter(); if (!val3.get_IsCompleted()) { await val3; ConfiguredTaskAwaiter val4 = default(ConfiguredTaskAwaiter); val3 = val4; } !0 result2 = val3.GetResult(); val3 = default(ConfiguredTaskAwaiter); Cache[fullName] = (XDocument)result2; } return member.GetXmlDocumentation(Cache[assemblyName.FullName]); } finally { _lock.Release(); } } return null; } catch { return null; } } public static async Task<XElement> GetXmlDocumentationAsync(this ParameterInfo parameter, string pathToXmlFile) { try { if (pathToXmlFile != null && DynamicApis.SupportsXPathApis && DynamicApis.SupportsFileApis && DynamicApis.SupportsPathApis) { AssemblyName assemblyName = parameter.Member.Module.Assembly.GetName(); _lock.Wait(); try { if (Cache.ContainsKey(assemblyName.FullName) && Cache[assemblyName.FullName] == null) return null; ConfiguredTaskAwaiter val = AwaitExtensions.ConfigureAwait<bool>(DynamicApis.FileExistsAsync(pathToXmlFile), false).GetAwaiter(); if (!val.get_IsCompleted()) { await val; ConfiguredTaskAwaiter val2 = default(ConfiguredTaskAwaiter); val = val2; } !0 result = val.GetResult(); val = default(ConfiguredTaskAwaiter); if (result == null) { Cache[assemblyName.FullName] = null; return null; } if (!Cache.ContainsKey(assemblyName.FullName)) { string fullName = assemblyName.FullName; ConfiguredTaskAwaiter val3 = AwaitExtensions.ConfigureAwait<XDocument>(Task.Factory.StartNew(() => XDocument.Load(pathToXmlFile)), false).GetAwaiter(); if (!val3.get_IsCompleted()) { await val3; ConfiguredTaskAwaiter val4 = default(ConfiguredTaskAwaiter); val3 = val4; } !0 result2 = val3.GetResult(); val3 = default(ConfiguredTaskAwaiter); Cache[fullName] = (XDocument)result2; } return parameter.GetXmlDocumentation(Cache[assemblyName.FullName]); } finally { _lock.Release(); } } return null; } catch { return null; } } private static XElement GetXmlDocumentation(this MemberInfo member, XDocument xml) { string memberElementName = GetMemberElementName(member); return ((IEnumerable)DynamicApis.XPathEvaluate(xml, $"""{memberElementName}""")).OfType<XElement>().First(); } private static XElement GetXmlDocumentation(this ParameterInfo parameter, XDocument xml) { string memberElementName = GetMemberElementName(parameter.Member); IEnumerable source = (!parameter.IsRetval && !string.IsNullOrEmpty(parameter.Name)) ? ((IEnumerable)DynamicApis.XPathEvaluate(xml, $"""{memberElementName}""{parameter.Name}""")) : ((IEnumerable)DynamicApis.XPathEvaluate(xml, $"""{memberElementName}""")); return source.OfType<XElement>().First(); } private static string RemoveLineBreakWhiteSpaces(string documentation) { if (string.IsNullOrEmpty(documentation)) return string.Empty; documentation = "\n" + documentation.Replace("\r", string.Empty).Trim(new char[1] { '\n' }); string value = Regex.Match(documentation, "(\\n[ \\t]*)").Value; documentation = documentation.Replace(value, "\n"); return documentation.Trim(new char[1] { '\n' }); } private static string GetMemberElementName(dynamic member) { string text = (string)((member is Type) ? ((Type)member).FullName : (member.DeclaringType.FullName + "." + member.Name)); char c; switch ((string)member.MemberType.ToString()) { case "Constructor": text = text.Replace(".ctor", "#ctor"); goto case "Method"; case "Method": { c = 'M'; string text2 = string.Join(",", (from x in ((MethodBase)member).GetParameters() select Regex.Replace(x.ParameterType.FullName, "(`[0-9]+)|(, .*?PublicKeyToken=[0-9a-z]*)", string.Empty).Replace("[[", "{").Replace("]]", "}")).ToArray()); if (!string.IsNullOrEmpty(text2)) text = text + "(" + text2 + ")"; break; } case "Event": c = 'E'; break; case "Field": c = 'F'; break; case "NestedType": text = text.Replace('+', '.'); goto case "TypeInfo"; case "TypeInfo": c = 'T'; break; case "Property": c = 'P'; break; default: throw new ArgumentException("Unknown member type.", "member"); } return string.Format("{0}:{1}", c, text.Replace("+", ".")); } private unsafe static async Task<string> GetXmlDocumentationPathAsync(dynamic assembly) { try { if (assembly == null) return null; if (string.IsNullOrEmpty(assembly.Location)) return null; dynamic assemblyName = assembly.GetName(); if (string.IsNullOrEmpty(assemblyName.Name)) return null; dynamic path = DynamicApis.PathCombine(DynamicApis.PathGetDirectoryName(assembly.Location), assemblyName.Name + ".xml"); if (<>o__14.<>p__16 == null) <>o__14.<>p__16 = CallSite<Func<CallSite, object, bool>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.UnaryOperation(CSharpBinderFlags.None, ExpressionType.IsTrue, typeof(XmlDocumentationExtensions), new CSharpArgumentInfo[1] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); Func<CallSite, object, bool> target = <>o__14.<>p__16.Target; CallSite<Func<CallSite, object, bool>> <>p__ = <>o__14.<>p__16; dynamic val = DynamicApis.FileExistsAsync(path).ConfigureAwait(false).GetAwaiter(); ICriticalNotifyCompletion awaiter; INotifyCompletion awaiter2; AsyncTaskMethodBuilder<string> asyncTaskMethodBuilder; if ((byte)val.IsCompleted != 0) { object arg = val.GetResult(); if (target(<>p__, arg)) return (string)path; if (((object)assembly).GetType().GetRuntimeProperty("CodeBase") != (PropertyInfo)null) { path = DynamicApis.PathCombine(DynamicApis.PathGetDirectoryName(assembly.CodeBase.Replace("file:///", string.Empty)), assemblyName.Name + ".xml").Replace("file:\\", string.Empty); if (<>o__14.<>p__27 == null) <>o__14.<>p__27 = CallSite<Func<CallSite, object, bool>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.UnaryOperation(CSharpBinderFlags.None, ExpressionType.IsTrue, typeof(XmlDocumentationExtensions), new CSharpArgumentInfo[1] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); target = <>o__14.<>p__27.Target; <>p__ = <>o__14.<>p__27; val = DynamicApis.FileExistsAsync(path).ConfigureAwait(false).GetAwaiter(); if (!((byte)val.IsCompleted != 0)) { awaiter = (val as ICriticalNotifyCompletion); if (awaiter == null) { awaiter2 = (INotifyCompletion)val; asyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter2, ref *(<GetXmlDocumentationPathAsync>d__14*)); awaiter2 = null; } else asyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref *(<GetXmlDocumentationPathAsync>d__14*)); awaiter = null; ; } arg = val.GetResult(); if (target(<>p__, arg)) return (string)path; } dynamic currentDomain = ReflectionExtensions.GetValue(Type.GetType("System.AppDomain").GetRuntimeProperty("CurrentDomain"), null); path = DynamicApis.PathCombine(currentDomain.BaseDirectory, assemblyName.Name + ".xml"); if (<>o__14.<>p__35 == null) <>o__14.<>p__35 = CallSite<Func<CallSite, object, bool>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.UnaryOperation(CSharpBinderFlags.None, ExpressionType.IsTrue, typeof(XmlDocumentationExtensions), new CSharpArgumentInfo[1] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); target = <>o__14.<>p__35.Target; <>p__ = <>o__14.<>p__35; val = DynamicApis.FileExistsAsync(path).ConfigureAwait(false).GetAwaiter(); if ((byte)val.IsCompleted != 0) { arg = val.GetResult(); if (target(<>p__, arg)) return (string)path; return (string)DynamicApis.PathCombine(currentDomain.BaseDirectory, "bin\\" + assemblyName.Name + ".xml"); } awaiter = (val as ICriticalNotifyCompletion); if (awaiter == null) { awaiter2 = (INotifyCompletion)val; asyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter2, ref *(<GetXmlDocumentationPathAsync>d__14*)); awaiter2 = null; } else asyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref *(<GetXmlDocumentationPathAsync>d__14*)); awaiter = null; ; } awaiter = (val as ICriticalNotifyCompletion); if (awaiter == null) { awaiter2 = (INotifyCompletion)val; asyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter2, ref *(<GetXmlDocumentationPathAsync>d__14*)); awaiter2 = null; } else asyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref *(<GetXmlDocumentationPathAsync>d__14*)); awaiter = null; ; } catch { return null; } } } }