2009-04-27 16 views
37

DataContractJsonSerializerを使用してenum値をシリアル化すると、列名ではなく列挙型の数値がシリアル化されます。DataContractJsonSerializerとEnum

IE:foo.barリターン "0" ではなく、 "バー" の値をシリアル化

enum foo 
{ 
    bar, 
    baz 
} 

私はこれを逆にすることをお勧めします。これを無効にする方法はありますか?

編集:

私はシリアライザを変更したくなかったので、私は簡単な回避策のハックを使用。

私はつまり、値に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

この:https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

をあなたはそれが仕事を得るためにあなたのWeb.configにいくつかのエントリを追加する必要があり、あなたはここで例のファイルを見ることができますenumはデフォルトでint型になっているので、それほど驚きはありません。 – Powerlord

+0

XMLシリアル化では、下位互換性のある微妙な問題が発生するため、wcfデータコントラクトで列挙型を公開しないようにすることもお勧めします。 http://stackoverflow.com/questions/326339/do-you-use-enum-types-in-your-wcf-web-services –

答えて

25

It looks like this is by designと、この動作を変更することはできません。

列挙メンバー値は、次のように を扱われますJSONの数字は、 のデータで扱われる方法からです。 契約の場合は、 のメンバー名になります。

ここan alternative(およびIMOより良く、より拡張可能)を使用した例です、あなたが探しているものを実現しシリアライザ:

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()); ' - as of 4.5 – shanabus

+0

@shanabus - あなたはwcfサービスのどこにその魔法の行を追加しますか? (質問はwcfとしてタグ付けされました) – BornToCode

+0

@BornToCode私は 'wcf'にはあまりよく慣れていません。これはあなたの質問に答えますか? - http://stackoverflow.com/questions/6642961/can-i-use-json-serialization-in-wcf-service? – shanabus

3

編集:申し訳ありませんが、ただ何のコーヒーを持っていない:(ここで

私はまだDataContractJsonSerializerで作業をしていませんが、quicの後に実行しています私はMSにむしろ失望しています。彼らは明らかに極端にWCFを非常に拡張可能にしましたが、DataContractJsonSerializerでは拡張性はありません。 MS風のJSONを使用する必要があります。 MSのスーパーラメ...すでにWCFを台無しにしています。

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:

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

は、いくつかのテストは、&列挙オブジェクト。すべての列挙型に対して1つ、列挙型を使用するオブジェクトに対して1つです。

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

をWCFのJSONのための2ウェイ・シリアライズ/ deserilizationを取得するには、あなたが構築しているときに、文字列型の第二のgetセットのプロパティを追加することができ、あなたのJSONオブジェクトは、JavaScriptの文字列を使用して、サーバー側で厳密に型付けされた列挙型を使用:

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

これはうまくいった! – kpasgma

+0

それは地獄のように醜いです。この種の実装を強制するためにMicrosoftがどのような不満を抱いているか。 – crush

9

私は誰もがこの問題を回避するためにNewtonsoftのシリアライザにデフォルト設定と思われるように、この問題へのエレガントな解決策を見つけようとクレイジー行ってきました。 Newtonsoftはより多くの機能を提供していますが、いくつかの重大な欠点があります。 いくつか列挙すると、IEnumerableを実装するクラスをシリアル化する場合はパラメータのないコンストラクタの必要性、クレイジーな動作、抽象型を使用する場合は非常に悪い動作をします(KnownTypes属性を使用しないため内部ネームスペースを呼び出し元に公開する冗長な出力)。

一方、MVC4 WebApiソリューションでDataContractJsonSerializerを使用する場合、DataContractJsonSerializerをカスタマイズする方法の例はほとんどありません。

DataContractJsonSerializerに付属する既知のDateTimeフォーマットの問題を解決するために、列挙型を表すソリューションを見つけるのにしばらく時間がかかりました。 I

PART - 拡張クラス におけるこれらの拡張メソッドを入れ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~

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

PART II - 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; 
    } 
} 

PART III - あなたのGlobal.asaxファイルを編集し、新しいJSONのフォーマッタを消費 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~私は道にNewtonsoft.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

WCF内で動作する。列挙型の問題が修正され、エラー処理が大幅に改善され、IISホステッドサービスでも機能します。あなたがここにGitHubの上でそれを見つけることができるので、それは、コードのかなり多くのです: https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

関連する問題