Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialize and deserialize Enum values as strings #64

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

jasonmead
Copy link

Fixes #15

@amccorma
Copy link

this issue is not working in the current version. working on a fix to the code.

@amccorma
Copy link

   public virtual object DeserializeObject(object value, Type type)
        {
            if (type == null) throw new ArgumentNullException("type");
            string str = value as string;

            if (type == typeof (Guid) && string.IsNullOrEmpty(str))
                return default(Guid);

            if (value == null)
                return null;

            object obj = null;

            if (str != null)
            {
                if (str.Length != 0) // We know it can't be null now.
                {
                    if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime)))
                        return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
                    if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset)))
                        return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
                    if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)))
                        return new Guid(str);
                    if (type == typeof(Uri))
                    {
                        bool isValid =  Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute);

                        Uri result;
                        if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result))
                            return result;

                                                return null;
                    }

                                    if (type == typeof(string))  
                                        return str;

                                    return Convert.ChangeType(str, type, CultureInfo.InvariantCulture);
                }
                else
                {
                    if (type == typeof(Guid))
                        obj = default(Guid);
                    else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))
                        obj = null;
                    else
                        obj = str;
                }
                // Empty string case
                if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))
                    return str;
            }
            else if (value is bool)
                return value;

            bool valueIsLong = value is long;
            bool valueIsDouble = value is double;
            if (type.IsEnum)
            {
                if (value is double || value is int || value is long)
                {
                    return Enum.ToObject(type, Convert.ToInt32(value.ToString()));
                }
                else if (value is string)
                {
                    return Enum.Parse(type, value.ToString());
                }
            }
            if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double)))
                return value;
            if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long)))
            {
                obj = type == typeof(int) || type == typeof(long) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short)
                            ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture)
                            : value;
            }
            else
            {
                IDictionary<string, object> objects = value as IDictionary<string, object>;
                if (objects != null)
                {
                    IDictionary<string, object> jsonObject = objects;

                    if (ReflectionUtils.IsTypeDictionary(type))
                    {
                        // if dictionary then
                        Type[] types = ReflectionUtils.GetGenericTypeArguments(type);
                        Type keyType = types[0];
                        Type valueType = types[1];

                        Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType);

                        IDictionary dict = (IDictionary)ConstructorCache[genericType]();

                        foreach (KeyValuePair<string, object> kvp in jsonObject)
                            dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType));

                        obj = dict;
                    }
                    else
                    {
                        if (type == typeof(object))
                            obj = value;
                        else
                        {
                            obj = ConstructorCache[type]();
                            foreach (KeyValuePair<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> setter in SetCache[type])
                            {
                                object jsonValue;
                                if (jsonObject.TryGetValue(setter.Key, out jsonValue))
                                {
                                    jsonValue = DeserializeObject(jsonValue, setter.Value.Key);
                                    setter.Value.Value(obj, jsonValue);
                                }
                            }
                        }
                    }
                }
                else
                {
                    IList<object> valueAsList = value as IList<object>;
                    if (valueAsList != null)
                    {
                        IList<object> jsonObject = valueAsList;
                        IList list = null;

                        if (type.IsArray)
                        {
                            list = (IList)ConstructorCache[type](jsonObject.Count);
                            int i = 0;
                            foreach (object o in jsonObject)
                                list[i++] = DeserializeObject(o, type.GetElementType());
                        }
                        else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type))
                        {
                            Type innerType = ReflectionUtils.GetGenericListElementType(type);
                            list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count);
                            foreach (object o in jsonObject)
                                list.Add(DeserializeObject(o, innerType));
                        }
                        obj = list;
                    }
                }
                return obj;
            }
            if (ReflectionUtils.IsNullableType(type))
                return ReflectionUtils.ToNullableType(obj, type);
            return obj;
        }

@amccorma
Copy link

amccorma commented Apr 26, 2016

Here the code change:

Method:

* public virtual object DeserializeObject(object value, Type type)*

line: 1386

Current version at time of change is 0.38.0
or copy method above and replace. tested with 4 enums.

 if (type.IsEnum)
            {
                if (value is double || value is int || value is long)
                {
                    return Enum.ToObject(type, Convert.ToInt32(value.ToString()));
                }
                else if (value is string)
                {
                    return Enum.Parse(type, value.ToString());
                }
            }

@jasonmead
Copy link
Author

Submit a PR with tests, please.

@amccorma
Copy link

I have never done a PR request. reading up on google and going try it.

@amccorma
Copy link

I followed this: https://guides.github.com/activities/contributing-to-open-source/

I did the fork. made a branch (enumfix). compiled solution. updated source. committed branch. added tests (EnumTest). unsure what do to next. the code been committed to the branch.

Everything compiles. test worked ok. checked in.

@jasonmead
Copy link
Author

The next step would be to open a pull request from your forked branch.

@amccorma amccorma mentioned this pull request Apr 27, 2016
@jchannon
Copy link

@jasonmead this is great, i'm not sure what the diff between your PR and @amccorma PR #72 although that PR I think has line ending issues as the whole simplejson file has changed. I'd like to get this into Nancy https://github.com/NancyFx/Nancy for the JSON work you did.

Including @prabirshrestha in this too.

What needs to be done to get this feature in ASAP?

@jasonmead
Copy link
Author

This will have to be implemented in Nancy directly. Unfortunately, we had to modify SimpleJson.cs in Nancy to get it to work correctly.

@prabirshrestha
Copy link
Member

@jchannon I will be out of town. will have to look at this next week.

@Deepscorn
Copy link

Deepscorn commented Mar 26, 2017

What is the status? I need enum support to use SimpleJson in a Unity game (last stable version of which sadly provides us, programmers, with C# 4 and .NET 2.0-3.5 at the time of writing which don't make much of choise when handling json). It's the only thing, that stops me from using SimpleJson. I need something like this to work:

[DataContract]
    public enum Keeper
    {
        [DataMember(Name = "no keeper")]
        None,
        [DataMember(Name = "Mary Rose")]
        Mary,
        [DataMember(Name = "Tom The Third")]
        Tom,
        [DataMember(Name = "Simply, Todd")]
        Todd
    }

[DataContract]
    public class Animal : JsonResponse
    {
        [NotNull]
        [DataMember(Name = "animal color")] // works ok
        public string Color { get; private set; }
        
        [DataMember(Name = "animal keeper")] // fails, enum serialized as number ( 0 for None, 1 for Mary and so on)
        public Keeper Keeper { get; private set; }
}

So, needed serialized example of animal:
{ "animal color" : "red", "animal keeper" : "Mary Rose" }

Actual serialized example of animal:
{ "animal color" : "red", "animal keeper" : 1 }

I came from java and there we successfully used (for example) com.google.api.client.util.Value attribute to specify [de]serialized string value for each enum member:

public enum CardStatus {
    @Value("active")
    ACTIVE,
    @Value("expired")
    EXPIRED,
    @Value("blocked")
    BLOCKED
}

public class Card {
    @Key("card id")
    private String id;
    @Key("status")
    private CardStatus status;
}

So, serialized example of Card:
{ "card id" : "101010", "status" : "expired" }

PS com.google.api.client.util.Value is in com.google.http-client:google-http-client-android:1.22.0 or com.google.http-client:google-http-client-jackson2:1.22.0 - don't remember exactly, allways imported both at the same time

PS I tested code of that merge request and it gave me:
{ "animal color" : "red", "animal keeper" : "Mary" }
As you see, sadly, DataMember attribute ignored

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deserialize Enums
5 participants