我曾试图找到一个优雅的解决方案来解决这个问题,因为似乎每个人都默认使用Newtonsoft的序列化器来解决这个问题。虽然Newtonsoft提供了更多功能,但它确实有一些严重的缺点。其中几个是:需要无参数构造函数,如果您希望序列化实现IEnumerable接口的类,则行为异常,而且在使用抽象类型时性能非常差(因为它不使用KnownTypes属性,而且解决方法会生成冗长的输出,向调用者公开您的内部命名空间)。
另一方面,在使用MVC4 WebApi解决方案时,很少有关于如何自定义DataContractJsonSerializer的示例。
我花了一段时间才找到一个解决方案,它将枚举表示为字符串,并解决了使用DataContractJsonSerializer时出现的已知DateTime格式问题。
第一部分-将这些扩展方法放入扩展类中
#region JSon
[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();
}
}
[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);
}
[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);
}
[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);
}
[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);
}
}
[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);
}
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
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来创建自己的格式化程序
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public class DataContractJsonFormatter : MediaTypeFormatter
{
public DataContractJsonFormatter()
{
SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
}
public override bool CanWriteType(Type type)
{
return true;
}
public override bool CanReadType(Type type)
{
return true;
}
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;
}
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格式化程序
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
protected void Application_Start()
{
GlobalConfiguration.Configuration.Formatters.Insert(0, new DataContractJsonFormatter());
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.UseDataContractJsonSerializer = false;
}