Home > Enterprise >  JsonConvert.DeserializeObject defaults to first Enum item
JsonConvert.DeserializeObject defaults to first Enum item

Time:01-04

I have the following data object.

public class Response<T> where T : MyBaseDTO
    {
     public bool result
        {
            get; set;
        }

     public List<Message> messages
        {
            get; set;
        }
     public List<T> data 
        {
            get; set;
        }
}
     public class Message
        {      
            public Message(MessageTypeEnum type)
            {
                this.typeEnum = type;
            }
    
            public string type
            {
                get
                {
                    return typeEnum.ToString();
                }
         
            }
    
            public MessageTypeEnum typeEnum
            {
                get; set;
            }
          
           public int index
            {
                get; set;
            }
            
            public string field
            {
                get; set;
            }
            public string code
            {
                get; set;
            }
           public string message
             {
              get; set;
            }
           public string messageValue
            {
             get; set;
             }
        public enum MessageTypeEnum
        {
            WARNING,
            INFO,
            ERROR
        }

When I use RestSharp to call the API, the deserialized Data is null with following error(Content contains correct response in string):

" ErrorMessage "Each parameter in the deserialization constructor on type 'Response.Message' must bind to an object property or field on deserialization."

I found the following post and added empty constructor to my object.

Error: Each parameter in constructor must bind to an object property or field on deserialization

    public class Message
{
    public Message()
    {
    }
    public Message(MessageTypeEnum type)
    {
        this.typeEnum = type;
    }

    public string type
    {
        get
        {
            return typeEnum.ToString();
        }

    }

    public MessageTypeEnum typeEnum
    {
        get; set;
    }

    public int index
    {
        get; set;
    }

    public string field
    {
        get; set;
    }
    public string code
    {
        get; set;
    }
    public string message
        {
          get; set;
         }
   public string messageValue
         {
          get; set;
          }
    public enum MessageTypeEnum
    {
        WARNING,
        INFO,
        ERROR
    }

Added empty constructor resolved my issue with RestSharp. However I just noticed it failed some unit tests in my other project that uses Message Object.

Here is the code

using var reader = new StreamReader(result.ResponseMessage.Content.ReadAsStream());
body = reader.ReadToEnd();

Response<Test> responseObject = JsonConvert.DeserializeObject<Response<Test>>(body);

"body" looks like something below which is expected.

body    "{\"result\":true,\"messages\":[{\"type\":\"ERROR\",\"index\":0,\"field\":\"ID\",\"code\":\"MISSING-ID\",\"message\":\"The entry is incomplete.",\"messageValue\":null}],\"data\":[{\"....

However, after the deserialization, "Type" became WARNING instead ERROR and hence it failed the unit test.

Looks like it defaults to first one of the Enum.

public enum MessageTypeEnum
        {
            WARNING,
            INFO,
            ERROR
        }

Does anyone knows what's going on here and how to fix the issue?

CodePudding user response:

Your code is not complete and your JSON example is malformed, so I am unable to provide an example with your exact code. That said, you should be able to suss out the details you need to fix your particular issue.

First, when you are deserializing to a class you must have an empty constructor as required by the deserializer. The deserializer will not be passing any parameters to your constructor, so you can't depend on them.

Second, to read/write a class property it must have both a get and set. The deserializer will not be able to fully work with properties that do not have both get and set.


One possible solution, is to create a string property that contains both get and set, and then create an additional property with only the get that is typed to the Enum.

The root object is basically your Response object, but as you didn't provide that code I made the RootObject.

 public class RootObject
    {
        public bool result { get; set; }
        public Message[] messages { get; set; }
    }

    public class Message
    {
        // string property to allow deserialization
        public string type
        {
            get; set;
        }

        // read only property typed to the enum that will
        // refer to the 'type' property for conversion to the enum
        public MessageTypeEnum typeEnum
        {
            get
            {
                switch (type.ToUpper())
                {
                    case nameof(MessageTypeEnum.WARNING):
                        return MessageTypeEnum.WARNING;
                    case nameof(MessageTypeEnum.INFO):
                        return MessageTypeEnum.INFO;
                    case nameof(MessageTypeEnum.ERROR):
                        return MessageTypeEnum.ERROR;
                    default:
                        return MessageTypeEnum.UNKNOWN;
                        // return UNKNOWN or throw an out of range exception?
                }
            }
        }

        public int index
        {
            get; set;
        }

        public string field
        {
            get; set;
        }
        public string code
        {
            get; set;
        }

        public enum MessageTypeEnum
        {
            WARNING,
            INFO,
            ERROR
        }
    }

CodePudding user response:

since you have type as string too, you have to fix your Message costructor by adding attribute

    [JsonConstructor]
    public Message(MessageTypeEnum type)
    {
        this.typeEnum = type;
    }

after this everything is working properly

  • Related