DataContractJsonSerializer 和枚举
Posted
技术标签:
【中文标题】DataContractJsonSerializer 和枚举【英文标题】:DataContractJsonSerializer and Enums 【发布时间】:2010-10-22 03:28:02 【问题描述】:当我使用 DataContractJsonSerializer 序列化枚举值时,它会序列化枚举的数值,而不是字符串名称。
IE:
enum foo
bar,
baz
序列化 foo.bar 的值返回“0”,而不是“bar”。
我更喜欢它,有没有办法覆盖它?
编辑:
因为我不想更改序列化程序,所以我使用了一个简单的解决方法 hack。
我在类中公开了一个属性来序列化,它在值上调用 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
【问题讨论】:
这并不奇怪,因为枚举默认为 int 类型。 在 XML 序列化中,还建议避免在 wcf 数据协定中公开枚举,因为它们会产生微妙的向后兼容问题。见***.com/questions/326339/… 【参考方案1】: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());
【讨论】:
我相信新的方法是jsonSerializer.Converters.Add(new StringEnumConverter());
- 从 4.5 开始
@shanabus - 你在 wcf 服务中的什么地方添加了这条神奇的线? (问题被标记为 wcf)
@BornToCode 我对wcf
不是很熟悉,这不是我发现这个问题的方式。这是否回答了您的问题 - ***.com/questions/6642961/… ?【参考方案2】:
我疯狂地试图找到一个优雅的解决方案来解决这个问题,因为似乎每个人都默认使用 Newtonsoft 的序列化程序来解决这个问题。 尽管 Newtonsoft 提供了更多功能,但它确实有一些严重的缺点。 举几个例子:需要无参数的构造函数,如果你想序列化实现 IEnumerable 的类的疯狂行为,并且在使用抽象类型时表现非常糟糕(因为它不使用 KnownTypes 属性,解决方法会生成一个将内部命名空间暴露给调用者的详细输出)。
另一方面,在 MVC4 WebApi 解决方案上使用 DataContractJsonSerializer 时如何自定义它的例子很少。
我花了一段时间才找到将枚举表示为字符串并解决 DataContractJsonSerializer 附带的已知 DateTime 格式问题的解决方案。
第 I 部分 - 将这些扩展方法放在扩展类中 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~
#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;
【讨论】:
【参考方案3】:要获得 wcf json 的 2 路序列化/反序列化,您可以添加第二个字符串类型的 get set 属性,当您在 javascript 中构建 json 对象时,请使用 string 命名属性,在服务器端使用强类型枚举版本: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
【讨论】:
虽然丑得要命。微软强迫这种类型的实施真是太丢脸了。【参考方案4】:编辑:抱歉刚起床没喝咖啡:(
这是使用 Json 序列化器而不是 DataContractJsonSerializer 执行您想要执行的操作的代码。
我还没有使用 DataContractJsonSerializer 完成任何工作,但是在快速扫描文档后,我对 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";
JavaScript 转换器。一个用于所有枚举,一个用于使用枚举的对象。
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> SupportedTypesget 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);
【讨论】:
【参考方案5】:我使用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
【讨论】:
以上是关于DataContractJsonSerializer 和枚举的主要内容,如果未能解决你的问题,请参考以下文章