如何在WCF服务响应中设置仅含两位毫秒的DateTime?
解决WCF输出两位毫秒的ISO 8601日期格式问题
我之前也碰到过类似的WCF日期序列化适配问题,针对你的需求,这里有几个可行的解决方案,你可以根据自己的场景选择:
方案一:直接修改数据契约字段类型(最简单,优先考虑)
如果你的WCF数据契约允许调整,最直接的方式是把DateTime类型的字段改成string,然后手动格式化日期到两位毫秒的格式:
- 修改数据契约:
[DataContract(Name = "YourDataContractName", Namespace = "YourNamespace")] public class YourDataContract { // 把原本的DateTime类型改为string [DataMember(Name = "startDateTime")] public string StartDateTime { get; set; } // 其他字段... }
- 设置日期时手动格式化:
// 使用ff格式符输出两位毫秒,加上Z表示UTC时间 foo.StartDateTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.ffZ", CultureInfo.InvariantCulture);
这个方法不需要额外的序列化配置,代码改动最小,适合契约允许调整的场景。
方案二:自定义DataContractSurrogate(不修改契约)
如果不能修改数据契约的字段类型,可以通过自定义IDataContractSurrogate来拦截DateTime的序列化过程,替换成指定格式的字符串:
- 实现自定义Surrogate类:
using System; using System.Globalization; using System.Runtime.Serialization; public class TwoDigitMillisecondDateTimeSurrogate : IDataContractSurrogate { // 指定DateTime类型用string序列化 public Type GetDataContractType(Type type) { return typeof(DateTime).IsAssignableFrom(type) ? typeof(string) : type; } // 序列化时把DateTime转为两位毫秒格式的字符串 public object GetObjectToSerialize(object obj, Type targetType) { if (obj is DateTime dateTime) { return dateTime.ToString("yyyy-MM-ddTHH:mm:ss.ffZ", CultureInfo.InvariantCulture); } return obj; } // 反序列化方法如果不需要可以返回默认值(这里只关注序列化方向) public object GetDeserializedObject(object obj, Type targetType) => obj; public object GetCustomDataToExport(Type clrType, Type dataContractType) => null; public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) => null; public void GetKnownCustomDataTypes(Collection<Type> customDataTypes) { } public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) => null; public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) => typeDeclaration; }
- 配置WCF使用这个Surrogate:
代码配置方式(服务端):
var host = new ServiceHost(typeof(YourService)); foreach (var endpoint in host.Description.Endpoints) { foreach (var operation in endpoint.Contract.Operations) { var serializerBehavior = operation.Behaviors.Find<DataContractSerializerOperationBehavior>(); if (serializerBehavior != null) { serializerBehavior.DataContractSurrogate = new TwoDigitMillisecondDateTimeSurrogate(); } } } host.Open();
配置文件方式:
<system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="YourServiceBehavior"> <dataContractSerializer dataContractSurrogate="YourNamespace.TwoDigitMillisecondDateTimeSurrogate, YourAssemblyName" /> </behavior> </serviceBehaviors> </behaviors> <services> <service name="YourNamespace.YourService" behaviorConfiguration="YourServiceBehavior"> <!-- 终结点配置... --> </service> </services> </system.serviceModel>
方案三:自定义消息格式化器(修改SOAP消息内容)
如果上面两种方法都不适用,可以通过自定义IDispatchMessageFormatter在消息序列化完成后,直接修改XML中的日期节点内容:
- 实现自定义Formatter类:
using System; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; using System.Xml; public class CustomDateTimeMessageFormatter : IDispatchMessageFormatter { private readonly IDispatchMessageFormatter _innerFormatter; private readonly string _dateNodeXPath; private readonly XmlNamespaceManager _namespaceManager; // 构造函数传入原格式化器、日期节点的XPath和命名空间 public CustomDateTimeMessageFormatter(IDispatchMessageFormatter innerFormatter, string dateNodeXPath, XmlNamespaceManager namespaceManager) { _innerFormatter = innerFormatter; _dateNodeXPath = dateNodeXPath; _namespaceManager = namespaceManager; } public void DeserializeRequest(Message message, object[] parameters) { _innerFormatter.DeserializeRequest(message, parameters); } public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result) { // 先让原格式化器生成消息 var originalMessage = _innerFormatter.SerializeReply(messageVersion, parameters, result); var xmlReader = originalMessage.GetReaderAtBodyContents(); var xmlDoc = new XmlDocument(); xmlDoc.Load(xmlReader); // 找到所有目标日期节点 var dateNodes = xmlDoc.SelectNodes(_dateNodeXPath, _namespaceManager); foreach (XmlNode node in dateNodes) { if (!string.IsNullOrEmpty(node.InnerText) && node.InnerText.EndsWith("Z")) { // 截取到两位毫秒:比如把2019-01-30T08:54:13.698Z改为2019-01-30T08:54:13.69Z var trimmedDate = node.InnerText.Substring(0, node.InnerText.Length - 2) + "Z"; node.InnerText = trimmedDate; } } // 创建新的消息返回 var newMessage = Message.CreateMessage(messageVersion, originalMessage.Headers.Action, xmlDoc.DocumentElement); newMessage.Headers.CopyHeadersFrom(originalMessage); return newMessage; } }
- 配置服务使用这个Formatter:
var host = new ServiceHost(typeof(YourService)); var namespaceManager = new XmlNamespaceManager(new NameTable()); namespaceManager.AddNamespace("a", "YourDataContractNamespace"); // 替换成你的实际命名空间 foreach (var endpoint in host.Description.Endpoints) { foreach (var operation in endpoint.Contract.Operations) { var formatterBehavior = operation.Behaviors.Find<DispatchMessageFormatterBehavior>(); if (formatterBehavior != null) { operation.Behaviors.Remove(formatterBehavior); // 传入日期节点的XPath,比如//a:startDateTime var customFormatter = new CustomDateTimeMessageFormatter( formatterBehavior.Formatter, "//a:startDateTime", namespaceManager); operation.Behaviors.Add(new DispatchMessageFormatterBehavior(customFormatter)); } } } host.Open();
内容的提问来源于stack exchange,提问作者Xavier W.
相关产品推荐
相关产品推荐

