【问题标题】:Generating swagger for Dictionary<SomeEnum, string>为 Dictionary<SomeEnum, string> 生成招摇
【发布时间】:2021-08-22 01:23:34
【问题描述】:

我正在使用 Swashbuckle.AspNetCore.Swagger 生成一个 swagger 文档,然后使用 NSwag 生成一个 C# SDK。

我有几个类,我使用 Dictionary 来保存杂项属性。

namespace Sample
{
    /// <summary>Possible properties for MyClass1 objects</summary>
    [JsonConverter(typeof(StringEnumConverter))]
    public enum PropEnum1
    {
        /// <summary>OpenAPI doesn't see this description</summary>
        PropName1,
        PropName2,
        PropName3,
        // and more property names
    }

    /// <summary>The first class...</summary>
    public class MyClass1
    {
        public string Name { get; set; }

        /// <summary>Properties that vary from instance to instance.</summary>
        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
        public IDictionary<PropEnum1, string> Props { get; set; }
    }

    // There is also a PropEnum2 and MyClass2, but let's stay simple
}

使用 Swashbuckle 6.14,我最终得到了如下所示的招摇:

"MyClass1": {
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "nullable": true
    },
    "props": {
      "type": "object",
      "properties": {
        "PropName1": {
          "type": "string"
        },
        "PropName2": {
          "type": "string"
        },
        "PropName3": {
          "type": "string"
        }
      },
      "additionalProperties": false,
      "description": "Properties that vary from instance to instance.",
      "nullable": true
    }
  },
  "additionalProperties": false,
  "description": "The first class..."
}

然后 NSwag 生成一个如下所示的 C# 接口:

/// <summary>The first class...</summary>
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")]
public partial class MyClass1
{
    [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public string Name { get; set; }

    /// <summary>Properties that vary from instance to instance.</summary>
    [Newtonsoft.Json.JsonProperty("props", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public Props Props { get; set; }
}

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v12.0.0.0)")]
public partial class Props
{
    [Newtonsoft.Json.JsonProperty("PropName1", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public string PropName1 { get; set; }

    [Newtonsoft.Json.JsonProperty("PropName2", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public string PropName2 { get; set; }

    [Newtonsoft.Json.JsonProperty("PropName3", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public string PropName3 { get; set; }
}

我发现每个可能的枚举值都有一个成员的 Props 类的生成有点令人惊讶,但我可以接受它。

问题是 MyClass1 的实例可能没有所有属性。我有一个返回 MyClass1 实例的 API。它可以在 swagger 页面上运行,但是使用 NSwag SDK 会给我一个错误“Newtonsoft.Json.JsonSerializationException:必需的属性 'PropName2' 需要一个非空值。”

当前的问题是生成的 Props 类中的 DisallowNull。如果我将它们从 DisallowNull 手动编辑为 Default,SDK 将按预期工作。但是手工编辑生成的代码并不好。

DisallowNull 来自 swagger 中的 PropName 属性,没有将“nullable”设置为 true。如果我暂停生成过程并将它们手动编辑到招摇中,我会在生成的 C# 代码中得到Required.Default。但是手工编辑生成的招摇也不好。

因此,解决它的一种方法是在 swagger 中的属性中添加一个“可空”,但我不知道该怎么做。

或者有没有办法让 Swashbuckle 将 Props 字典更像 Dictionary

this post 看来, Dictionary 似乎是这样处理的,但那可能是旧的。

【问题讨论】:

  • 我发现,在处理某些无法在 yaml/json 中准确或轻松表示的 C# 结构时,手动编辑从 swagger 生成的代码有时是一种必要的弊端。海事组织。您可能想更改返回字典的结构,但是,我只是手动更改它。
  • @DekuDesu 虽然我不为 Google 工作,但我尽量不作恶。 :) 具体来说,我不希望其他人必须记住手动编辑。不过,我确实有选择。我可以编写一个脚本来进行编辑。我可以更改原始源以使用 Dictionary,尽管这会失去一些类型安全性。 (为了招摇,我在我的 API 中将 [Flag] 枚举更改为普通枚举数组。)我想我会研究一个 Swashbuckle 过滤器来改变招摇的生成。
  • 您的目标是 Swagger JSON 2.0 还是 3.0?(默认为 3.0)
  • 3.0(执行 SerializeAsV3() 为 NSwag 生成 swagger 文件)。
  • 我到了某个地方 - Swashbuckle.AspNetCore 页面上的 DictionaryTKeyEnumTValueSchemaFilter 示例与我想要的完全相反。

标签: c# swagger swashbuckle


【解决方案1】:

这就是我最终的结果。它将类型转换为通用对象(其中属性必须是 valueType。请注意,我已经将枚举序列化为字符串,因此这可能不适合其他人。

    /// <summary>
    /// For properties that are Dictionary[SomeEnum, valueType] alter the schema
    /// so the generated SDK code will be IDictionary[string, valueType].
    /// </summary>
    public class EnumDictionaryToStringDictionarySchemaFilter : ISchemaFilter
    {
        /// <summary>
        /// Apply the schema changes
        /// </summary>
        /// <param name="schema">The schema model</param>
        /// <param name="context">The schema filter context</param>
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            // Only for fields that are Dictionary<Enum, TValue>
            //
            if (!context.Type.IsGenericType)
                return;

            if (!context.Type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))
                && !context.Type.GetGenericTypeDefinition().IsAssignableFrom(typeof(IDictionary<,>)))
                return;

            var keyType = context.Type.GetGenericArguments()[0];

            if (!keyType.IsEnum)
                return;

            var valueType = context.Type.GetGenericArguments()[1];
            var valueTypeSchema = context.SchemaGenerator.GenerateSchema(valueType, context.SchemaRepository);

            schema.Type = "object";
            schema.Properties.Clear();
            schema.AdditionalPropertiesAllowed = true;
            schema.AdditionalProperties = valueTypeSchema;
        }
    }

【讨论】:

    猜你喜欢
    • 2010-10-17
    • 1970-01-01
    • 2022-01-23
    • 2017-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多