Skip to content

Commit e4e41c6

Browse files
committed
Fixed issue with type of value from <example> when using IncludeXmlCommentsFromInheritDocs, unchase#34
1 parent 0dfe7e5 commit e4e41c6

File tree

4 files changed

+208
-17
lines changed

4 files changed

+208
-17
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
3+
namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions
4+
{
5+
/// <summary>
6+
/// Extension methods for <see cref=" object"/>.
7+
/// </summary>
8+
internal static class TypeExtensions
9+
{
10+
/// <summary>
11+
/// Check if an object is a number
12+
/// </summary>
13+
/// <param name="type"></param>
14+
/// <returns>True or False</returns>
15+
internal static bool IsNumber(this Type type)
16+
{
17+
return type == typeof(sbyte)
18+
|| type == typeof(byte)
19+
|| type == typeof(short)
20+
|| type == typeof(ushort)
21+
|| type == typeof(int)
22+
|| type == typeof(uint)
23+
|| type == typeof(long)
24+
|| type == typeof(ulong)
25+
//|| type == typeof(Int128 - Only in .NET 7, 8, 9)
26+
//|| type == typeof(UInt128 - Only in .NET 7, 8, 9)
27+
//|| type == typeof(nint - Only in .NET 7, 8, 9)
28+
//|| type == typeof(nuint - Only in .NET 7, 8, 9)
29+
//|| type == typeof(Half - Only in .NET 5, 6, 7, 8, 9)
30+
|| type == typeof(float)
31+
|| type == typeof(double)
32+
|| type == typeof(decimal);
33+
}
34+
}
35+
}

src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs

Lines changed: 97 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
using System;
1+
using Microsoft.OpenApi.Any;
2+
using Microsoft.OpenApi.Models;
3+
using Swashbuckle.AspNetCore.SwaggerGen;
4+
using System;
25
using System.Collections.Generic;
6+
using System.Globalization;
37
using System.Linq;
48
using System.Reflection;
59
using System.Xml.XPath;
610

7-
using Microsoft.OpenApi.Any;
8-
using Microsoft.OpenApi.Models;
9-
using Swashbuckle.AspNetCore.SwaggerGen;
10-
1111
namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions
1212
{
1313
internal static class XmlCommentsExtensions
@@ -114,15 +114,11 @@ private static MemberInfo[] GetTargets(MemberInfo memberInfo, string cref)
114114
}
115115

116116
// Find all matching members in all interfaces and the base class.
117-
var targets = type.GetInterfaces()
118-
.Append(type.BaseType)
119-
.SelectMany(
120-
x => x.FindMembers(
121-
memberInfo.MemberType,
122-
BindingFlags.Instance | BindingFlags.Public,
123-
(info, _) => info.Name == memberInfo.Name,
124-
null))
125-
.ToList();
117+
var targets = type.GetInterfaces().Append(type.BaseType).SelectMany(x => x.FindMembers(
118+
memberInfo.MemberType,
119+
BindingFlags.Instance | BindingFlags.Public,
120+
(info, _) => info.Name == memberInfo.Name,
121+
null)).ToList();
126122

127123
// Try to find the target, if one is declared.
128124
if (!string.IsNullOrWhiteSpace(cref))
@@ -168,7 +164,7 @@ internal static void ApplyPropertyComments(
168164
XPathNavigator targetXmlNode;
169165
if (string.IsNullOrWhiteSpace(cref))
170166
{
171-
var target = memberInfo.GetTargetRecursive(inheritedDocs, cref);
167+
var target = GetTargetRecursive(memberInfo, inheritedDocs, cref);
172168
if (target == null)
173169
{
174170
return;
@@ -204,10 +200,95 @@ internal static void ApplyPropertyComments(
204200
var exampleNode = targetXmlNode.SelectSingleNode(ExampleTag);
205201
if (exampleNode != null)
206202
{
207-
schema.Example = new OpenApiString(XmlCommentsTextHelper.Humanize(exampleNode.InnerXml));
203+
schema.Example = GetExampleValue(memberInfo, exampleNode);
208204
}
209205
}
210206

207+
private static IOpenApiAny GetExampleValue(MemberInfo memberInfo, XPathNavigator exampleNode)
208+
{
209+
var type = GetUnderlyingType(memberInfo);
210+
var exampleValue = exampleNode.InnerXml;
211+
212+
if (string.IsNullOrEmpty(exampleValue))
213+
{
214+
return new OpenApiNull();
215+
}
216+
217+
if (!type.IsNumber())
218+
{
219+
return new OpenApiString(XmlCommentsTextHelper.Humanize(exampleValue));
220+
}
221+
222+
223+
if (
224+
type == typeof(byte)
225+
|| type == typeof(sbyte)
226+
)
227+
{
228+
return new OpenApiByte(byte.Parse(XmlCommentsTextHelper.Humanize(exampleValue)));
229+
}
230+
231+
if (
232+
type == typeof(short)
233+
|| type == typeof(ushort)
234+
|| type == typeof(int)
235+
|| type == typeof(uint)
236+
)
237+
{
238+
return new OpenApiInteger(int.Parse(XmlCommentsTextHelper.Humanize(exampleValue), CultureInfo.InvariantCulture));
239+
}
240+
241+
if (
242+
type == typeof(long)
243+
|| type == typeof(ulong)
244+
)
245+
{
246+
return new OpenApiLong(long.Parse(XmlCommentsTextHelper.Humanize(exampleValue), CultureInfo.InvariantCulture));
247+
}
248+
249+
if (
250+
type == typeof(float)
251+
|| type == typeof(decimal)
252+
)
253+
{
254+
return new OpenApiFloat(float.Parse(XmlCommentsTextHelper.Humanize(exampleValue), CultureInfo.InvariantCulture));
255+
}
256+
257+
if (type == typeof(double))
258+
{
259+
return new OpenApiDouble(double.Parse(XmlCommentsTextHelper.Humanize(exampleValue), CultureInfo.InvariantCulture));
260+
}
261+
262+
return new OpenApiString(XmlCommentsTextHelper.Humanize(exampleValue));
263+
}
264+
265+
private static Type GetUnderlyingType(this MemberInfo member)
266+
{
267+
switch (member.MemberType)
268+
{
269+
case MemberTypes.Event:
270+
return ((EventInfo)member).EventHandlerType;
271+
case MemberTypes.Field:
272+
return ((FieldInfo)member).FieldType;
273+
case MemberTypes.Method:
274+
return ((MethodInfo)member).ReturnType;
275+
case MemberTypes.Property:
276+
var propType = ((PropertyInfo)member).PropertyType;
277+
278+
if (!propType.Name.ToLower().Contains("nullable"))
279+
{
280+
return propType;
281+
}
282+
283+
return propType.GenericTypeArguments?.Length > 0 ? propType.GenericTypeArguments[0] : propType;
284+
285+
default:
286+
throw new ArgumentException
287+
(
288+
"Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"
289+
);
290+
}
291+
}
211292
internal static XPathNavigator GetMemberXmlNode(string memberName, List<XPathDocument> documents)
212293
{
213294
string path = $"/doc/members/member[@name='{memberName}']";

test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/IInheritDocClass.cs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models
1+
// ReSharper disable CommentTypo
2+
// ReSharper disable IdentifierTypo
3+
namespace Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models
24
{
35
/// <summary>
46
/// InheritDocClass - inheritdoc
@@ -14,6 +16,61 @@ public interface IInheritDocClass : IInheritDocCommon
1416
/// <remarks>
1517
/// Name remarks - inheritdoc
1618
/// </remarks>
19+
/// <example>Donald Duck</example>
1720
string Name { get; set; }
21+
22+
/// <summary>
23+
/// Age - inheritdoc
24+
/// </summary>
25+
/// <remarks>
26+
/// Age remarks - inheritdoc
27+
/// </remarks>
28+
/// <example>75</example>
29+
int? Age { get; set; }
30+
31+
/// <summary>
32+
/// Weight - inheritdoc
33+
/// </summary>
34+
/// <remarks>
35+
/// Weight remarks - inheritdoc
36+
/// </remarks>
37+
/// <example>0.174</example>
38+
float? Weight { get; set; }
39+
40+
/// <summary>
41+
/// NumberOfFeet - inheritdoc
42+
/// </summary>
43+
/// <remarks>
44+
/// NumberOfFeet remarks - inheritdoc
45+
/// </remarks>
46+
/// <example>2</example>
47+
int? NumberOfFeet { get; set; }
48+
49+
/// <summary>
50+
/// AByte - inheritdoc
51+
/// </summary>
52+
/// <remarks>
53+
/// AByte remarks - inheritdoc
54+
/// </remarks>
55+
/// <example>1</example>
56+
byte? AByte { get; set; }
57+
58+
/// <summary>
59+
/// AShort - inheritdoc
60+
/// </summary>
61+
/// <remarks>
62+
/// AShort remarks - inheritdoc
63+
/// </remarks>
64+
/// <example>1234</example>
65+
short? AShort { get; set; }
66+
67+
/// <summary>
68+
/// ALong - inheritdoc
69+
/// </summary>
70+
/// <remarks>
71+
/// ALong remarks - inheritdoc
72+
/// </remarks>
73+
/// <example>1234</example>
74+
long? ALong { get; set; }
1875
}
1976
}

test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/InheritDocClass.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@ public class InheritDocClass : IInheritDocClass
66
/// <inheritdoc/>
77
public string Name { get; set; }
88

9+
/// <inheritdoc/>
10+
public int? Age { get; set; }
11+
12+
/// <inheritdoc/>
13+
public float? Weight { get; set; }
14+
15+
/// <inheritdoc/>
16+
public int? NumberOfFeet { get; set; }
17+
18+
/// <inheritdoc/>
19+
public byte? AByte { get; set; }
20+
21+
/// <inheritdoc/>
22+
public short? AShort { get; set; }
23+
24+
/// <inheritdoc/>
25+
public long? ALong { get; set; }
26+
927
/// <inheritdoc cref="IInheritDocCommon.Common"/>
1028
public string Common { get; set; }
1129

0 commit comments

Comments
 (0)