1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System.ServiceModel.Dispatcher
6 using System.Collections;
7 using System.ServiceModel.Channels;
8 using System.ServiceModel;
9 using System.ServiceModel.Description;
10 using System.Collections.Generic;
11 using System.Collections.ObjectModel;
12 using System.Diagnostics;
13 using System.Runtime.Serialization;
14 using System.Reflection;
16 using System.Xml.Serialization;
17 using System.ServiceModel.Diagnostics;
19 using System.Xml.Schema;
21 class XmlSerializerOperationFormatter : OperationFormatter
23 const string soap11Encoding = "http://schemas.xmlsoap.org/soap/encoding/";
24 const string soap12Encoding = "http://www.w3.org/2003/05/soap-encoding";
28 MessageInfo requestMessageInfo;
29 MessageInfo replyMessageInfo;
31 public XmlSerializerOperationFormatter(OperationDescription description, XmlSerializerFormatAttribute xmlSerializerFormatAttribute,
32 MessageInfo requestMessageInfo, MessageInfo replyMessageInfo) :
33 base(description, xmlSerializerFormatAttribute.Style == OperationFormatStyle.Rpc, xmlSerializerFormatAttribute.IsEncoded)
35 if (xmlSerializerFormatAttribute.IsEncoded && xmlSerializerFormatAttribute.Style != OperationFormatStyle.Rpc)
36 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxDocEncodedNotSupported, description.Name)));
37 this.isEncoded = xmlSerializerFormatAttribute.IsEncoded;
39 this.requestMessageInfo = requestMessageInfo;
40 this.replyMessageInfo = replyMessageInfo;
43 protected override void AddHeadersToMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest)
45 XmlSerializer serializer;
46 MessageHeaderDescriptionTable headerDescriptionTable;
47 MessageHeaderDescription unknownHeaderDescription;
55 serializer = requestMessageInfo.HeaderSerializer;
56 headerDescriptionTable = requestMessageInfo.HeaderDescriptionTable;
57 unknownHeaderDescription = requestMessageInfo.UnknownHeaderDescription;
61 serializer = replyMessageInfo.HeaderSerializer;
62 headerDescriptionTable = replyMessageInfo.HeaderDescriptionTable;
63 unknownHeaderDescription = replyMessageInfo.UnknownHeaderDescription;
65 if (serializer != null)
67 object[] headerValues = new object[headerDescriptionTable.Count];
68 MessageHeaderOfTHelper messageHeaderOfTHelper = null;
71 foreach (MessageHeaderDescription headerDescription in messageDescription.Headers)
73 object parameterValue = parameters[headerDescription.Index];
74 if (!headerDescription.IsUnknownHeaderCollection)
76 if (headerDescription.TypedHeader)
78 if (messageHeaderOfTHelper == null)
79 messageHeaderOfTHelper = new MessageHeaderOfTHelper(parameters.Length);
80 headerValues[headerIndex++] = messageHeaderOfTHelper.GetContentAndSaveHeaderAttributes(parameters[headerDescription.Index], headerDescription);
83 headerValues[headerIndex++] = parameterValue;
87 MemoryStream memoryStream = new MemoryStream();
88 XmlDictionaryWriter bufferWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream);
89 bufferWriter.WriteStartElement("root");
90 serializer.Serialize(bufferWriter, headerValues, null, isEncoded ? GetEncoding(message.Version.Envelope) : null);
91 bufferWriter.WriteEndElement();
93 XmlDocument doc = new XmlDocument();
94 memoryStream.Position = 0;
95 doc.Load(new XmlTextReader(memoryStream) { DtdProcessing = DtdProcessing.Prohibit });
96 //doc.Save(Console.Out);
97 foreach (XmlElement element in doc.DocumentElement.ChildNodes)
99 MessageHeaderDescription matchingHeaderDescription = headerDescriptionTable.Get(element.LocalName, element.NamespaceURI);
100 if (matchingHeaderDescription == null)
101 message.Headers.Add(new XmlElementMessageHeader(this, message.Version, element.LocalName, element.NamespaceURI,
102 false/*mustUnderstand*/, null/*actor*/, false/*relay*/, element));
105 if (matchingHeaderDescription.TypedHeader)
106 messageHeaderOfTHelper.GetHeaderAttributes(matchingHeaderDescription, out mustUnderstand, out relay, out actor);
109 mustUnderstand = matchingHeaderDescription.MustUnderstand;
110 relay = matchingHeaderDescription.Relay;
111 actor = matchingHeaderDescription.Actor;
113 message.Headers.Add(new XmlElementMessageHeader(this, message.Version, element.LocalName, element.NamespaceURI,
114 mustUnderstand, actor, relay, element));
118 if (unknownHeaderDescription != null && parameters[unknownHeaderDescription.Index] != null)
120 foreach (object unknownHeader in (IEnumerable)parameters[unknownHeaderDescription.Index])
123 XmlElement element = (XmlElement)GetContentOfMessageHeaderOfT(unknownHeaderDescription, unknownHeader, out mustUnderstand, out relay, out actor);
125 message.Headers.Add(new XmlElementMessageHeader(this, message.Version, element.LocalName, element.NamespaceURI,
126 mustUnderstand, actor, relay, element));
130 catch (InvalidOperationException e)
132 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
133 SR.GetString(SR.SFxErrorSerializingHeader, messageDescription.MessageName, e.Message), e));
137 protected override void GetHeadersFromMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest)
141 XmlSerializer serializer;
142 MessageHeaderDescriptionTable headerDescriptionTable;
143 MessageHeaderDescription unknownHeaderDescription;
146 serializer = requestMessageInfo.HeaderSerializer;
147 headerDescriptionTable = requestMessageInfo.HeaderDescriptionTable;
148 unknownHeaderDescription = requestMessageInfo.UnknownHeaderDescription;
152 serializer = replyMessageInfo.HeaderSerializer;
153 headerDescriptionTable = replyMessageInfo.HeaderDescriptionTable;
154 unknownHeaderDescription = replyMessageInfo.UnknownHeaderDescription;
156 MessageHeaders headers = message.Headers;
157 ArrayList unknownHeaders = null;
158 XmlDocument xmlDoc = null;
159 if (unknownHeaderDescription != null)
161 unknownHeaders = new ArrayList();
162 xmlDoc = new XmlDocument();
164 if (serializer == null)
166 if (unknownHeaderDescription != null)
168 for (int headerIndex = 0; headerIndex < headers.Count; headerIndex++)
169 AddUnknownHeader(unknownHeaderDescription, unknownHeaders, xmlDoc, null/*bufferWriter*/, headers[headerIndex], headers.GetReaderAtHeader(headerIndex));
170 parameters[unknownHeaderDescription.Index] = unknownHeaders.ToArray(unknownHeaderDescription.TypedHeader ? typeof(MessageHeader<XmlElement>) : typeof(XmlElement));
176 MemoryStream memoryStream = new MemoryStream();
177 XmlDictionaryWriter bufferWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream);
178 message.WriteStartEnvelope(bufferWriter);
179 message.WriteStartHeaders(bufferWriter);
180 MessageHeaderOfTHelper messageHeaderOfTHelper = null;
181 for (int headerIndex = 0; headerIndex < headers.Count; headerIndex++)
183 MessageHeaderInfo header = headers[headerIndex];
184 XmlDictionaryReader headerReader = headers.GetReaderAtHeader(headerIndex);
185 MessageHeaderDescription matchingHeaderDescription = headerDescriptionTable.Get(header.Name, header.Namespace);
186 if (matchingHeaderDescription != null)
188 if (header.MustUnderstand)
189 headers.UnderstoodHeaders.Add(header);
190 if (matchingHeaderDescription.TypedHeader)
192 if (messageHeaderOfTHelper == null)
193 messageHeaderOfTHelper = new MessageHeaderOfTHelper(parameters.Length);
194 messageHeaderOfTHelper.SetHeaderAttributes(matchingHeaderDescription, header.MustUnderstand, header.Relay, header.Actor);
198 if (matchingHeaderDescription == null && unknownHeaderDescription != null)
199 AddUnknownHeader(unknownHeaderDescription, unknownHeaders, xmlDoc, bufferWriter, header, headerReader);
201 bufferWriter.WriteNode(headerReader, false);
202 headerReader.Close();
204 bufferWriter.WriteEndElement();
205 bufferWriter.WriteEndElement();
206 bufferWriter.Flush();
209 XmlDocument doc = new XmlDocument();
210 memoryStream.Position = 0;
211 doc.Load(memoryStream);
212 doc.Save(Console.Out);
215 memoryStream.Position = 0;
216 XmlDictionaryReader bufferReader = XmlDictionaryReader.CreateTextReader(memoryStream.GetBuffer(), 0, (int)memoryStream.Length, XmlDictionaryReaderQuotas.Max);
218 bufferReader.ReadStartElement();
219 bufferReader.MoveToContent();
220 if (!bufferReader.IsEmptyElement)
222 bufferReader.ReadStartElement();
223 object[] headerValues = (object[])serializer.Deserialize(bufferReader, isEncoded ? GetEncoding(message.Version.Envelope) : null);
225 foreach (MessageHeaderDescription headerDescription in messageDescription.Headers)
227 if (!headerDescription.IsUnknownHeaderCollection)
229 object parameterValue = headerValues[headerIndex++];
230 if (headerDescription.TypedHeader && parameterValue != null)
231 parameterValue = messageHeaderOfTHelper.CreateMessageHeader(headerDescription, parameterValue);
232 parameters[headerDescription.Index] = parameterValue;
235 bufferReader.Close();
237 if (unknownHeaderDescription != null)
238 parameters[unknownHeaderDescription.Index] = unknownHeaders.ToArray(unknownHeaderDescription.TypedHeader ? typeof(MessageHeader<XmlElement>) : typeof(XmlElement));
240 catch (InvalidOperationException e)
242 // all exceptions from XmlSerializer get wrapped in InvalidOperationException,
243 // so we must be conservative and never turn this into a fault
244 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
245 SR.GetString(SR.SFxErrorDeserializingHeader, messageDescription.MessageName), e));
249 private static void AddUnknownHeader(MessageHeaderDescription unknownHeaderDescription, ArrayList unknownHeaders, XmlDocument xmlDoc, XmlDictionaryWriter bufferWriter, MessageHeaderInfo header, XmlDictionaryReader headerReader)
251 object unknownHeader = xmlDoc.ReadNode(headerReader);
252 if (bufferWriter != null)
253 ((XmlElement)unknownHeader).WriteTo(bufferWriter);
254 if (unknownHeader != null && unknownHeaderDescription.TypedHeader)
255 unknownHeader = TypedHeaderManager.Create(unknownHeaderDescription.Type, unknownHeader, header.MustUnderstand, header.Relay, header.Actor);
257 unknownHeaders.Add(unknownHeader);
260 protected override void WriteBodyAttributes(XmlDictionaryWriter writer, MessageVersion version)
262 if (isEncoded && version.Envelope == EnvelopeVersion.Soap11)
264 string encoding = GetEncoding(version.Envelope);
265 writer.WriteAttributeString("encodingStyle", version.Envelope.Namespace, encoding);
267 writer.WriteAttributeString("xmlns", "xsi", null, XmlSchema.InstanceNamespace);
268 writer.WriteAttributeString("xmlns", "xsd", null, XmlSchema.Namespace);
271 protected override void SerializeBody(XmlDictionaryWriter writer, MessageVersion version, string action, MessageDescription messageDescription, object returnValue, object[] parameters, bool isRequest)
273 if (writer == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("writer"));
274 if (parameters == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("parameters"));
277 MessageInfo messageInfo;
279 messageInfo = requestMessageInfo;
281 messageInfo = replyMessageInfo;
282 if (messageInfo.RpcEncodedTypedMessageBodyParts == null)
284 SerializeBody(writer, version, messageInfo.BodySerializer, messageDescription.Body.ReturnValue, messageDescription.Body.Parts, returnValue, parameters);
288 object[] bodyPartValues = new object[messageInfo.RpcEncodedTypedMessageBodyParts.Count];
289 object bodyObject = parameters[messageDescription.Body.Parts[0].Index];
290 if (bodyObject == null)
291 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxBodyCannotBeNull, messageDescription.MessageName)));
293 foreach (MessagePartDescription bodyPart in messageInfo.RpcEncodedTypedMessageBodyParts)
295 MemberInfo member = bodyPart.MemberInfo;
296 FieldInfo field = member as FieldInfo;
298 bodyPartValues[i++] = field.GetValue(bodyObject);
301 PropertyInfo property = member as PropertyInfo;
302 if (property != null)
303 bodyPartValues[i++] = property.GetValue(bodyObject, null);
306 SerializeBody(writer, version, messageInfo.BodySerializer, null/*returnPart*/, messageInfo.RpcEncodedTypedMessageBodyParts, null/*returnValue*/, bodyPartValues);
308 catch (InvalidOperationException e)
310 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
311 SR.GetString(SR.SFxErrorSerializingBody, messageDescription.MessageName, e.Message), e));
315 void SerializeBody(XmlDictionaryWriter writer, MessageVersion version, XmlSerializer serializer, MessagePartDescription returnPart, MessagePartDescriptionCollection bodyParts, object returnValue, object[] parameters)
317 if (serializer == null)
322 bool hasReturnValue = IsValidReturnValue(returnPart);
323 object[] bodyParameters = new object[bodyParts.Count + (hasReturnValue ? 1 : 0)];
327 bodyParameters[paramIndex++] = returnValue;
329 for (int i = 0; i < bodyParts.Count; i++)
330 bodyParameters[paramIndex++] = parameters[bodyParts[i].Index];
332 string encoding = isEncoded ? GetEncoding(version.Envelope) : null;
333 serializer.Serialize(writer, bodyParameters, null, encoding);
337 protected override object DeserializeBody(XmlDictionaryReader reader, MessageVersion version, string action, MessageDescription messageDescription, object[] parameters, bool isRequest)
339 MessageInfo messageInfo;
341 messageInfo = requestMessageInfo;
343 messageInfo = replyMessageInfo;
344 if (messageInfo.RpcEncodedTypedMessageBodyParts == null)
345 return DeserializeBody(reader, version, messageInfo.BodySerializer, messageDescription.Body.ReturnValue, messageDescription.Body.Parts, parameters, isRequest);
347 object[] bodyPartValues = new object[messageInfo.RpcEncodedTypedMessageBodyParts.Count];
348 DeserializeBody(reader, version, messageInfo.BodySerializer, null/*returnPart*/, messageInfo.RpcEncodedTypedMessageBodyParts, bodyPartValues, isRequest);
349 object bodyObject = Activator.CreateInstance(messageDescription.Body.Parts[0].Type);
351 foreach (MessagePartDescription bodyPart in messageInfo.RpcEncodedTypedMessageBodyParts)
353 MemberInfo member = bodyPart.MemberInfo;
354 FieldInfo field = member as FieldInfo;
356 field.SetValue(bodyObject, bodyPartValues[i++]);
359 PropertyInfo property = member as PropertyInfo;
360 if (property != null)
361 property.SetValue(bodyObject, bodyPartValues[i++], null);
364 parameters[messageDescription.Body.Parts[0].Index] = bodyObject;
368 object DeserializeBody(XmlDictionaryReader reader, MessageVersion version, XmlSerializer serializer, MessagePartDescription returnPart, MessagePartDescriptionCollection bodyParts, object[] parameters, bool isRequest)
372 if (reader == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("reader"));
373 if (parameters == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("parameters"));
374 object returnValue = null;
375 if (serializer == null)
379 if (reader.NodeType == XmlNodeType.EndElement)
382 object[] bodyParameters = (object[])serializer.Deserialize(reader, isEncoded ? GetEncoding(version.Envelope) : null);
385 if (IsValidReturnValue(returnPart))
386 returnValue = bodyParameters[paramIndex++];
388 for (int i = 0; i < bodyParts.Count; i++)
389 parameters[bodyParts[i].Index] = bodyParameters[paramIndex++];
392 catch (InvalidOperationException e)
394 // all exceptions from XmlSerializer get wrapped in InvalidOperationException,
395 // so we must be conservative and never turn this into a fault
396 string resourceKey = isRequest ? SR.SFxErrorDeserializingRequestBody : SR.SFxErrorDeserializingReplyBody;
398 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
399 SR.GetString(resourceKey, OperationName), e));
403 internal static string GetEncoding(EnvelopeVersion version)
405 if (version == EnvelopeVersion.Soap11)
407 return soap11Encoding;
409 else if (version == EnvelopeVersion.Soap12)
411 return soap12Encoding;
415 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("version",
416 SR.GetString(SR.EnvelopeVersionNotSupported, version));
420 internal abstract class MessageInfo
422 internal abstract XmlSerializer BodySerializer { get; }
423 internal abstract XmlSerializer HeaderSerializer { get; }
424 internal abstract MessageHeaderDescriptionTable HeaderDescriptionTable { get; }
425 internal abstract MessageHeaderDescription UnknownHeaderDescription { get; }
426 internal abstract MessagePartDescriptionCollection RpcEncodedTypedMessageBodyParts { get; }
429 class MessageHeaderOfTHelper
432 internal MessageHeaderOfTHelper(int parameterCount)
434 attributes = new object[parameterCount];
436 internal object GetContentAndSaveHeaderAttributes(object parameterValue, MessageHeaderDescription headerDescription)
438 if (parameterValue == null)
443 if (headerDescription.Multiple)
445 object[] messageHeaderOfTArray = (object[])parameterValue;
446 MessageHeader<object>[] messageHeaderOfTAttributes = new MessageHeader<object>[messageHeaderOfTArray.Length];
447 Array tArray = Array.CreateInstance(headerDescription.Type, messageHeaderOfTArray.Length);
448 for (int i = 0; i < tArray.Length; i++)
450 tArray.SetValue(GetContentOfMessageHeaderOfT(headerDescription, messageHeaderOfTArray[i], out mustUnderstand, out relay, out actor), i);
451 messageHeaderOfTAttributes[i] = new MessageHeader<object>(null, mustUnderstand, actor, relay);
453 attributes[headerDescription.Index] = messageHeaderOfTAttributes;
458 object content = GetContentOfMessageHeaderOfT(headerDescription, parameterValue, out mustUnderstand, out relay, out actor);
459 attributes[headerDescription.Index] = new MessageHeader<object>(null, mustUnderstand, actor, relay);
465 internal void GetHeaderAttributes(MessageHeaderDescription headerDescription, out bool mustUnderstand, out bool relay, out string actor)
467 MessageHeader<object> matchingMessageHeaderOfTAttribute = null;
468 if (headerDescription.Multiple)
470 MessageHeader<object>[] messageHeaderOfTAttributes = (MessageHeader<object>[])attributes[headerDescription.Index];
471 for (int i = 0; i < messageHeaderOfTAttributes.Length; i++)
473 if (messageHeaderOfTAttributes[i] != null)
475 matchingMessageHeaderOfTAttribute = messageHeaderOfTAttributes[i];
476 messageHeaderOfTAttributes[i] = null;
480 //assert(matchingMessageHeaderOfTAttribute != null);
484 matchingMessageHeaderOfTAttribute = (MessageHeader<object>)attributes[headerDescription.Index];
485 mustUnderstand = matchingMessageHeaderOfTAttribute.MustUnderstand;
486 relay = matchingMessageHeaderOfTAttribute.Relay;
487 actor = matchingMessageHeaderOfTAttribute.Actor;
490 internal void SetHeaderAttributes(MessageHeaderDescription headerDescription, bool mustUnderstand, bool relay, string actor)
492 if (headerDescription.Multiple)
494 if (attributes[headerDescription.Index] == null)
495 attributes[headerDescription.Index] = new List<MessageHeader<object>>();
496 ((List<MessageHeader<object>>)attributes[headerDescription.Index]).Add(new MessageHeader<object>(null, mustUnderstand, actor, relay));
499 attributes[headerDescription.Index] = new MessageHeader<object>(null, mustUnderstand, actor, relay);
502 internal object CreateMessageHeader(MessageHeaderDescription headerDescription, object headerValue)
504 if (headerDescription.Multiple)
506 IList<MessageHeader<object>> messageHeaderOfTAttributes = (IList<MessageHeader<object>>)attributes[headerDescription.Index];
507 object[] messageHeaderOfTArray = (object[])Array.CreateInstance(TypedHeaderManager.GetMessageHeaderType(headerDescription.Type), messageHeaderOfTAttributes.Count);
508 Array headerValues = (Array)headerValue;
509 for (int i = 0; i < messageHeaderOfTArray.Length; i++)
511 MessageHeader<object> messageHeaderOfTAttribute = messageHeaderOfTAttributes[i];
512 messageHeaderOfTArray[i] = TypedHeaderManager.Create(headerDescription.Type, headerValues.GetValue(i),
513 messageHeaderOfTAttribute.MustUnderstand, messageHeaderOfTAttribute.Relay, messageHeaderOfTAttribute.Actor);
515 return messageHeaderOfTArray;
519 MessageHeader<object> messageHeaderOfTAttribute = (MessageHeader<object>)attributes[headerDescription.Index];
520 return TypedHeaderManager.Create(headerDescription.Type, headerValue,
521 messageHeaderOfTAttribute.MustUnderstand, messageHeaderOfTAttribute.Relay, messageHeaderOfTAttribute.Actor);