wcf 内置的json序列化工具,有时需要替换,或者特殊情况的处理,需要修改。
我也遇到了Dto属性类型是datetime,json的反序列化 和 序列号不友好。
这是国外网站的一个方案:Replacing WCF DataContractJsonSerializer with Newtonsoft JsonSerializer
不过时间格式还不是我想要的,他是发布在GitHub上,于是简单修改了时间格式,使更符合国情。
需要引用新写的库 WcfNewtonsoftJsonSerializer ,然后配置文件配置好。
关键配置部分:behaviorExtensions、endpointBehaviors、webHttpBinding
1 <system.serviceModel> 2 3 <extensions> 4 <behaviorExtensions> 5 <add name="newtonsoftJsonBehavior" type="WcfNewtonsoftJsonSerializer.NewtonsoftJsonBehaviorExtension, WcfNewtonsoftJsonSerializer" /> 6 behaviorExtensions> 7 extensions> 8 9 <behaviors> 10 <endpointBehaviors> 11 <behavior name="restEndPointBehavior"> 12 <webHttp helpEnabled="true" /> 13 <newtonsoftJsonBehavior /> 14 behavior> 15 endpointBehaviors> 16 17 <serviceBehaviors> 18 <behavior name="restServiceBehavior"> 19 <serviceMetadata httpGetEnabled="true"/> 20 <serviceDebug includeExceptionDetailInFaults="true" httpHelpPageEnabled="true"/> 21 behavior> 22 serviceBehaviors> 23 behaviors> 24 <bindings> 25 <webHttpBinding> 26 <binding name="restWebHttpBinding" 27 contentTypeMapper="WcfNewtonsoftJsonSerializer.NewtonsoftJsonContentTypeMapper, WcfNewtonsoftJsonSerializer" 28 allowCookies="false" 29 bypassProxyOnLocal="false" 30 hostNameComparisonMode="StrongWildcard" 31 maxBufferPoolSize="524288" 32 maxBufferSize="2147483647" 33 closeTimeout="00:01:00" 34 openTimeout="00:01:00" 35 receiveTimeout="00:02:00" 36 sendTimeout="00:02:00" 37 maxReceivedMessageSize="2147483647" 38 transferMode="Buffered"> 39 <security mode="None"> 40 <transport clientCredentialType="None" /> 41 security> 42 <readerQuotas maxArrayLength="2147483647" 43 maxBytesPerRead="2147483647" 44 maxDepth="2147483647" 45 maxNameTableCharCount="2147483647" 46 maxStringContentLength="2147483647" /> 47 binding> 48 webHttpBinding> 49 bindings> 50 51 <services> 52 <service name="WcfService1.Service1" behaviorConfiguration="restServiceBehavior"> 53 <endpoint address="" 54 contract="WcfService1.IService1" 55 binding="webHttpBinding" 56 bindingConfiguration="restWebHttpBinding" 57 behaviorConfiguration="restEndPointBehavior" /> 58 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> 59 service> 60 services> 61 62 system.serviceModel>
以下是WcfNewtonsoftJsonSerializer项目的关键代码:
1 public class NewtonsoftJsonBehavior : WebHttpBehavior 2 { 3 public override void Validate( ServiceEndpoint endpoint ) 4 { 5 base.Validate( endpoint ); 6 7 var elements = endpoint.Binding.CreateBindingElements(); 8 var webEncoder = elements.Find(); 9 if ( webEncoder == null ) 10 { 11 throw new InvalidOperationException( "This behavior must be used in an endpoint with the WebHttpBinding (or a custom binding with the WebMessageEncodingBindingElement)." ); 12 } 13 14 foreach ( var operation in endpoint.Contract.Operations ) 15 { 16 ValidateOperation( operation ); 17 } 18 } 19 20 protected override IDispatchMessageFormatter GetRequestDispatchFormatter( OperationDescription operationDescription, ServiceEndpoint endpoint ) 21 { 22 if ( IsGetOperation( operationDescription ) ) 23 { 24 // no change for GET operations 25 return base.GetRequestDispatchFormatter( operationDescription, endpoint ); 26 } 27 28 if ( operationDescription.Messages[0].Body.Parts.Count == 0 ) 29 { 30 // nothing in the body, still use the default 31 return base.GetRequestDispatchFormatter( operationDescription, endpoint ); 32 } 33 34 return new NewtonsoftJsonDispatchFormatter( operationDescription, true ); 35 } 36 37 protected override IDispatchMessageFormatter GetReplyDispatchFormatter( OperationDescription operationDescription, ServiceEndpoint endpoint ) 38 { 39 if ( operationDescription.Messages.Count == 1 || operationDescription.Messages[1].Body.ReturnValue.Type == typeof( void ) ) 40 { 41 return base.GetReplyDispatchFormatter( operationDescription, endpoint ); 42 } 43 else 44 { 45 return new NewtonsoftJsonDispatchFormatter( operationDescription, false ); 46 } 47 } 48 49 private void ValidateOperation( OperationDescription operation ) 50 { 51 if ( operation.Messages.Count > 1 ) 52 { 53 if ( operation.Messages[1].Body.Parts.Count > 0 ) 54 { 55 throw new InvalidOperationException( "Operations cannot have out/ref parameters." ); 56 } 57 } 58 59 var bodyStyle = GetBodyStyle( operation ); 60 var inputParameterCount = operation.Messages[0].Body.Parts.Count; 61 if ( !IsGetOperation( operation ) ) 62 { 63 var wrappedRequest = bodyStyle == WebMessageBodyStyle.Wrapped || bodyStyle == WebMessageBodyStyle.WrappedRequest; 64 if ( inputParameterCount == 1 && wrappedRequest ) 65 { 66 throw new InvalidOperationException( "Wrapped body style for single parameters not implemented in this behavior." ); 67 } 68 } 69 70 var wrappedResponse = bodyStyle == WebMessageBodyStyle.Wrapped || bodyStyle == WebMessageBodyStyle.WrappedResponse; 71 var isVoidReturn = operation.Messages.Count == 1 || operation.Messages[1].Body.ReturnValue.Type == typeof( void ); 72 if ( !isVoidReturn && wrappedResponse ) 73 { 74 throw new InvalidOperationException( "Wrapped response not implemented in this behavior." ); 75 } 76 } 77 78 private WebMessageBodyStyle GetBodyStyle( OperationDescription operation ) 79 { 80 var wga = operation.Behaviors.Find (); 81 if ( wga != null ) 82 { 83 return wga.BodyStyle; 84 } 85 86 var wia = operation.Behaviors.Find (); 87 if ( wia != null ) 88 { 89 return wia.BodyStyle; 90 } 91 92 return DefaultBodyStyle; 93 } 94 95 private bool IsGetOperation( OperationDescription operation ) 96 { 97 var wga = operation.Behaviors.Find (); 98 if ( wga != null ) 99 { 100 return true; 101 } 102 103 var wia = operation.Behaviors.Find (); 104 if ( wia != null ) 105 { 106 return wia.Method == "HEAD"; 107 } 108 109 return false; 110 } 111 }
1 public class NewtonsoftJsonBehaviorExtension : BehaviorExtensionElement 2 { 3 public override Type BehaviorType 4 { 5 get { return typeof ( NewtonsoftJsonBehavior ); } 6 } 7 8 protected override object CreateBehavior() 9 { 10 return new NewtonsoftJsonBehavior(); 11 } 12 }
1 public class NewtonsoftJsonContentTypeMapper : WebContentTypeMapper 2 { 3 public override WebContentFormat GetMessageFormatForContentType( string contentType ) 4 { 5 return WebContentFormat.Raw; 6 } 7 }
1 public class NewtonsoftJsonDispatchFormatter : IDispatchMessageFormatter 2 { 3 private readonly OperationDescription _operation; 4 private readonly Dictionary<string, int> _parameterNames; 5 6 public NewtonsoftJsonDispatchFormatter( OperationDescription operation, bool isRequest ) 7 { 8 _operation = operation; 9 if ( isRequest ) 10 { 11 var operationParameterCount = operation.Messages[0].Body.Parts.Count; 12 if ( operationParameterCount > 1 ) 13 { 14 _parameterNames = new Dictionary<string, int>(); 15 for ( var i = 0; i < operationParameterCount; i++ ) 16 { 17 _parameterNames.Add( operation.Messages[0].Body.Parts[i].Name, i ); 18 } 19 } 20 } 21 } 22 23 public void DeserializeRequest( Message message, object[] parameters ) 24 { 25 object bodyFormatProperty; 26 if ( !message.Properties.TryGetValue( WebBodyFormatMessageProperty.Name, out bodyFormatProperty ) || 27 ( bodyFormatProperty as WebBodyFormatMessageProperty ).Format != WebContentFormat.Raw ) 28 { 29 throw new InvalidOperationException( "Incoming messages must have a body format of Raw. Is a ContentTypeMapper set on the WebHttpBinding?" ); 30 } 31 32 var bodyReader = message.GetReaderAtBodyContents(); 33 bodyReader.ReadStartElement( "Binary" ); 34 var rawBody = bodyReader.ReadContentAsBase64(); 35 var ms = new MemoryStream( rawBody ); 36 37 var sr = new StreamReader( ms ); 38 var serializer = new Newtonsoft.Json.JsonSerializer(); 39 if ( parameters.Length == 1 ) 40 { 41 // single parameter, assuming bare 42 parameters[0] = serializer.Deserialize( sr, _operation.Messages[0].Body.Parts[0].Type ); 43 } 44 else 45 { 46 // multiple parameter, needs to be wrapped 47 Newtonsoft.Json.JsonReader reader = new Newtonsoft.Json.JsonTextReader( sr ); 48 reader.Read(); 49 if ( reader.TokenType != Newtonsoft.Json.JsonToken.StartObject ) 50 { 51 throw new InvalidOperationException( "Input needs to be wrapped in an object" ); 52 } 53 54 reader.Read(); 55 while ( reader.TokenType == Newtonsoft.Json.JsonToken.PropertyName ) 56 { 57 var parameterName = reader.Value as string; 58 reader.Read(); 59 if ( _parameterNames.ContainsKey( parameterName ) ) 60 { 61 var parameterIndex = _parameterNames[parameterName]; 62 parameters[parameterIndex] = serializer.Deserialize( reader, _operation.Messages[0].Body.Parts[parameterIndex].Type ); 63 } 64 else 65 { 66 reader.Skip(); 67 } 68 69 reader.Read(); 70 } 71 72 reader.Close(); 73 } 74 75 sr.Close(); 76 ms.Close(); 77 } 78 79 public Message SerializeReply( MessageVersion messageVersion, object[] parameters, object result ) 80 { 81 byte[] body; 82 var serializer = new Newtonsoft.Json.JsonSerializer(); 83 serializer.Converters.Add( new IsoDateTimeConverter() {DateTimeFormat = "yyyy-MM-dd HH:mm:ss", DateTimeStyles = DateTimeStyles.None} ); 84 85 using ( var ms = new MemoryStream() ) 86 { 87 using ( var sw = new StreamWriter( ms, Encoding.UTF8 ) ) 88 { 89 using ( Newtonsoft.Json.JsonWriter writer = new Newtonsoft.Json.JsonTextWriter( sw ) ) 90 { 91 //writer.Formatting = Newtonsoft.Json.Formatting.Indented; 92 serializer.Serialize( writer, result ); 93 sw.Flush(); 94 body = ms.ToArray(); 95 } 96 } 97 } 98 99 var replyMessage = Message.CreateMessage( messageVersion, _operation.Messages[1].Action, new RawBodyWriter( body ) ); 100 replyMessage.Properties.Add( WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty( WebContentFormat.Raw ) ); 101 var respProp = new HttpResponseMessageProperty(); 102 respProp.Headers[HttpResponseHeader.ContentType] = "application/json"; 103 replyMessage.Properties.Add( HttpResponseMessageProperty.Name, respProp ); 104 return replyMessage; 105 } 106 }
1 public class RawBodyWriter : BodyWriter 2 { 3 private readonly byte[] _content; 4 5 public RawBodyWriter( byte[] content ) 6 : base( true ) 7 { 8 _content = content; 9 } 10 11 protected override void OnWriteBodyContents( XmlDictionaryWriter writer ) 12 { 13 writer.WriteStartElement( "Binary" ); 14 writer.WriteBase64( _content, 0, _content.Length ); 15 writer.WriteEndElement(); 16 } 17 }