字符串枚举转换器无效的整数值

5

我有一个带有POST方法的简单控制器。
我的模型具有枚举类型的属性。
当我发送有效值时,一切都按预期工作。

{  "MyProperty": "Option2"}

或者

{  "MyProperty": 2}

如果我发送一个无效的字符串
{  "MyProperty": "Option15"}

如果我传递一个无效的整数,它会保留该无效值,但是如果传递有效值,则会正确获取默认值(Option1)。

{  "MyProperty": 15}

enter image description here

我能避免这种情况并获取默认值或抛出错误吗?

谢谢

public class ValuesController : ApiController
{
    [HttpPost]
    public void Post(MyModel value) {}
}

public class MyModel
{
    [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
    public MyEnum MyProperty { get; set; }
}

public enum MyEnum
{
    Option1 = 0,
    Option2,
    Option3
}

更新
我知道我可以将任何int转换为枚举,这不是问题。
@AakashM的建议解决了我的一半问题,我之前不知道AllowIntegerValues

现在当我发布无效的int时,我会正确地收到错误提示。

{  "MyProperty": 15} 

现在唯一有问题的情况是当我发送一个字符串,它是一个数字时(这很奇怪,因为当我发送一个无效的非数字字符串时,它会正确地失败)。
{  "MyProperty": "15"}

初步看起来,没有办法在不完全取消AllowIntegerValues的情况下实现。C#枚举的标准行为并未受到转换器的影响 —— 就像你可以编写MyEnum hmm = (MyEnum)15;一样,转换器也允许在这里使用15 - AakashM
6
与MVC无关,请参考https://dev59.com/d2w15IYBdhLWcg3wxuch。 - CodeCaster
@AakashM 感谢你的提示,这是一半的解决方案。请查看我的更新答案。如果我们找不到更好的解决方案,请将其作为答案发布,我会接受它。 - George Vovos
2个回答

5

我通过扩展StringEnumConverter并使用@ AakashM的建议解决了我的问题

public class OnlyStringEnumConverter : StringEnumConverter
{
    public OnlyStringEnumConverter()
    {
        AllowIntegerValues = false;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (!AllowIntegerValues && reader.TokenType == JsonToken.String)
        {
            string s = reader.Value.ToString().Trim();
            if (!String.IsNullOrEmpty(s))
            {
                if (char.IsDigit(s[0]) || s[0] == '-' || s[0] == '+')
                {
                    string message = String.Format(CultureInfo.InvariantCulture, "Value '{0}' is not allowed for enum '{1}'.", s, objectType.FullName);
                    string formattedMessage = FormatMessage(reader as IJsonLineInfo, reader.Path, message);
                    throw new JsonSerializationException(formattedMessage);
                }
            }
        }
        return base.ReadJson(reader, objectType, existingValue, serializer);
    }

    // Copy of internal method in NewtonSoft.Json.JsonPosition, to get the same formatting as a standard JsonSerializationException
    private static string FormatMessage(IJsonLineInfo lineInfo, string path, string message)
    {
        if (!message.EndsWith(Environment.NewLine, StringComparison.Ordinal))
        {
            message = message.Trim();
            if (!message.EndsWith("."))
            {
                message += ".";
            }
            message += " ";
        }
        message += String.Format(CultureInfo.InvariantCulture, "Path '{0}'", path);
        if (lineInfo != null && lineInfo.HasLineInfo())
        {
            message += String.Format(CultureInfo.InvariantCulture, ", line {0}, position {1}", lineInfo.LineNumber, lineInfo.LinePosition);
        }
        message += ".";
        return message;
    }

}

+1 - 真是我所需要的。我已经做出了一些更改,以支持其他整数类型(例如:long、ulong),并且抛出了JsonSerializationException而不是ArgumentException,并生成与标准转换器生成的消息格式相同的消息。遗憾的是,JsonSerializationException.Create(reader, message)是内部的... - Joe
@joe,我已经为此创建了一个拉取请求,但JameNewton告诉我现在已经修复了。我自己还没有确认,但稍后我会尝试一下。 - George Vovos

1
MyEnum _myProperty;
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public MyEnum MyProperty 
{ 
    get
    {
        return _myProperty;
    }
    set
    {
        if (Enum.IsDefined(typeof(MyEnum), value))
            _myProperty = value;
        else
            _myProperty = 0;
    }
}

感谢@TVOHM的回答,但是由于我有许多具有许多属性的模型,所以我正在寻找像@ AakashM的通用解决方案。 这是一个有效的答案,加一。 - George Vovos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接