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

.NET Standard WCF服务与.NET Core 2.0客户端字典序列化差异问题

Fixing Dictionary Serialization/Deserialization Mismatch Between .NET Standard WCF Service and .NET Core 2.0 Client

Hey there, let's tackle this Dictionary serialization/deserialization mismatch between your .NET Standard WCF service and .NET Core 2.0 client using Newtonsoft.Json. I've run into similar issues before, so here are the most common fixes sorted by likelihood:

1. Align Newtonsoft.Json Versions Across Projects

Version discrepancies are one of the top culprits here. .NET Core 2.0 has a default Newtonsoft.Json dependency that might not match what's in your .NET Standard WCF project—different versions can handle Dictionary serialization (like order preservation, key type handling) differently.

  • Fix: Make sure both projects reference the exact same version of the Newtonsoft.Json NuGet package. You can enforce this in your csproj files:
    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
    

2. Replace WCF's Default Serializer with Newtonsoft.Json

WCF uses DataContractJsonSerializer by default, even when you set WebMessageFormat.Json. This will produce JSON that's incompatible with Newtonsoft.Json's deserialization logic. You need to swap in Newtonsoft as WCF's serializer.

  • Fix: Implement a custom dispatch formatter and register it in your service behavior:
    First, create the formatter:
    public class NewtonsoftJsonDispatchFormatter : IDispatchMessageFormatter
    {
        private readonly OperationDescription _operation;
        private readonly JsonSerializerSettings _settings;
    
        public NewtonsoftJsonDispatchFormatter(OperationDescription operation, JsonSerializerSettings settings)
        {
            _operation = operation;
            _settings = settings;
        }
    
        public void DeserializeRequest(Message message, object[] parameters)
        {
            var bodyReader = message.GetReaderAtBodyContents();
            bodyReader.ReadStartElement("Binary");
            var bytes = bodyReader.ReadContentAsBase64();
            var json = Encoding.UTF8.GetString(bytes);
            parameters[0] = JsonConvert.DeserializeObject(json, _operation.Messages[0].Body.Parts[0].Type, _settings);
        }
    
        public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
        {
            var json = JsonConvert.SerializeObject(result, _settings);
            var bytes = Encoding.UTF8.GetBytes(json);
            var writer = XmlDictionaryWriter.CreateBinaryWriter(new MemoryStream(bytes));
            writer.WriteStartElement("Binary");
            writer.WriteBase64(bytes, 0, bytes.Length);
            writer.WriteEndElement();
            writer.Flush();
            var ms = (MemoryStream)writer.BaseStream;
            ms.Position = 0;
            var message = Message.CreateMessage(messageVersion, _operation.Messages[1].Action, XmlDictionaryReader.CreateBinaryReader(ms, XmlDictionaryReaderQuotas.Max));
            message.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
            var httpResponseProp = new HttpResponseMessageProperty();
            httpResponseProp.Headers[HttpResponseHeader.ContentType] = "application/json";
            message.Properties.Add(HttpResponseMessageProperty.Name, httpResponseProp);
            return message;
        }
    }
    
    Then create a behavior to inject the formatter:
    public class NewtonsoftJsonBehavior : WebHttpBehavior
    {
        private readonly JsonSerializerSettings _settings;
    
        public NewtonsoftJsonBehavior(JsonSerializerSettings settings) => _settings = settings;
    
        protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription op, ServiceEndpoint endpoint)
            => new NewtonsoftJsonDispatchFormatter(op, _settings);
    
        protected override IDispatchMessageFormatter GetReplyDispatchFormatter(OperationDescription op, ServiceEndpoint endpoint)
            => new NewtonsoftJsonDispatchFormatter(op, _settings);
    }
    
    Finally, register the behavior when setting up your service host:
    var host = new ServiceHost(typeof(SomeService));
    var endpoint = host.AddServiceEndpoint(typeof(ISomeService), new WebHttpBinding(), "");
    endpoint.Behaviors.Add(new NewtonsoftJsonBehavior(new JsonSerializerSettings
    {
        Formatting = Formatting.Indented,
        TypeNameHandling = TypeNameHandling.Auto // Adjust if you have polymorphic types
    }));
    host.Open();
    

3. Handle Dictionary Key Types Correctly

If your Dictionary uses a non-primitive key type (like a custom class), Newtonsoft.Json might serialize it as an object, which the client can't parse back into a key. Even for primitive types, casing mismatches can cause issues.

  • Fix:
    • For custom key types: Implement IEquatable<T>, override GetHashCode() and ToString() to ensure consistent serialization/deserialization.
    • Configure serializer settings to match casing and handle special types:
      var jsonSettings = new JsonSerializerSettings
      {
          ContractResolver = new DefaultContractResolver
          {
              NamingStrategy = new CamelCaseNamingStrategy() // Match casing between service and client
          },
          Converters = { new StringEnumConverter() } // If keys are enums
      };
      

4. Match Client Deserialization Settings to Service

Your .NET Core client must use identical Newtonsoft.Json settings as the service. Mismatched settings (like casing, type handling) will break Dictionary parsing.

  • Fix: Use the same JsonSerializerSettings when deserializing on the client:
    var httpClient = new HttpClient();
    var responseJson = await httpClient.GetStringAsync("https://your-service-url/SomeWebMethod");
    var result = JsonConvert.DeserializeObject<SomeClass>(responseJson, new JsonSerializerSettings
    {
        ContractResolver = new DefaultContractResolver
        {
            NamingStrategy = new CamelCaseNamingStrategy()
        },
        TypeNameHandling = TypeNameHandling.Auto
    });
    

5. Account for WCF's WrappedRequest Body Style

You set BodyStyle = WebMessageBodyStyle.WrappedRequest on your service operation, which wraps the response in an object named after your method (e.g., {"SomeWebMethodResult": {...}}). If your client tries to deserialize directly to SomeClass, it will fail because of this extra wrapper.

  • Fix:
    • Option 1: Change the service's BodyStyle to WebMessageBodyStyle.Bare to return the raw SomeClass JSON directly.
    • Option 2: Deserialize the wrapper first on the client, then extract the result:
      var wrapper = JsonConvert.DeserializeObject<Dictionary<string, SomeClass>>(responseJson);
      var actualResult = wrapper["SomeWebMethodResult"];
      

内容的提问来源于stack exchange,提问作者alesc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:18:06