1 // created on 24/04/2003 at 15:35
\r
3 // System.Runtime.Serialization.Formatters.Soap.SoapReader
\r
6 // Jean-Marc Andre (jean-marc.andre@polymtl.ca)
\r
10 // Permission is hereby granted, free of charge, to any person obtaining
\r
11 // a copy of this software and associated documentation files (the
\r
12 // "Software"), to deal in the Software without restriction, including
\r
13 // without limitation the rights to use, copy, modify, merge, publish,
\r
14 // distribute, sublicense, and/or sell copies of the Software, and to
\r
15 // permit persons to whom the Software is furnished to do so, subject to
\r
16 // the following conditions:
\r
18 // The above copyright notice and this permission notice shall be
\r
19 // included in all copies or substantial portions of the Software.
\r
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
33 using System.Xml.Schema;
\r
34 using System.Reflection;
\r
35 using System.Collections;
\r
36 using System.Threading;
\r
37 using System.Globalization;
\r
38 using System.Runtime.Remoting;
\r
39 using System.Runtime.Serialization;
\r
40 using System.Runtime.Remoting.Messaging;
\r
41 using System.Runtime.Remoting.Metadata;
\r
43 namespace System.Runtime.Serialization.Formatters.Soap {
\r
44 internal sealed class SoapReader {
\r
48 private SerializationBinder _binder;
\r
49 private SoapTypeMapper mapper;
\r
50 private ObjectManager objMgr;
\r
51 private StreamingContext _context;
\r
52 private long _nextAvailableId = long.MaxValue;
\r
53 private ISurrogateSelector _surrogateSelector;
\r
54 private XmlTextReader xmlReader;
\r
55 private Hashtable _fieldIndices;
\r
56 private long _topObjectId = 1;
\r
60 public MemberInfo[] MemberInfos;
\r
61 public Hashtable Indices;
\r
68 private long NextAvailableId
\r
73 return _nextAvailableId;
\r
79 #region Constructors
\r
81 public SoapReader(SerializationBinder binder, ISurrogateSelector selector, StreamingContext context)
\r
84 objMgr = new ObjectManager(selector, context);
\r
86 _surrogateSelector = selector;
\r
87 _fieldIndices = new Hashtable();
\r
92 #region Public Methods
\r
94 public object Deserialize(Stream inStream, ISoapMessage soapMessage)
\r
96 var savedCi = CultureInfo.CurrentCulture;
\r
98 Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
\r
99 Deserialize_inner(inStream, soapMessage);
\r
102 Thread.CurrentThread.CurrentCulture = savedCi;
\r
108 void Deserialize_inner(Stream inStream, ISoapMessage soapMessage)
\r
110 ArrayList headers = null;
\r
111 xmlReader = new XmlTextReader(inStream);
\r
112 xmlReader.WhitespaceHandling = WhitespaceHandling.None;
\r
113 mapper = new SoapTypeMapper(_binder);
\r
117 // SOAP-ENV:Envelope
\r
118 xmlReader.MoveToContent();
\r
119 xmlReader.ReadStartElement ();
\r
120 xmlReader.MoveToContent();
\r
123 while (!(xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == "Body" && xmlReader.NamespaceURI == SoapTypeMapper.SoapEnvelopeNamespace))
\r
125 if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == "Header" && xmlReader.NamespaceURI == SoapTypeMapper.SoapEnvelopeNamespace)
\r
127 if (headers == null) headers = new ArrayList ();
\r
128 DeserializeHeaders (headers);
\r
132 xmlReader.MoveToContent();
\r
136 xmlReader.ReadStartElement();
\r
137 xmlReader.MoveToContent();
\r
140 if (soapMessage != null)
\r
142 if (DeserializeMessage (soapMessage)) {
\r
143 _topObjectId = NextAvailableId;
\r
144 RegisterObject (_topObjectId, soapMessage, null, 0, null, null);
\r
146 xmlReader.MoveToContent();
\r
148 if (headers != null)
\r
149 soapMessage.Headers = (Header[]) headers.ToArray (typeof(Header));
\r
152 while (xmlReader.NodeType != XmlNodeType.EndElement)
\r
156 xmlReader.ReadEndElement ();
\r
157 xmlReader.MoveToContent();
\r
159 // SOAP-ENV:Envelope
\r
160 xmlReader.ReadEndElement ();
\r
164 if(xmlReader != null) xmlReader.Close();
\r
170 public SoapTypeMapper Mapper {
\r
171 get { return mapper; }
\r
174 public XmlTextReader XmlReader {
\r
175 get { return xmlReader; }
\r
178 #region Private Methods
\r
180 private object TopObject
\r
185 objMgr.RaiseDeserializationEvent();
\r
186 return objMgr.GetObject(_topObjectId);
\r
190 private bool IsNull()
\r
192 string tmp = xmlReader["null", XmlSchema.InstanceNamespace];
\r
193 return (tmp == null || tmp == string.Empty)?false:true;
\r
196 private long GetId()
\r
200 string strId = xmlReader["id"];
\r
201 if(strId == null || strId == String.Empty) return 0;
\r
202 id = Convert.ToInt64(strId.Substring(4));
\r
206 private long GetHref()
\r
210 string strHref = xmlReader["href"];
\r
211 if(strHref == null || strHref == string.Empty) return 0;
\r
212 href = Convert.ToInt64(strHref.Substring(5));
\r
216 private Type GetComponentType()
\r
218 string strValue = xmlReader["type", XmlSchema.InstanceNamespace];
\r
219 if(strValue == null) {
\r
220 if(GetId() != 0) return typeof(string);
\r
223 return GetTypeFromQName (strValue);
\r
226 private bool DeserializeMessage(ISoapMessage message)
\r
228 string typeNamespace, assemblyName;
\r
230 if(xmlReader.Name == SoapTypeMapper.SoapEnvelopePrefix + ":Fault")
\r
236 SoapServices.DecodeXmlNamespaceForClrTypeNamespace(
\r
237 xmlReader.NamespaceURI,
\r
240 message.MethodName = xmlReader.LocalName;
\r
241 message.XmlNameSpace = xmlReader.NamespaceURI;
\r
243 ArrayList paramNames = new ArrayList();
\r
244 ArrayList paramValues = new ArrayList();
\r
245 long paramValuesId = NextAvailableId;
\r
246 int[] indices = new int[1];
\r
248 if (!xmlReader.IsEmptyElement)
\r
250 int initialDepth = xmlReader.Depth;
\r
253 while(xmlReader.Depth > initialDepth)
\r
255 long paramId, paramHref;
\r
256 object objParam = null;
\r
257 paramNames.Add (xmlReader.Name);
\r
258 Type paramType = null;
\r
260 if (message.ParamTypes != null) {
\r
261 if (i >= message.ParamTypes.Length)
\r
262 throw new SerializationException ("Not enough parameter types in SoapMessages");
\r
263 paramType = message.ParamTypes [i];
\r
267 objParam = DeserializeComponent(
\r
274 indices[0] = paramValues.Add(objParam);
\r
275 if(paramHref != 0)
\r
277 RecordFixup(paramValuesId, paramHref, paramValues.ToArray(), null, null, null, indices);
\r
279 else if(paramId != 0)
\r
281 // RegisterObject(paramId, objParam, null, paramValuesId, null, indices);
\r
288 xmlReader.ReadEndElement();
\r
295 message.ParamNames = (string[]) paramNames.ToArray(typeof(string));
\r
296 message.ParamValues = paramValues.ToArray();
\r
297 RegisterObject(paramValuesId, message.ParamValues, null, 0, null, null);
\r
301 void DeserializeHeaders (ArrayList headers)
\r
303 xmlReader.ReadStartElement ();
\r
304 xmlReader.MoveToContent ();
\r
306 while (xmlReader.NodeType != XmlNodeType.EndElement)
\r
308 if (xmlReader.NodeType != XmlNodeType.Element) { xmlReader.Skip(); continue; }
\r
310 if (xmlReader.GetAttribute ("root", SoapTypeMapper.SoapEncodingNamespace) == "1")
\r
311 headers.Add (DeserializeHeader ());
\r
315 xmlReader.MoveToContent ();
\r
318 xmlReader.ReadEndElement ();
\r
321 Header DeserializeHeader ()
\r
323 Header h = new Header (xmlReader.LocalName, null);
\r
324 h.HeaderNamespace = xmlReader.NamespaceURI;
\r
325 h.MustUnderstand = xmlReader.GetAttribute ("mustUnderstand", SoapTypeMapper.SoapEnvelopeNamespace) == "1";
\r
328 long fieldId, fieldHref;
\r
329 long idHeader = NextAvailableId;
\r
330 FieldInfo fieldInfo = typeof(Header).GetField ("Value");
\r
332 value = DeserializeComponent (null, out fieldId, out fieldHref, idHeader, fieldInfo, null);
\r
335 if(fieldHref != 0 && value == null)
\r
337 RecordFixup (idHeader, fieldHref, h, null, null, fieldInfo, null);
\r
339 else if(value != null && value.GetType().IsValueType && fieldId != 0)
\r
341 RecordFixup (idHeader, fieldId, h, null, null, fieldInfo, null);
\r
343 else if(fieldId != 0)
\r
345 RegisterObject (fieldId, value, null, idHeader, fieldInfo, null);
\r
348 RegisterObject (idHeader, h, null, 0, null, null);
\r
353 private object DeserializeArray(long id)
\r
355 // Special case for base64 byte arrays
\r
356 if (GetComponentType () == typeof(byte[])) {
\r
357 byte[] data = Convert.FromBase64String (xmlReader.ReadElementString());
\r
358 RegisterObject(id, data, null, 0, null, null);
\r
362 // Get the array properties
\r
363 string strArrayType = xmlReader["arrayType", SoapTypeMapper.SoapEncodingNamespace];
\r
364 string[] arrayInfo = strArrayType.Split(':');
\r
365 int arraySuffixInfo = arrayInfo[1].LastIndexOf('[');
\r
366 String arrayElementType = arrayInfo[1].Substring(0, arraySuffixInfo);
\r
367 String arraySuffix = arrayInfo[1].Substring(arraySuffixInfo);
\r
368 string[] arrayDims = arraySuffix.Substring(1,arraySuffix.Length-2).Trim().Split(',');
\r
369 int numberOfDims = arrayDims.Length;
\r
370 int[] lengths = new int[numberOfDims];
\r
372 for (int i=0; i < numberOfDims; i++)
\r
374 lengths[i] = Convert.ToInt32(arrayDims[i]);
\r
377 int[] indices = new int[numberOfDims];
\r
379 // Create the array
\r
380 Type arrayType = mapper.GetType (arrayElementType, xmlReader.LookupNamespace(arrayInfo[0]));
\r
381 Array array = Array.CreateInstance(
\r
385 for(int i = 0; i < numberOfDims; i++)
\r
387 indices[i] = array.GetLowerBound(i);
\r
390 // Deserialize the array items
\r
391 int arrayDepth = xmlReader.Depth;
\r
393 while(xmlReader.Depth > arrayDepth)
\r
395 Type itemType = GetComponentType();
\r
396 if(itemType == null)
\r
397 itemType = array.GetType().GetElementType();
\r
398 long itemId, itemHref;
\r
400 object objItem = DeserializeComponent(itemType,
\r
408 object obj = objMgr.GetObject(itemHref);
\r
410 array.SetValue(obj, indices);
\r
412 RecordFixup(id, itemHref, array, null, null, null, indices);
\r
414 else if(objItem != null && objItem.GetType().IsValueType && itemId != 0)
\r
416 RecordFixup(id, itemId, array, null, null, null, indices);
\r
418 else if(itemId != 0)
\r
420 RegisterObject(itemId, objItem, null, id, null, indices);
\r
421 array.SetValue(objItem, indices);
\r
425 array.SetValue(objItem, indices);
\r
428 // Get the next indice
\r
429 for(int dim = array.Rank - 1; dim >= 0; dim--)
\r
432 if(indices[dim] > array.GetUpperBound(dim))
\r
436 indices[dim] = array.GetLowerBound(dim);
\r
445 RegisterObject(id, array, null, 0, null, null);
\r
446 xmlReader.ReadEndElement();
\r
452 private object Deserialize()
\r
454 object objReturn = null;
\r
455 Type type = mapper.GetType (xmlReader.LocalName, xmlReader.NamespaceURI);
\r
459 id = (id == 0)?1:id;
\r
461 if(type == typeof(Array))
\r
463 objReturn = DeserializeArray(id);
\r
467 objReturn = DeserializeObject(type, id, 0, null, null);
\r
475 private object DeserializeObject(
\r
479 MemberInfo parentMemberInfo,
\r
482 SerializationInfo info = null;
\r
483 bool NeedsSerializationInfo = false;
\r
486 // in case of String & TimeSpan we should allways use 'ReadInternalSoapValue' method
\r
487 // in case of other internal types, we should use ReadInternalSoapValue' only if it is NOT
\r
488 // the root object, means it is a data member of another object that is being serialized.
\r
489 bool shouldReadInternal = (type == typeof(String) || type == typeof(TimeSpan) );
\r
490 if(shouldReadInternal || mapper.IsInternalSoapType (type) && (indices != null || parentMemberInfo != null) )
\r
492 object obj = mapper.ReadInternalSoapValue (this, type);
\r
495 RegisterObject(id, obj, info, parentId, parentMemberInfo, indices);
\r
499 object objReturn =
\r
500 FormatterServices.GetUninitializedObject(type);
\r
502 objMgr.RaiseOnDeserializingEvent (objReturn);
\r
503 if(objReturn is ISerializable)
\r
504 NeedsSerializationInfo = true;
\r
506 if(_surrogateSelector != null && NeedsSerializationInfo == false)
\r
508 ISurrogateSelector selector;
\r
509 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate(
\r
513 NeedsSerializationInfo |= (surrogate != null);
\r
516 if(NeedsSerializationInfo)
\r
519 DeserializeISerializableObject(objReturn, id, out info, out hasFixup);
\r
524 DeserializeSimpleObject(objReturn, id, out hasFixup);
\r
525 if(!hasFixup && objReturn is IObjectReference)
\r
526 objReturn = ((IObjectReference)objReturn).GetRealObject(_context);
\r
529 RegisterObject(id, objReturn, info, parentId, parentMemberInfo, indices);
\r
530 xmlReader.ReadEndElement();
\r
535 private object DeserializeSimpleObject(
\r
542 Type currentType = obj.GetType();
\r
543 TypeMetadata tm = GetTypeMetadata (currentType);
\r
545 object[] data = new object[tm.MemberInfos.Length];
\r
547 xmlReader.MoveToContent ();
\r
548 while (xmlReader.NodeType != XmlNodeType.EndElement)
\r
550 if (xmlReader.NodeType != XmlNodeType.Element) {
\r
555 object fieldObject;
\r
556 long fieldId, fieldHref;
\r
558 object indexob = tm.Indices [xmlReader.LocalName];
\r
559 if (indexob == null)
\r
560 throw new SerializationException ("Field \"" + xmlReader.LocalName + "\" not found in class " + currentType.FullName);
\r
562 int index = (int) indexob;
\r
563 FieldInfo fieldInfo = (tm.MemberInfos[index]) as FieldInfo;
\r
566 DeserializeComponent(fieldInfo.FieldType,
\r
573 data[index] = fieldObject;
\r
575 if(fieldHref != 0 && fieldObject == null)
\r
577 RecordFixup(id, fieldHref, obj, null, null, fieldInfo, null);
\r
581 if(fieldObject != null && fieldObject.GetType().IsValueType && fieldId != 0)
\r
583 RecordFixup(id, fieldId, obj, null, null, fieldInfo, null);
\r
590 RegisterObject(fieldId, fieldObject, null, id, fieldInfo, null);
\r
594 FormatterServices.PopulateObjectMembers (obj, tm.MemberInfos, data);
\r
599 private object DeserializeISerializableObject(
\r
602 out SerializationInfo info,
\r
606 long fieldId, fieldHref;
\r
607 info = new SerializationInfo(obj.GetType(), new FormatterConverter());
\r
610 int initialDepth = xmlReader.Depth;
\r
612 while(xmlReader.Depth > initialDepth)
\r
614 Type fieldType = GetComponentType();
\r
615 string fieldName = XmlConvert.DecodeName (xmlReader.LocalName);
\r
616 object objField = DeserializeComponent(
\r
623 if(fieldHref != 0 && objField == null)
\r
625 RecordFixup(id, fieldHref, obj, info, fieldName, null, null);
\r
629 else if(fieldId != 0 && objField.GetType().IsValueType)
\r
631 RecordFixup(id, fieldId, obj, info, fieldName, null, null);
\r
638 RegisterObject(fieldId, objField, null, id, null, null);
\r
641 info.AddValue(fieldName, objField, (fieldType != null)?fieldType:typeof(object));
\r
648 private object DeserializeComponent(
\r
649 Type componentType,
\r
650 out long componentId,
\r
651 out long componentHref,
\r
653 MemberInfo parentMemberInfo,
\r
666 Type xsiType = GetComponentType();
\r
667 if(xsiType != null) componentType = xsiType;
\r
669 if(xmlReader.HasAttributes)
\r
671 componentId = GetId();
\r
672 componentHref = GetHref();
\r
675 if(componentId != 0)
\r
678 string str = xmlReader.ReadElementString();
\r
679 objMgr.RegisterObject (str, componentId);
\r
682 if(componentHref != 0)
\r
684 // Move the cursor to the next node
\r
686 return objMgr.GetObject(componentHref);
\r
689 if(componentType == null)
\r
690 return xmlReader.ReadElementString();
\r
692 componentId = NextAvailableId;
\r
693 objReturn = DeserializeObject(
\r
702 public void RecordFixup(
\r
703 long parentObjectId,
\r
704 long childObjectId,
\r
705 object parentObject,
\r
706 SerializationInfo info,
\r
708 MemberInfo memberInfo,
\r
713 objMgr.RecordDelayedFixup(parentObjectId, fieldName, childObjectId);
\r
715 else if (parentObject is Array)
\r
717 if (indices.Length == 1)
\r
718 objMgr.RecordArrayElementFixup (parentObjectId, indices[0], childObjectId);
\r
720 objMgr.RecordArrayElementFixup (parentObjectId, (int[])indices.Clone(), childObjectId);
\r
724 objMgr.RecordFixup (parentObjectId, memberInfo, childObjectId);
\r
728 private void RegisterObject (
\r
730 object objectInstance,
\r
731 SerializationInfo info,
\r
732 long parentObjectId,
\r
733 MemberInfo parentObjectMember,
\r
736 if (parentObjectId == 0) indices = null;
\r
738 if (!objectInstance.GetType().IsValueType || parentObjectId == 0)
\r
739 objMgr.RegisterObject (objectInstance, objectId, info, 0, null, null);
\r
742 if(objMgr.GetObject(objectId) != null)
\r
743 throw new SerializationException("Object already registered");
\r
744 if (indices != null) indices = (int[])indices.Clone();
\r
745 objMgr.RegisterObject (
\r
750 parentObjectMember,
\r
755 TypeMetadata GetTypeMetadata (Type type)
\r
757 TypeMetadata tm = _fieldIndices[type] as TypeMetadata;
\r
758 if (tm != null) return tm;
\r
760 tm = new TypeMetadata ();
\r
761 tm.MemberInfos = FormatterServices.GetSerializableMembers (type, _context);
\r
763 tm.Indices = new Hashtable();
\r
764 for(int i = 0; i < tm.MemberInfos.Length; i++) {
\r
765 SoapFieldAttribute at = (SoapFieldAttribute) InternalRemotingServices.GetCachedSoapAttribute (tm.MemberInfos[i]);
\r
766 tm.Indices [XmlConvert.EncodeLocalName (at.XmlElementName)] = i;
\r
769 _fieldIndices[type] = tm;
\r
773 public Type GetTypeFromQName (string qname)
\r
775 string[] strName = qname.Split(':');
\r
776 string namespaceURI = xmlReader.LookupNamespace (strName[0]);
\r
777 return mapper.GetType (strName[1], namespaceURI);
\r