You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WCF服务响应中设置仅含两位毫秒的DateTime?

解决WCF输出两位毫秒的ISO 8601日期格式问题

我之前也碰到过类似的WCF日期序列化适配问题,针对你的需求,这里有几个可行的解决方案,你可以根据自己的场景选择:

方案一:直接修改数据契约字段类型(最简单,优先考虑)

如果你的WCF数据契约允许调整,最直接的方式是把DateTime类型的字段改成string,然后手动格式化日期到两位毫秒的格式:

  1. 修改数据契约:
[DataContract(Name = "YourDataContractName", Namespace = "YourNamespace")]
public class YourDataContract
{
    // 把原本的DateTime类型改为string
    [DataMember(Name = "startDateTime")]
    public string StartDateTime { get; set; }

    // 其他字段...
}
  1. 设置日期时手动格式化:
// 使用ff格式符输出两位毫秒,加上Z表示UTC时间
foo.StartDateTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.ffZ", CultureInfo.InvariantCulture);

这个方法不需要额外的序列化配置,代码改动最小,适合契约允许调整的场景。

方案二:自定义DataContractSurrogate(不修改契约)

如果不能修改数据契约的字段类型,可以通过自定义IDataContractSurrogate来拦截DateTime的序列化过程,替换成指定格式的字符串:

  1. 实现自定义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;
}
  1. 配置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中的日期节点内容:

  1. 实现自定义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;
    }
}
  1. 配置服务使用这个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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:47:55