5

I have a simple Controller with a POST method.
My model has a property of type enum.
When I send valid values ,everything works as expected

{  "MyProperty": "Option2"}

or

{  "MyProperty": 2}

If I send an invalid string

{  "MyProperty": "Option15"}

it correctly gets the default value (Option1) but if I send an invalid int ,it keep the invalid value

{  "MyProperty": 15}

enter image description here

Can I avoid that and get the default value or throw an error?

Thanks

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
}

Update
I know I can cast any int to an enum,that's not the problem.
@AakashM's suggestion solves half my problem,I didn't know about AllowIntegerValues

Now I correctly get an error when Posting an invalid int

{  "MyProperty": 15} 

The only problematic case now is when I post a string which is a number (which is strange because when I send an invalid non numeric string it correctly fails)

{  "MyProperty": "15"}
3
  • A quick look suggests no, not without unsetting AllowIntegerValues entirely. The standard behaviour of C# enums is unaltered by the converter - just as you could write MyEnum hmm = (MyEnum)15;, so the converter allows 15 here. Commented Feb 18, 2016 at 11:18
  • 6
    Not related to MVC, see stackoverflow.com/questions/6413804/… Commented Feb 18, 2016 at 11:18
  • @AakashM Thanks for the tip,it's half a solution.See my updated answer.If we don't find a better solution please post it as an asnwer and I'll accept it Commented Feb 18, 2016 at 12:21

2 Answers 2

5

I solved my problem by extending StringEnumConverter and using @ AakashM's suggestion

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;
    }

}
Sign up to request clarification or add additional context in comments.

2 Comments

+1 - just what I needed. I've taken the liberty of making changes to support other integral types (e.g. long, ulong), and to throw a JsonSerializationException rather than ArgumentException with a message whose format is the same as those generated by standard converters. It's a pity the JsonSerializationException.Create(reader, message) is internal ...
@joe I created a pull request for this but JameNewton told me this is fixed now.I haven't confirmed that myself but I'll try to later.
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;
    }
}

1 Comment

Thanks @TVOHM but since I have many models with many properties I'm looking for a generic solution like @ AakashM's. This is a valid answer though ,+1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.