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

XmlDocumentationExtensions

public static class XmlDocumentationExtensions
Provides extension methods for reading XML comments from reflected members.
using Microsoft.CSharp.RuntimeBinder; using System; 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 Task<string> GetXmlSummaryAsync(this Type type) { return ((MemberInfo)type.GetTypeInfo()).GetXmlDocumentationAsync("summary"); } public static Task<string> GetXmlRemarksAsync(this Type type) { return ((MemberInfo)type.GetTypeInfo()).GetXmlDocumentationAsync("remarks"); } [Obsolete("Use GetXmlSummary instead.")] public static Task<string> GetXmlDocumentationAsync(this Type type) { return type.GetXmlDocumentationAsync("summary"); } public static Task<string> GetXmlDocumentationAsync(this Type type, string tagName) { return ((MemberInfo)type.GetTypeInfo()).GetXmlDocumentationAsync(tagName); } public static async Task<string> GetXmlSummaryAsync(this MemberInfo member) { return await member.GetXmlDocumentationAsync("summary").ConfigureAwait(false); } public static async Task<string> GetXmlRemarksAsync(this MemberInfo member) { return await member.GetXmlDocumentationAsync("remarks").ConfigureAwait(false); } [Obsolete("Use GetXmlSummary instead.")] public static async Task<string> GetXmlDocumentationAsync(this MemberInfo member) { return await member.GetXmlDocumentationAsync("summary").ConfigureAwait(false); } public static async Task<string> GetXmlDocumentationAsync(this MemberInfo member, string tagName) { if (!DynamicApis.SupportsXPathApis || !DynamicApis.SupportsFileApis || !DynamicApis.SupportsPathApis) return string.Empty; AssemblyName assemblyName = member.Module.Assembly.GetName(); await _lock.WaitAsync(); try { if (Cache.ContainsKey(assemblyName.FullName) && Cache[assemblyName.FullName] == null) return string.Empty; } finally { _lock.Release(); } return await member.GetXmlDocumentationAsync(await GetXmlDocumentationPathAsync(member.Module.Assembly).ConfigureAwait(false), tagName).ConfigureAwait(false); } public static async Task<string> GetXmlDocumentationAsync(this ParameterInfo parameter) { if (!DynamicApis.SupportsXPathApis || !DynamicApis.SupportsFileApis || !DynamicApis.SupportsPathApis) return string.Empty; AssemblyName assemblyName = parameter.Member.Module.Assembly.GetName(); await _lock.WaitAsync(); try { if (Cache.ContainsKey(assemblyName.FullName) && Cache[assemblyName.FullName] == null) return string.Empty; } finally { _lock.Release(); } return await parameter.GetXmlDocumentationAsync(await GetXmlDocumentationPathAsync(parameter.Member.Module.Assembly).ConfigureAwait(false)).ConfigureAwait(false); } public static Task<string> GetXmlDocumentationAsync(this Type type, string pathToXmlFile, string tagName) { return ((MemberInfo)type.GetTypeInfo()).GetXmlDocumentationAsync(pathToXmlFile, tagName); } public static async Task<string> GetXmlDocumentationAsync(this MemberInfo member, string pathToXmlFile, string tagName) { try { if (pathToXmlFile != null && DynamicApis.SupportsXPathApis && DynamicApis.SupportsFileApis && DynamicApis.SupportsPathApis) { AssemblyName assemblyName = member.Module.Assembly.GetName(); await _lock.WaitAsync(); try { if (Cache.ContainsKey(assemblyName.FullName) && Cache[assemblyName.FullName] == null) return string.Empty; if (!(await DynamicApis.FileExistsAsync(pathToXmlFile).ConfigureAwait(false))) { Cache[assemblyName.FullName] = null; return string.Empty; } if (!Cache.ContainsKey(assemblyName.FullName)) { string fullName = assemblyName.FullName; XDocument value = await Task.Factory.StartNew(() => XDocument.Load(pathToXmlFile)).ConfigureAwait(false); Cache[fullName] = value; } return member.GetXmlDocumentation(Cache[assemblyName.FullName], tagName); } finally { _lock.Release(); } } return string.Empty; } catch { return string.Empty; } } public static async Task<string> GetXmlDocumentationAsync(this ParameterInfo parameter, string pathToXmlFile) { try { if (pathToXmlFile != null && DynamicApis.SupportsXPathApis && DynamicApis.SupportsFileApis && DynamicApis.SupportsPathApis) { AssemblyName assemblyName = parameter.Member.Module.Assembly.GetName(); await _lock.WaitAsync(); try { if (Cache.ContainsKey(assemblyName.FullName) && Cache[assemblyName.FullName] == null) return string.Empty; if (!(await DynamicApis.FileExistsAsync(pathToXmlFile).ConfigureAwait(false))) { Cache[assemblyName.FullName] = null; return string.Empty; } if (!Cache.ContainsKey(assemblyName.FullName)) { string fullName = assemblyName.FullName; XDocument value = await Task.Factory.StartNew(() => XDocument.Load(pathToXmlFile)).ConfigureAwait(false); Cache[fullName] = value; } return parameter.GetXmlDocumentation(Cache[assemblyName.FullName]); } finally { _lock.Release(); } } return string.Empty; } catch { return string.Empty; } } private static string GetXmlDocumentation(this MemberInfo member, XDocument xml, string tagName) { string memberElementName = GetMemberElementName(member); return RemoveLineBreakWhiteSpaces(DynamicApis.XPathEvaluate(xml, string.Format("string(/doc/members/member[@name='{0}']/" + tagName + ")", new object[1] { memberElementName })).Trim()); } private static string GetXmlDocumentation(this ParameterInfo parameter, XDocument xml) { string memberElementName = GetMemberElementName(parameter.Member); string documentation = (!parameter.IsRetval && !string.IsNullOrEmpty(parameter.Name)) ? DynamicApis.XPathEvaluate(xml, string.Format("string(/doc/members/member[@name='{0}']/param[@name='{1}'])", new object[2] { memberElementName, parameter.Name })).Trim() : DynamicApis.XPathEvaluate(xml, $"""{new object[1] { memberElementName }}""").Trim(); return RemoveLineBreakWhiteSpaces(documentation); } private static string RemoveLineBreakWhiteSpaces(string documentation) { return Regex.Replace(documentation.Replace("\r", string.Empty), "\\n\\s+", "\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}", new object[2] { 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__18.<>p__16 == null) <>o__18.<>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__18.<>p__16.Target; CallSite<Func<CallSite, object, bool>> <>p__ = <>o__18.<>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)((object)assembly).GetType().GetRuntimeProperty("CodeBase") != null) { path = DynamicApis.PathCombine(DynamicApis.PathGetDirectoryName(assembly.CodeBase.Replace("file:///", string.Empty)), assemblyName.Name + ".xml").Replace("file:\\", string.Empty); if (<>o__18.<>p__27 == null) <>o__18.<>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__18.<>p__27.Target; <>p__ = <>o__18.<>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__18*)); awaiter2 = null; } else asyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref *(<GetXmlDocumentationPathAsync>d__18*)); awaiter = null; ; } arg = val.GetResult(); if (target(<>p__, arg)) return (string)path; } dynamic currentDomain = Type.GetType("System.AppDomain").GetRuntimeProperty("CurrentDomain").GetValue(null); path = DynamicApis.PathCombine(currentDomain.BaseDirectory, assemblyName.Name + ".xml"); if (<>o__18.<>p__35 == null) <>o__18.<>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__18.<>p__35.Target; <>p__ = <>o__18.<>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__18*)); awaiter2 = null; } else asyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref *(<GetXmlDocumentationPathAsync>d__18*)); awaiter = null; ; } awaiter = (val as ICriticalNotifyCompletion); if (awaiter == null) { awaiter2 = (INotifyCompletion)val; asyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter2, ref *(<GetXmlDocumentationPathAsync>d__18*)); awaiter2 = null; } else asyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref *(<GetXmlDocumentationPathAsync>d__18*)); awaiter = null; ; } catch { return null; } } } }