简体   繁体   中英

Check enum null when it is not nullable and do not have any null options

This is different from questions like below

How to tell if an enum property has been set? C#

I am working on creating WCF Rest service using existing DataContract classes. I cannot change property datatypes like enum to enum? and also cannot add new option in my enum like undefined, none, or by default set anything since if I do anyone of these changes it will be firm wide impact and many applications depend on it.

Normally people call my WCF REST Service using applications like POSTMAN where they send in json data like below in which Gender is an enum with Male, Female, Transgender, etc. If they do not send it my service throws exception and I want to add validation logic to check whether enum is null or not when QA scall my service using POSTMAN and send JSON data even though it is not nullable and also do not have any None, Null options in my enum? If it is NULL I want to send ArgumentNullException back to callers with nice message. I want to handle this situation gracefully.

public enum Gender 
{
    Male = 0,
    Female = 1,
    Transgender = 2
}

Below is good

{
      "Name" : "XXX"
      "Gender" : "1"
}

Below throws error

{
      "Name" : "XXX"
      "Gender" : ""
}

SOLUTION:

Thanks to pswg for pointing in correct direction and I marked his answer below. I am using Newtonsoft so I did like below

string stringfydata = Newtonsoft.Json.JsonConvert.SerializeObject(requestGender);
if(string.IsNullOrEmpty(stringfydata))
{
   throw new ArgumentNullException("Gender value cannot be NULL or Empty.");
}

Other than the obvious option of warping the enum in a class, which might not work in your specific situation, you can set the enum variable to a integer out of the enum range. After that, you can then check to see if the integer is defined within the enum. Since C# does not check enumerations , you can do the following:

    public enum Gender
    {
        Male = 0,
        Female = 1,
        Transgender = 2
    }

    public int HandleGender(string strJsonGender){
        if (strJsonGender == "")
        {
            return -1;
        }
        else {
            // Get int representation of the gender
            return (int)((Gender)Enum
                    .Parse(typeof(Gender),
                           strJsonGender, true));
        }
    }

    public void MainMethod(string strJsonGender) {
        Gender gVal;
        int iVal = HandleGender(strJsonGender);

        if (Enum.IsDefined(typeof(Gender), iVal))
        {
            // Handle if the an actual gender was specified
            gVal = (Gender)iVal;
        }
        else { 
            // Handle "null" logic
        }

Note: the answers below use DataContracts since you've indicated in your question, but similar solutions exist for Json.Net serialization .

You can use [DataMember(EmitDefaultValue = false)] to ignore cases where Gender is not specified at all. In this case, the value that's returned will be whatever enum member is assigned a value of 0 (note that if no member has that value, you'll still get a value of 0 , which could be useful for you).

[DataContract]
class Person
{
    [DataMember]
    public string Name { get; set; }

    [DataMember(EmitDefaultValue = false)]
    public Gender Gender { get; set; }
}


void Main()
{
    var json = "{\"Name\": \"XXX\"}";
    var ser = new DataContractJsonSerializer(typeof(Person));
    var obj = ser.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(json)));
    obj.Dump(); // Person { Name = "XXX", Gender = Male }
}

To handle cases where an empty string is provided instead of a valid value or no value at all, you can use this hacky little trick:

[DataContract]
class Person
{
    [DataMember]
    public string Name { get; set; }

    [IgnoreDataMember]
    public Gender Gender
    {
        get
        {
            if (GenderValue.GetType() == typeof(string))
            {
                Enum.TryParse((string)GenderValue, out Gender result);
                return result;
            }
            return (Gender)Convert.ToInt32(GenderValue);
        }
        set
        {
            GenderValue = value;
        }
    }

    [DataMember(Name = "Gender", EmitDefaultValue = false)]
    private object GenderValue { get; set; }
}


void Main()
{
    var json = "{\"Name\": \"XXX\", \"Gender\": \"\"}";
    var ser = new DataContractJsonSerializer(typeof(Person));
    var obj = ser.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(json)));
    obj.Dump(); // Person { Name = "XXX", Gender = Male }
}

However, this is somewhat awkward and can be easily abused. I'd recommend caution with this approach. As others have mentioned, we typically want to throw errors whenever invalid values are provided to a function / API. By 'failing fast' you let the user attempting to use the API know that they've constructed a request that's likely to produce unexpected results at some point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM