views:

1445

answers:

3

When I serialize a enum value using DataContractJsonSerializer, it serializes the numerical value of the enum, not the string name.

IE:

enum foo
{
   bar,
   baz
}

Serializing a value of foo.bar returns "0", not "bar".

I'd prefer it the other way around, is there a way to override this?

Edit:

Because I didn't want to change the serializer, I used a simple workaround hack.

I exposed a property in the class to serialize that calls ToString on the value, ie:

// Old
[DataMember]
public EnumType Foo
{
    get { return _foo; }
    set { _foo = value; }
}

// New, I still kept the EnumType but I only serialize the string version

public EnumType Foo
{
    get { return _foo; }
    set { _foo = value; }
}

[DataMember]
public string FooType
{
    get { return _foo.ToString(); }
    private set {}
}
+4  A: 

It looks like this is by design and this behavior cannot be changed:

Enumeration member values are treated as numbers in JSON, which is different from how they are treated in data contracts, where they are included as member names.

Here's an example using an alternative (and IMO better and more extensible) serializer which achieves what you are looking for:

using System;
using Newtonsoft.Json;

class Program
{
    static void Main(string[] args)
    {
        var baz = Foo.Baz;
        var serializer = new JsonSerializer();
        serializer.Converters.Add(new JsonEnumTypeConverter());
        serializer.Serialize(Console.Out, baz);
        Console.WriteLine();
    }
}

enum Foo
{
    Bar,
    Baz
}

public class JsonEnumTypeConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Foo);
    }
    public override void WriteJson(JsonWriter writer, object value)
    {
        writer.WriteValue(((Foo)value).ToString());
    }

    public override object ReadJson(JsonReader reader, Type objectType)
    {
        return Enum.Parse(typeof(Foo), reader.Value.ToString());
    }
}
Darin Dimitrov
+2  A: 

edit: Sorry just got up no coffee :(

Here is the code to do what you want to do with the Json Serializer, not the DataContractJsonSerializer.

I haven't done any work with DataContractJsonSerializer yet but after quickly scanning the docs, I am rather disappointed in MS. They obviously went to extremes to make WCF very extensible, but with the DataContractJsonSerializer there is no extensibility. You have to use MS flavored JSON with it. SUPER lame of MS ... already messing up WCF.

    using System;
    using System.Collections.Generic;
    using System.Runtime.Serialization;
    using System.Web.Script.Serialization;

Some Test Objects & Enum:

    public enum SomeSillyEnum
    {
        Foo,Bar,Doo,Daa,Dee
    }

    public class UseSillyEnum
    {
        public SomeSillyEnum PublicEnum { get; set; }
        public string SomeOtherProperty { get; set; }
        public UseSillyEnum()
        {
            PublicEnum = SomeSillyEnum.Foo;
            SomeOtherProperty = "Testing";
        }
    }

JavaScriptConverters. One for all enums, and one for an object using an enum.

public class EnumStringConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        foreach(string key in dictionary.Keys)
        {
            try { return Enum.Parse(type, dictionary[key].ToString(), false); }
            catch(Exception ex) { throw new SerializationException("Problem trying to deserialize enum from JSON.",ex); }
        }
        return Activator.CreateInstance(type);
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        Dictionary<string,object> objs = new Dictionary<string, object>();
        objs.Add(obj.ToString(), ((Enum)obj).ToString("D"));
        return objs;
    }

    public override IEnumerable<Type> SupportedTypes{get {return new Type[] {typeof (Enum)};}}
}

public class SillyConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        UseSillyEnum se = new UseSillyEnum();
        foreach (string key in dictionary.Keys)
        {
            switch(key)
            {
                case "PublicEnum":
                    se.PublicEnum = (SomeSillyEnum) Enum.Parse(typeof (SomeSillyEnum), dictionary[key].ToString(), false);
                    break;
                case "SomeOtherProperty":
                    se.SomeOtherProperty = dictionary[key].ToString();
                    break;
            }
        }
        return se;
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        UseSillyEnum se = (UseSillyEnum)obj;
        Dictionary<string, object> objs = new Dictionary<string, object>();
        objs.Add("PublicEnum", se.PublicEnum);
        objs.Add("SomeOtherProperty", se.SomeOtherProperty);
        return objs;
    }

    public override IEnumerable<Type> SupportedTypes { get { return new Type[] { typeof(UseSillyEnum) }; } }
}

And using it inside a page:

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        /* Handles ALL Enums

        JavaScriptSerializer jsonSer = new JavaScriptSerializer(); 
        jsonSer.RegisterConverters( new JavaScriptConverter[] { new EnumStringConverter() } );

        string json = jsonSer.Serialize(new UseSillyEnum());
        Response.Write(json);

        UseSillyEnum obj = jsonSer.Deserialize<UseSillyEnum>(json);
        Response.Write(obj.PublicEnum);

        */

        /* Handles Object that uses an enum */
        JavaScriptSerializer jsonSer = new JavaScriptSerializer();
        jsonSer.RegisterConverters( new JavaScriptConverter[] { new SillyConverter() } );
        string json = jsonSer.Serialize(new UseSillyEnum());
        Response.Write(json);

        UseSillyEnum obj = jsonSer.Deserialize<UseSillyEnum>(json);
        Response.Write(obj.PublicEnum);
    }
}
Chad Grant
A: 

To get 2 way serialization/deserilization for wcf json you can add a second get set property of type string, when you're construction your json object in javascript use the string named property, on the server side use the strongly typed enum version: e.g.

public class DTOSearchCriteria
{
    public int? ManufacturerID { get; set; }
    public int? ModelID { get; set; }


    private SortBy _sort;


    public SortBy SortType
    {
        get
        {
            return _sort;
        }
        set
        {
            _sort = value;
        }
    }

    public String Sort 
    {
        get
        {
            return _sort.ToString();
        }
        set
        {
            _sort = (SortBy) Enum.Parse(typeof(SortBy), value);
        }
    }





    public int PageSize { get; set; }
    public int PageNumber { get; set; }
}


public enum SortBy
{
    PriceDescending,
    PriceAscending
}
Jason Roberts