.NET Standard WCF服务与.NET Core 2.0客户端字典序列化差异问题
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
csprojfiles:<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:
Then create a behavior to inject 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; } }
Finally, register the behavior when setting up your service host: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); }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>, overrideGetHashCode()andToString()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 };
- For custom key types: Implement
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
JsonSerializerSettingswhen 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
BodyStyletoWebMessageBodyStyle.Bareto return the rawSomeClassJSON 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"];
- Option 1: Change the service's
内容的提问来源于stack exchange,提问作者alesc

