0

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 p.s.w.g 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.");
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Ziggler
  • 3,361
  • 3
  • 43
  • 61
  • 2
    I don't see a question here, and I'm not sure what you're trying to achieve. What is the desired behavior when the client sends `"Gender": ""`? I suppose you could always provide a custom Json serializer to infer a different value when none is provided. – p.s.w.g Jun 20 '19 at 16:34
  • @RufusL.. I bolded my question. Is it good ? – Ziggler Jun 20 '19 at 16:40
  • How do you want to check if `Gender` is null if it is not nullable? – Eliahu Aaron Jun 20 '19 at 16:42
  • @Ziggler What you can do is, as p.s.w.g implies, add custom JSON serialization to set the property to `(Gender)(-1)` when you get an empy string. Then use `(Gender)(-1)` as your "null" value in your code. – 15ee8f99-57ff-4f92-890c-b56153 Jun 20 '19 at 16:42
  • @p.s.w.g and EdPlunkett … I am trying p.s.w.g suggestion... – Ziggler Jun 20 '19 at 16:44
  • 1
    If something is not nullable, then it can't be `null`. It might be helpful if you show the code that's throwing an exception (likely where you deserialize the input). But it's also not clear what you want to do in the case where `Gender` is not specified. Do you want to default the value to `Male`, `Female`, or `Transgender`? It seems to me that the current behavior is most correct - throw an exception when a required argument is missing. – Rufus L Jun 20 '19 at 16:44
  • @EdPlunkett I don't fully understand the architecture here, but it seems likely that if we assign `Gender` an undefined value like `-1`, something's going to "crash" sooner or later unless a default `Gender` is assumed at some point. But maybe that's not correct? I'm arguing that the service should not consider `""` a *"normal"* value for gender. – Rufus L Jun 20 '19 at 16:59
  • Seriously I cannot understand why this question is getting negative points. It pretty straight forward. QAs are testing my service and in postman they are sending me JSON data like this {"Name" : "XXX", "Gender" : "" }. My client app will never set Gender as NULL. QAs were able to send null and they created a BUG and I am trying to fix it. Thanks to p.s.w.g he pointed me in correct direction now working on it. – Ziggler Jun 20 '19 at 17:02
  • 1
    @Ziggler So is the goal to fail more gracefully when you receive invalid enum values, and send more useful error information back to the client? – 15ee8f99-57ff-4f92-890c-b56153 Jun 20 '19 at 17:04
  • 1
    I didn't down-vote, but I think the unclear part of the question is that you never stated what you want to do when an empty string is passed in for an `enum` that has no "default" value, which was asked in the first comment. – Rufus L Jun 20 '19 at 17:05
  • @EdPlunkett … yes whatever you said above is absolutely correct and what I want. – Ziggler Jun 20 '19 at 17:12

2 Answers2

2

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
        }
Cabbage Champion
  • 1,193
  • 1
  • 6
  • 21
1

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.

Sebastian Inones
  • 1,561
  • 1
  • 19
  • 32
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • Thanks p.s.w.g for pointing me correct direction. I followed your suggestion and fixed my issue. I did like this. string stringfydata = Newtonsoft.Json.JsonConvert.SerializeObject(requestGender); after this I check whether stringfydata is null or not. If it is NULL I am sending them back ArgumentNullException. – Ziggler Jun 20 '19 at 17:09