Last active
January 30, 2019 14:53
-
-
Save mikebeaton/ea9e47109f55fbaec0113756904b900e to your computer and use it in GitHub Desktop.
Stand-alone implementation of Swashbuckle.AspNetCore pull #960 which fixes the formatting of non-string JSON examples. Can be used with existing distributions until the pull makes it into a public release. See https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1021 for instructions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.ComponentModel; | |
| using System.Xml.XPath; | |
| using System.Reflection; | |
| using System.Linq; | |
| using Microsoft.OpenApi.Any; | |
| using Newtonsoft.Json.Serialization; | |
| using Microsoft.OpenApi.Models; | |
| using Microsoft.AspNetCore.Mvc; | |
| namespace Swashbuckle.AspNetCore.SwaggerGen | |
| { | |
| internal static class JsonPropertyExtensions | |
| { | |
| internal static bool TryGetMemberInfo(this JsonProperty jsonProperty, out MemberInfo memberInfo) | |
| { | |
| if (jsonProperty.UnderlyingName == null) | |
| { | |
| memberInfo = null; | |
| return false; | |
| } | |
| var metadataAttribute = jsonProperty.DeclaringType.GetTypeInfo() | |
| .GetCustomAttributes(typeof(ModelMetadataTypeAttribute), true) | |
| .FirstOrDefault(); | |
| var typeToReflect = (metadataAttribute != null) | |
| ? ((ModelMetadataTypeAttribute)metadataAttribute).MetadataType | |
| : jsonProperty.DeclaringType; | |
| memberInfo = typeToReflect.GetMember(jsonProperty.UnderlyingName).FirstOrDefault(); | |
| return (memberInfo != null); | |
| } | |
| } | |
| /// <summary> | |
| /// Implementation of <see cref="XmlCommentsSchemaFilter"/> with https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/960 applied, | |
| /// for use until the fix is put in a release of Swashbuckle.AspNetCore | |
| /// </summary> | |
| public class XmlCommentsSchemaFilter_Fix960 : ISchemaFilter | |
| { | |
| private const string MemberXPath = "/doc/members/member[@name='{0}']"; | |
| private const string SummaryTag = "summary"; | |
| private const string ExampleXPath = "example"; | |
| private readonly XPathNavigator _xmlNavigator; | |
| /// <summary> | |
| /// Constructor | |
| /// </summary> | |
| /// <param name="xmlDoc"></param> | |
| public XmlCommentsSchemaFilter_Fix960(XPathDocument xmlDoc) | |
| { | |
| _xmlNavigator = xmlDoc.CreateNavigator(); | |
| } | |
| /// <summary> | |
| /// Apply filter | |
| /// </summary> | |
| public void Apply(OpenApiSchema schema, SchemaFilterContext context) | |
| { | |
| var jsonObjectContract = context.JsonContract as JsonObjectContract; | |
| if (jsonObjectContract == null) return; | |
| var memberName = XmlCommentsMemberNameHelper.GetMemberNameForType(context.SystemType); | |
| var typeNode = _xmlNavigator.SelectSingleNode(string.Format(MemberXPath, memberName)); | |
| if (typeNode != null) | |
| { | |
| var summaryNode = typeNode.SelectSingleNode(SummaryTag); | |
| if (summaryNode != null) | |
| schema.Description = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml); | |
| } | |
| if (schema.Properties == null) return; | |
| foreach (var entry in schema.Properties) | |
| { | |
| var jsonProperty = jsonObjectContract.Properties[entry.Key]; | |
| if (jsonProperty == null) continue; | |
| if (jsonProperty.TryGetMemberInfo(out MemberInfo memberInfo)) | |
| { | |
| ApplyPropertyComments(entry.Value, memberInfo); | |
| } | |
| } | |
| } | |
| private void ApplyPropertyComments(OpenApiSchema propertySchema, MemberInfo memberInfo) | |
| { | |
| var memberName = XmlCommentsMemberNameHelper.GetMemberNameForMember(memberInfo); | |
| var memberNode = _xmlNavigator.SelectSingleNode(string.Format(MemberXPath, memberName)); | |
| if (memberNode == null) return; | |
| var summaryNode = memberNode.SelectSingleNode(SummaryTag); | |
| if (summaryNode != null) | |
| { | |
| propertySchema.Description = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml); | |
| } | |
| var exampleNode = memberNode.SelectSingleNode(ExampleXPath); | |
| if (exampleNode != null) | |
| { | |
| var exampleString = XmlCommentsTextHelper.Humanize(exampleNode.InnerXml); | |
| var memberType = (memberInfo.MemberType & MemberTypes.Field) != 0 ? ((FieldInfo)memberInfo).FieldType : ((PropertyInfo)memberInfo).PropertyType; | |
| propertySchema.Example = ConvertToOpenApiType(exampleString, memberType); | |
| } | |
| } | |
| private static IOpenApiPrimitive ConvertToOpenApiType(string value, Type type) | |
| { | |
| object typedExample; | |
| try | |
| { | |
| typedExample = TypeDescriptor.GetConverter(type).ConvertFrom(value); | |
| } | |
| catch (Exception) | |
| { | |
| return new OpenApiString(value); | |
| } | |
| return OpenApiPrimitiveFactory.CreateFrom(typedExample) ?? new OpenApiString(value); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment