DataContractJsonSerializer和Enums
当我使用DataContractJsonSerializer序列化一个枚举值时,它序列化枚举的数值,而不是string名称。
IE:
enum foo { bar, baz }
序列化foo.bar的值返回“0”,而不是“bar”。
我更喜欢反过来,有没有办法来覆盖这个?
编辑:
因为我不想更改序列化程序,所以我使用了一个简单的解决方法。
我暴露了类中的一个属性来序列化调用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 {} }
它看起来像这是devise ,这种行为是不能改变的:
枚举成员值在JSON中被视为数字,这与数据合约中的处理方式不同,它们作为成员名称包含在数据合约中。
下面是一个使用替代 (和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()); } }
为了得到wcf json的2路序列化/保存,你可以添加第二个types为string的get set属性,当你在javascript中构build你的json对象时,在服务器端使用名为property的string ,使用强types的枚举版本: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 }
我疯了,试图find一个优雅的解决scheme,因为似乎每个人都默认牛顿软件的序列化器来解决这个问题。 虽然Newtonsoft提供了更多的function,但确实有一些严重的缺陷。 举几个例子:需要无参数的构造函数,如果您想要序列化实现IEnumerable的类,那么就会出现疯狂的行为,而且在使用抽象types时它会执行得非常糟糕(因为它不使用KnownTypes属性,而且变通方法会生成详细输出,将您的内部命名空间公开给调用者)。
另一方面,在MVC4 WebApi解决scheme上使用DataContractJsonSerializer时,如何自定义DataContractJsonSerializer。
我花了一段时间才find一个将枚举表示为string的解决scheme,并解决了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来创build自己的格式化器~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// <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; }
编辑:对不起刚起床没有咖啡:(
这里是代码做你想做的与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;
一些testing对象和枚举:
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); } }
我已经使用了在WCF中工作的方式使用Newtonsoft.Json
库将这个解决scheme的所有部分放在一起。 它修复了枚举问题,并使error handling更好,它在IIS托pipe服务中工作。 这是相当多的代码,所以你可以在这里find它在GitHub上: https : //github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs
你必须添加一些条目到你的Web.config
才能使它工作,你可以在这里看到一个示例文件: https : //github.com/jongrant/wcfjsonserializer/blob/master/Web.config