为了账号安全,请及时绑定邮箱和手机立即绑定

复杂类型枚举模型绑定

复杂类型枚举模型绑定

C#
哈士奇WWW 2023-09-24 15:49:36
背景在 .NET Core 中,如果模型在其层次结构中包含任何位置并且提供的值与 .NET Core 中的名称不完全匹配,则默认控制器模型绑定将失败(null为操作参数生成值)。空格或奇怪的大写字母会破坏绑定,这对于我的 API 端点的使用者来说似乎不友好。enumenum我的解决方案我创建了一个模型绑定程序提供程序,它使用反射来确定目标绑定类型中的某个位置是否存在enum; 如果此检查为真,它将返回一个自定义模型绑定器(通过传递类型构造enum),它使用正则表达式/字符串操作(粗略)来扫描请求正文中的值enum,并努力将它们解析为该enum类型中的名称,在进行JsonConvert反序列化之前。在我看来,这个解决方案对于我想要实现的目标来说过于复杂和丑陋。我想要的是类似 JsonConvert 属性(对于我的enum字段)的东西,它可以在绑定/反序列化期间进行此工作。Newtonsoft 的开箱即用解决方案 ( StringEnumConverter) 不会尝试调整字符串以适合类型enum(我想是公平的),但我无法在这里扩展 Newtonsoft 的功能,因为它依赖于很多内部类(无需复制和粘贴)他们的大量代码)。管道中是否有我遗漏的部分可以更好地利用来满足这一需求?PS我把这个放在这里,而不是代码审查(太理论化)或软件工程(太具体);如果地方不对请指教。
查看完整描述

1 回答

?
ibeautiful

TA贡献1993条经验 获得超5个赞

我为此使用了类型安全枚举模式,我认为它适合您。通过 TypeSafeEnum,您可以使用 Newtonsoft 的 JsonConverter 属性控制映射到 JSON 的内容。由于您没有要发布的代码,我已经构建了一个示例。


应用程序的 TypeSafeEnums 使用的基类:


public abstract class TypeSafeEnumBase

{

    protected readonly string Name;

    protected readonly int Value;


    protected TypeSafeEnumBase(int value, string name)

    {

        this.Name = name;

        this.Value = value;

    }


    public override string ToString()

    {

        return Name;

    }

}

作为 TypeSafeEnum 实现的示例类型,它通常是一个普通的 Enum,包括 Parse 和 TryParse 方法:


public sealed class BirdType : TypeSafeEnumBase

{

    private const int BlueBirdId = 1;

    private const int RedBirdId = 2;

    private const int GreenBirdId = 3;

    public static readonly BirdType BlueBird = 

        new BirdType(BlueBirdId, nameof(BlueBird), "Blue Bird");

    public static readonly BirdType RedBird = 

        new BirdType(RedBirdId, nameof(RedBird), "Red Bird");

    public static readonly BirdType GreenBird = 

        new BirdType(GreenBirdId, nameof(GreenBird), "Green Bird");


    private BirdType(int value, string name, string displayName) :

        base(value, name)

    {

        DisplayName = displayName;

    }


    public string DisplayName { get; }


    public static BirdType Parse(int value)

    {

        switch (value)

        {

            case BlueBirdId:

                return BlueBird;

            case RedBirdId:

                return RedBird;

            case GreenBirdId:

                return GreenBird;

            default:

                throw new ArgumentOutOfRangeException(nameof(value), $"Unable to parse for value, '{value}'. Not found.");

        }

    }


    public static BirdType Parse(string value)

    {

        switch (value)

        {

            case "Blue Bird":

            case nameof(BlueBird):

                return BlueBird;

            case "Red Bird":

            case nameof(RedBird):

                return RedBird;

            case "Green Bird":

            case nameof(GreenBird):

                return GreenBird;

            default:

                throw new ArgumentOutOfRangeException(nameof(value), $"Unable to parse for value, '{value}'. Not found.");

        }

    }


    public static bool TryParse(int value, out BirdType type)

    {

        try

        {

            type = Parse(value);

            return true;

        }

        catch

        {

            type = null;

            return false;

        }

    }


    public static bool TryParse(string value, out BirdType type)

    {

        try

        {

            type = Parse(value);

            return true;

        }

        catch

        {

            type = null;

            return false;

        }

    }

}

用于处理类型安全转换的容器,因此您无需为实现的每个类型安全创建转换器,并在实现新的类型安全枚举时防止 TypeSafeEnumJsonConverter 发生更改:


public class TypeSafeEnumConverter

{

    public static object ConvertToTypeSafeEnum(string typeName, string value)

    {

        switch (typeName)

        {

            case "BirdType":

                return BirdType.Parse(value);

            //case "SomeOtherType": // other type safe enums

            //    return // some other type safe parse call

            default:

                return null;

        }

    }

}

实现 Newtonsoft 的 JsonConverter,它又调用我们的 TypeSafeEnumConverter


public class TypeSafeEnumJsonConverter : JsonConverter

{

    public override bool CanConvert(Type objectType)

    {

        var types = new[] { typeof(TypeSafeEnumBase) };

        return types.Any(t => t == objectType);

    }


    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

    {

        string name = objectType.Name;

        string value = serializer.Deserialize(reader).ToString();

        return TypeSafeEnumConversion.ConvertToTypeSafeEnum(name, value); // call to our type safe converter

    }


    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)

    {

        if (value == null && serializer.NullValueHandling == NullValueHandling.Ignore)

        {

            return;

        }

        writer.WriteValue(value?.ToString());

    }

}

使用 BirdType 并设置要使用的转换器的示例对象:


public class BirdCoup

{

    [JsonProperty("bird-a")]

    [JsonConverter(typeof(TypeSafeEnumJsonConverter))] // sets the converter used for this type

    public BirdType BirdA { get; set; }


    [JsonProperty("bird-b")]

    [JsonConverter(typeof(TypeSafeEnumJsonConverter))] // sets the converter for this type

    public BirdType BirdB { get; set; }

}

使用示例:


// sample #1, converts value with spaces to BirdTyp

string sampleJson_1 = "{\"bird-a\":\"Red Bird\",\"bird-b\":\"Blue Bird\"}";

BirdCoup resultSample_1 = 

JsonConvert.DeserializeObject<BirdCoup>(sampleJson_1, new JsonConverter[]{new TypeSafeEnumJsonConverter()});


// sample #2, converts value with no spaces in name to BirdType

string sampleJson_2 = "{\"bird-a\":\"RedBird\",\"bird-b\":\"BlueBird\"}";

BirdCoup resultSample_2 = 

JsonConvert.DeserializeObject<BirdCoup>(sampleJson_2, new JsonConverter[] { new TypeSafeEnumJsonConverter() });



查看完整回答
反对 回复 2023-09-24
  • 1 回答
  • 0 关注
  • 106 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信