2009-04-27 95 views
37

當我使用DataContractJsonSerializer序列化枚舉值時,它序列化枚舉的數值,而不是字符串名稱。DataContractJsonSerializer和枚舉

IE:

enum foo 
{ 
    bar, 
    baz 
} 

序列化的foo.bar返回 「0」 值,而不是 「欄」。

我更喜歡它的另一種方式,有沒有辦法來覆蓋這個?

編輯:

因爲我沒有想改變串行,我用了一個簡單的解決方法破解。

我在類露出的屬性來序列,關於值調用的ToString,即:

// 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 {} 
} 
+0

這沒有太大的意外,因爲枚舉默認爲int類型。 – Powerlord 2009-04-27 21:03:37

+0

在XML序列化中,還建議避免暴露wcf數據合約中的枚舉,因爲它們會創建微妙的向後兼容問題。請參閱http://stackoverflow.com/questions/326339/do-you-use-enum-types-in-your-wcf-web-services – 2013-07-21 09:02:12

回答

25

It looks like this is by design而這種行爲不能被改變:

枚舉部件的值被視爲 作爲JSON中的數字,與 合約中的數據處理方式 不同,它們包含在 成員名稱中。

下面是使用an alternative(和海事組織更好,更可擴展)串行器爲例,其實現你在找什麼:

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()); 
    } 
} 
+7

我相信這樣做的新方法是`jsonSerializer.Converters.Add(new StringEnumConverter()); ` - 自4.5 – shanabus 2012-04-27 20:52:29

3

編輯:對不起,剛剛起牀沒有咖啡:(

這裏是代碼做你想要做什麼與Json串行器,而不是DataContractJsonSerializer。

我還沒有做DataContractJsonSerializer的任何工作,但在quic之後kly掃描文檔,我對MS很失望。他們顯然走極端,使WCF非常可擴展,但與DataContractJsonSerializer沒有可擴展性。你必須使用MS風格的JSON。超級瘸腿的MS ...已經搞亂了WCF。

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

一些測試對象&枚舉:

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。一個用於所有枚舉,另一個用於使用枚舉的對象。

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) }; } } 
} 

,並用它在頁面內:

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); 
    } 
} 
10

拿到2路串行/ deserilization WCF的JSON你可以添加一個字符串類型的第二獲取設置屬性,當你建造你的JSON javascript中的對象使用字符串命名屬性,在服務器端使用強類型枚舉版本:eg

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 
} 
9

我瘋了努力,因爲它似乎每個人都默認爲Newtonsoft的串行器來解決此問題,找到一個優雅的解決這個問題。 儘管Newtonsoft提供了更多功能,但確實存在一些嚴重缺陷。 爲了列舉一些:無參數構造函數的需要,如果您希望序列化實現IEnumerable的類,則需要使用瘋狂行爲,並且在使用抽象類型時執行得非常糟糕(因爲它不使用KnownTypes屬性,並且變通方法會生成一個詳細的輸出將你的內部命名空間公開給調用者)。

另一方面,關於如何在MVC4 WebApi解決方案上使用DataContractJsonSerializer時自定義DataContractJsonSerializer的例子很少。

我花了一段時間才找到一個解決方案,它將枚舉表示爲字符串,並解決了DataContractJsonSerializer附帶的已知DateTime格式問題。第一部分 - 將這些擴展方法放在擴展類中 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~

#region JSon 

    /// <summary>Serializes an object to JSon.</summary> 
    /// <param name="obj">The object to serialize.</param> 
    /// <returns>Returns a byte array with the serialized object.</returns> 
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks> 
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")] 
    public static byte[] SerializeJson(this object obj) 
    { 
     using (MemoryStream b = new MemoryStream()) 
     { 
      SerializeJson(obj, b); 
      return b.ToArray(); 
     } 
    } 

    /// <summary>Serializes an object to JSon.</summary> 
    /// <param name="obj">The object to serialize.</param> 
    /// <param name="stream">The stream to write to.</param> 
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks> 
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")] 
    public static void SerializeJson(this object obj, Stream stream) 
    { 
     var settings = new DataContractJsonSerializerSettings(); 
     settings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 
     settings.DataContractSurrogate = new EnumToStringDataContractSurrogate(); 

     var type = obj == null ? typeof(object) : obj.GetType(); 

     var enumerationValue = obj as System.Collections.IEnumerable; 

     var fixedValue = enumerationValue != null 
         ? type.IsGenericType && !type.GetGenericArguments()[0].IsInterface 
          ? enumerationValue.ToArray(type.GetGenericArguments()[0]) 
          : enumerationValue.OfType<object>().ToArray() 
         : obj; 

     if (enumerationValue != null && (!type.IsGenericType || (type.IsGenericType || type.GetGenericArguments()[0].IsInterface))) 
     { 
      var firstMember = (fixedValue as System.Collections.IEnumerable).OfType<object>().FirstOrDefault(); 
      if (firstMember != null) 
       fixedValue = enumerationValue.ToArray(firstMember.GetType()); 
     } 

     var fixedType = obj == null 
         ? type 
         : fixedValue.GetType(); 

     var jsonSer = new DataContractJsonSerializer(fixedType, settings); 
     jsonSer.WriteObject(stream, fixedValue); 
    } 

    /// <summary> 
    /// Deserializes an object. 
    /// </summary> 
    /// <typeparam name="T">The output type of the object.</typeparam> 
    /// <param name="data">The serialized contents.</param> 
    /// <returns>Returns the typed deserialized object.</returns> 
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks> 
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")] 
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")] 
    public static T DeserializeJSon<T>(this byte[] data) 
    { 
     using (MemoryStream b = new MemoryStream(data)) 
      return DeserializeJSon<T>(b); 
    } 

    /// <summary>Deserializes a JSon object.</summary> 
    /// <typeparam name="T">The output type of the object.</typeparam> 
    /// <param name="stream">The stream to read from.</param> 
    /// <returns>Returns the typed object.</returns> 
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks> 
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")] 
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")] 
    public static T DeserializeJSon<T>(this Stream stream) 
    { 
     var settings = new DataContractJsonSerializerSettings(); 
     settings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 
     settings.DataContractSurrogate = new EnumToStringDataContractSurrogate(); 

     var jsonSer = new DataContractJsonSerializer(typeof(T), settings); 
     return (T)jsonSer.ReadObject(stream); 
    } 

    /// <summary>Deserializes a JSon object.</summary> 
    /// <param name="data">The serialized contents.</param> 
    /// <param name="targetType">The target type.</param> 
    /// <returns>Returns the typed deserialized object.</returns> 
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks> 
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")] 
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")] 
    public static object DeserializeJSon(this byte[] data, Type targetType) 
    { 
     using (MemoryStream b = new MemoryStream(data)) 
     { 
      return DeserializeJSon(b, targetType); 
     } 
    } 

    /// <summary>Deserializes a JSon object.</summary> 
    /// <param name="data">The serialized contents.</param> 
    /// <param name="targetType">The target type.</param> 
    /// <returns>Returns the typed deserialized object.</returns> 
    /// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks> 
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")] 
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")] 
    public static object DeserializeJSon(this Stream data, Type targetType) 
    { 
     var settings = new DataContractJsonSerializerSettings(); 
     settings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 
     settings.DataContractSurrogate = new EnumToStringDataContractSurrogate(); 

     var jsonSer = new DataContractJsonSerializer(targetType, settings); 
     return jsonSer.ReadObject(data);    
    } 

    /// <summary>Enumerator contract surrogate.</summary> 
    internal class EnumToStringDataContractSurrogate : IDataContractSurrogate 
    { 
     Type IDataContractSurrogate.GetDataContractType(Type type) 
     { 
      return type == typeof(Enum) ? typeof(string) : type; 
     } 

     object IDataContractSurrogate.GetDeserializedObject(object obj, Type targetType) 
     { 
      if (targetType.IsEnum) 
      { 
       return obj == null 
         ? System.Enum.GetValues(targetType).OfType<int>().FirstOrDefault() 
         : System.Enum.Parse(targetType, obj.ToString()); 
      } 
      return obj; 
     } 

     object IDataContractSurrogate.GetObjectToSerialize(object obj, Type targetType) 
     { 
      if (obj is Enum) 
      { 
       var pair = Enum.GetName(obj.GetType(), obj); 
       return pair; 
      } 

      return obj; 
     } 

     object IDataContractSurrogate.GetCustomDataToExport(Type clrType, Type dataContractType) 
     { 
      throw new NotImplementedException(); 
     } 

     object IDataContractSurrogate.GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType) 
     { 
      throw new NotImplementedException(); 
     } 

     void IDataContractSurrogate.GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes) 
     { 
      throw new NotImplementedException(); 
     } 

     Type IDataContractSurrogate.GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) 
     { 
      throw new NotImplementedException(); 
     } 

     System.CodeDom.CodeTypeDeclaration IDataContractSurrogate.ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    #endregion 


    /// <summary>Creates an array from a non generic source.</summary> 
    /// <param name="source">The source.</param> 
    /// <param name="type">The target type of the array.</param> 
    /// <returns>Returns a typed array.</returns> 
    public static Array ToArray(this IEnumerable source, Type type) 
    { 
     var param = Expression.Parameter(typeof(IEnumerable), "source"); 
     var cast = Expression.Call(typeof(Enumerable), "Cast", new[] { type }, param); 
     var toArray = Expression.Call(typeof(Enumerable), "ToArray", new[] { type }, cast); 
     var lambda = Expression.Lambda<Func<IEnumerable, Array>>(toArray, param).Compile(); 

     return lambda(source); 
    } 

第二部分 - 通過封裝DataContractJsonSerializer 創建自己的格式化~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <summary>Custom implementation of DataContract formatter.</summary> 
public class DataContractJsonFormatter : MediaTypeFormatter 
{ 

    /// <summary>Creates a new instance.</summary> 
    public DataContractJsonFormatter() 
    { 
     SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json")); 
    } 

    /// <summary>Gets if the formatter the write a given type.</summary> 
    /// <param name="type">The type to handle.</param> 
    /// <returns>Returns if the formatter the write a given type.</returns> 
    public override bool CanWriteType(Type type) 
    { 
     return true; 
    } 

    /// <summary>Gets if the formatter the read a given type.</summary> 
    /// <param name="type">The type to handle.</param> 
    /// <returns>Returns if the formatter the read a given type.</returns> 
    public override bool CanReadType(Type type) 
    { 
     return true; 
    } 

    /// <summary>Deserializes an object.</summary> 
    /// <param name="type">The target type.</param> 
    /// <param name="readStream">The stream to read from.</param> 
    /// <param name="content">The http content.</param> 
    /// <param name="formatterLogger">A logger.</param> 
    /// <returns>Returns the deserialized object.</returns> 
    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, System.Net.Http.HttpContent content, IFormatterLogger formatterLogger) 
    { 
     var task = Task<object>.Factory.StartNew(() => 
     { 
      return readStream.DeserializeJSon(type); 
     }); 

     return task; 
    } 

    /// <summary>Serializes an object.</summary> 
    /// <param name="type">The target type.</param> 
    /// <param name="value">The object to serialize.</param> 
    /// <param name="writeStream">The stream to write to.</param> 
    /// <param name="content">The http content.</param> 
    /// <param name="transportContext">The context.</param> 
    /// <returns>Returns the deserialized object.</returns> 
    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext) 
    { 
     var task = Task.Factory.StartNew(() => 
     { 
      value.SerializeJson(writeStream); 
     }); 

     return task; 
    } 
} 

第三部分 - 編輯您的Global.asax文件並使用您的新的JSON格式 ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~

/// <summary>Event handlers of when the application starts.</summary> 
    [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] 
    protected void Application_Start() 
    { 
     //Register my custom DataContract JSon serializer 
     GlobalConfiguration.Configuration.Formatters.Insert(0, new DataContractJsonFormatter()); 

     //Register areas 
     AreaRegistration.RegisterAllAreas(); 

     WebApiConfig.Register(GlobalConfiguration.Configuration); 
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
     RouteConfig.RegisterRoutes(RouteTable.Routes); 
     // BundleConfig.RegisterBundles(BundleTable.Bundles); 

     //JSON serialization config 
     var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; 
     json.UseDataContractJsonSerializer = false; 
    } 
1

我已經把所有使用Newtonsoft.Json庫的方式本解決方案的件在WCF內工作。它修復了枚舉問題,並使錯誤處理更好,並且它在IIS託管服務中工作。這是一個相當大量的代碼,所以你可以在GitHub上找到它的位置:https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

你有一些條目添加到您的Web.config得到它的工作,你可以在這裏看到一個例子文件: https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config