gpt4 book ai didi

c# - Swashbuckle 多态性支持问题

转载 作者:太空宇宙 更新时间:2023-11-03 14:51:15 25 4
gpt4 key购买 nike

我正在使用 Swashbuckle v3.0。

我不确定这是否是一个错误,但多态性并没有像它应该的那样工作。我有以下类(class):

BasePersonDocumentDto
{
Id,
Number
}

IdentityDto: BasePersonDocumentDto
{

}

PassportDto: BasePersonDocumentDto
{
VisaNumber
}

为了应用继承和多态性,我创建了一个模式和文档过滤器。 I followed this answer以下是我使用的代码。

public class PolymorphismSchemaFilter<T> : ISchemaFilter
{
private List<Type> derivedTypes = new List<Type>() { typeof(IdentityDto), typeof(PassportDto) };

public void Apply(Schema model, SchemaFilterContext context)
{
if (!derivedTypes.Contains(context.SystemType)) return;

var baseSchema = new Schema() { Ref = "#/definitions/" + typeof(T).Name };
var clonedBaseSchema = new Schema
{
Properties = model.Properties,
Type = model.Type,
Required = model.Required
};

model.AllOf = new List<Schema> { baseSchema, clonedBaseSchema };

//Reset properties for they are included in allOf, should be null but code does not handle it
model.Properties = new Dictionary<string, Schema>();
}
}


public class PolymorphismDocumentFilter<T> : IDocumentFilter
{
private List<Type> derivedTypes = new List<Type>() { typeof(IdentityDto), typeof(PassportDto) };

public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
const string discriminatorName = "discriminator";

var baseSchema = context.SchemaRegistry.Definitions[typeof(T).Name];

//Discriminator property
baseSchema.Discriminator = discriminatorName;
baseSchema.Required = new List<string> { discriminatorName };

if (!baseSchema.Properties.ContainsKey(discriminatorName))
baseSchema.Properties.Add(discriminatorName, new Schema { Type = "string" });

//Register dervied classes
foreach (var item in derivedTypes)
context.SchemaRegistry.GetOrRegister(item);
}
}

生成的定义是:

//This is for BasePersonDocumentDto and for IdentityDto
"BasePersonDocumentDto":{
"required":[
"discriminator"
],
"type":"object",
"properties":{
"number":{
"type":"string"
},
"id":{
"format":"int32",
"type":"integer"
},
"discriminator":{
"type":"string"
}
},
"discriminator":"discriminator"
}

"PassportDto":{
"required":[
"discriminator"
],
"type":"object",
"properties":{
"number":{
"type":"string"
},
"id":{
"format":"int32",
"type":"integer"
},
"visaNumber":{
"type":"string"
},
"discriminator":{
"type":"string"
}
},
"discriminator":"discriminator"
}

现在进行测试,我创建了一个具有 List 属性的 InputDto。我尝试传递以下 JSON,但两个传递的项目都显示为 BasePersonDocumentDto,即使正确传递了鉴别器。这是我传递的 JSON:

{  
"documentsDto":[
{
"discriminator":"IdentityDto"
},
{
"visaNumber":"RRXX323TR",
"discriminator":"PassportDto"
}
]
}

最佳答案

一段时间后,我找到了解决问题的方法。我的情况很简单。我只有一个基类和一些派生类,但是 swagger 只生成了基类而不是派生类。

为了解决这个问题,我使用了我在问题中发布的代码,它应用了继承,还添加了一个名为“鉴别器”的隐式列。此列用于区分子类型。

最后一个问题是在反序列化为对象时,swagger 无法使用“鉴别器”列,因此它总是映射到基本类型。我找到了两个解决方案来解决这个问题,第一个是实现自定义模型绑定(bind)器,第二个是实现自定义 JsonConverter。

我选择了第二个选项,因为它看起来更合乎逻辑,因为我的问题是处理反序列化 Json。除了将 JsonConverter 与我的基类一起使用外,我还装饰了它的“KnownType”属性。

[KnownType(typeof(IdentityDto))]
[KnownType(typeof(PassportDto))]
[JsonConverter(typeof(InheritanceConverter))]
BasePersonDocumentDto
{
Id,
Number
}

下面是 JsonConverter 的代码:

/// <summary>
/// Json deserialization for inheritance structure with discriminator
/// </summary>
public class InheritanceConverter : JsonConverter
{
/// <summary>
/// Default name for the discriminator property
/// </summary>
private string _discriminator { get; set; } = "discriminator";

public InheritanceConverter()
{

}

/// <summary>
/// Discriminator name to map between types
/// </summary>
/// <param name="discriminator">The discriminator property name. The defualt value is 'discriminator'</param>
public InheritanceConverter(string discriminator)
{
if (!string.IsNullOrWhiteSpace(discriminator))
_discriminator = discriminator;
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//Check null
if (reader.TokenType == JsonToken.Null) return null;

//Parse json
var jDerivedObject = JObject.Load(reader);

//Get discriminator
var discriminator = jDerivedObject.Value<string>(_discriminator);
if (string.IsNullOrWhiteSpace(discriminator))
throw new Exception("Invalid discriminator value");

//Get the type
var derivedType = GetSubType(discriminator, objectType);

//Create a new instance of the target type
var derivedObject = Activator.CreateInstance(derivedType);

//Populate the derived object
serializer.Populate(jDerivedObject.CreateReader(), derivedObject);

return derivedObject;
}

//TODO:- validate based on the base and sub-types via the KnownTypeAttributes
public override bool CanConvert(Type objectType) => true;

//Not required
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}

//Not required
public override bool CanWrite => false;

#region Methods

/// <summary>
/// Get sub-type via KnownTypeAttributes
/// </summary>
/// <param name="derivedTypeName">The target type name which corosponds to the discriminator</param>
private Type GetSubType(string derivedTypeName, Type baseType)
{
var knownTypes = baseType.GetCustomAttributes(false).Where(ca => ca.GetType().Name == "KnownTypeAttribute").ToList();

if (knownTypes == null || knownTypes.Count == 0)
throw new Exception(
string.Format("Couldn't find any KnownAttributes over the base {0}. Please define at least one KnownTypeAttribute to determine the sub-type", baseType.Name));

foreach (dynamic type in knownTypes)
{
if (type.Type != null && type.Type.Name.ToLower() == derivedTypeName.ToLower())
return type.Type;
}

throw new Exception(string.Format("Discriminator '{0}' doesn't match any of the defined sub-types via KnownTypeAttributes", derivedTypeName));
}

#endregion
}

关于c# - Swashbuckle 多态性支持问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51692739/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com