Merge pull request #2734 from nealef/master
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Runtime / Serialization / XmlObjectSerializerWriteContext.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.Runtime.Serialization
6 {
7     using System;
8     using System.Collections;
9     using System.Diagnostics;
10     using System.Globalization;
11     using System.IO;
12     using System.Reflection;
13     using System.Text;
14     using System.Xml;
15     using System.Collections.Generic;
16     using System.Xml.Serialization;
17     using System.ServiceModel.Diagnostics;
18     using System.Security;
19     using System.Security.Permissions;
20     using System.Runtime.CompilerServices;
21     using System.Runtime.Serialization.Diagnostics;
22
23 #if USE_REFEMIT
24     public class XmlObjectSerializerWriteContext : XmlObjectSerializerContext
25 #else
26     internal class XmlObjectSerializerWriteContext : XmlObjectSerializerContext
27 #endif
28     {
29         ObjectReferenceStack byValObjectsInScope = new ObjectReferenceStack();
30         XmlSerializableWriter xmlSerializableWriter;
31         const int depthToCheckCyclicReference = 512;
32         protected bool preserveObjectReferences;
33         ObjectToIdCache serializedObjects;
34         bool isGetOnlyCollection;
35         readonly bool unsafeTypeForwardingEnabled;
36         protected bool serializeReadOnlyTypes;
37
38         internal static XmlObjectSerializerWriteContext CreateContext(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver dataContractResolver)
39         {
40             return (serializer.PreserveObjectReferences || serializer.DataContractSurrogate != null)
41                 ? new XmlObjectSerializerWriteContextComplex(serializer, rootTypeDataContract, dataContractResolver)
42                 : new XmlObjectSerializerWriteContext(serializer, rootTypeDataContract, dataContractResolver);
43         }
44
45         internal static XmlObjectSerializerWriteContext CreateContext(NetDataContractSerializer serializer, Hashtable surrogateDataContracts)
46         {
47             return new XmlObjectSerializerWriteContextComplex(serializer, surrogateDataContracts);
48         }
49
50         protected XmlObjectSerializerWriteContext(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver resolver)
51             : base(serializer, rootTypeDataContract, resolver)
52         {
53             this.serializeReadOnlyTypes = serializer.SerializeReadOnlyTypes;
54             // Known types restricts the set of types that can be deserialized
55             this.unsafeTypeForwardingEnabled = true;
56         }
57
58         protected XmlObjectSerializerWriteContext(NetDataContractSerializer serializer)
59             : base(serializer)
60         {
61             this.unsafeTypeForwardingEnabled = NetDataContractSerializer.UnsafeTypeForwardingEnabled;
62         }
63
64         internal XmlObjectSerializerWriteContext(XmlObjectSerializer serializer, int maxItemsInObjectGraph, StreamingContext streamingContext, bool ignoreExtensionDataObject)
65             : base(serializer, maxItemsInObjectGraph, streamingContext, ignoreExtensionDataObject)
66         {
67             // Known types restricts the set of types that can be deserialized
68             this.unsafeTypeForwardingEnabled = true;
69         }
70
71 #if USE_REFEMIT
72         internal ObjectToIdCache SerializedObjects
73 #else
74         protected ObjectToIdCache SerializedObjects
75 #endif
76         {
77             get
78             {
79                 if (serializedObjects == null)
80                     serializedObjects = new ObjectToIdCache();
81                 return serializedObjects;
82             }
83         }
84
85         internal override bool IsGetOnlyCollection
86         {
87             get { return this.isGetOnlyCollection; }
88             set { this.isGetOnlyCollection = value; }
89         }
90
91         internal bool SerializeReadOnlyTypes
92         {
93             get { return this.serializeReadOnlyTypes; }
94         }
95
96         internal bool UnsafeTypeForwardingEnabled
97         {
98             get { return this.unsafeTypeForwardingEnabled; }
99         }
100
101 #if USE_REFEMIT
102         public void StoreIsGetOnlyCollection()
103 #else
104         internal void StoreIsGetOnlyCollection()
105 #endif
106         {
107             this.isGetOnlyCollection = true;
108         }
109
110         public void InternalSerializeReference(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
111         {
112             if (!OnHandleReference(xmlWriter, obj, true /*canContainCyclicReference*/))
113                 InternalSerialize(xmlWriter, obj, isDeclaredType, writeXsiType, declaredTypeID, declaredTypeHandle);
114             OnEndHandleReference(xmlWriter, obj, true /*canContainCyclicReference*/);
115         }
116
117         public virtual void InternalSerialize(XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
118         {
119             if (writeXsiType)
120             {
121                 Type declaredType = Globals.TypeOfObject;
122                 SerializeWithXsiType(xmlWriter, obj, Type.GetTypeHandle(obj), null/*type*/, -1, declaredType.TypeHandle, declaredType);
123             }
124             else if (isDeclaredType)
125             {
126                 DataContract contract = GetDataContract(declaredTypeID, declaredTypeHandle);
127                 SerializeWithoutXsiType(contract, xmlWriter, obj, declaredTypeHandle);
128             }
129             else
130             {
131                 RuntimeTypeHandle objTypeHandle = Type.GetTypeHandle(obj);
132                 if (declaredTypeHandle.Equals(objTypeHandle))
133                 {
134                     DataContract dataContract = (declaredTypeID >= 0)
135                         ? GetDataContract(declaredTypeID, declaredTypeHandle)
136                         : GetDataContract(declaredTypeHandle, null /*type*/);
137                     SerializeWithoutXsiType(dataContract, xmlWriter, obj, declaredTypeHandle);
138                 }
139                 else
140                 {
141                     SerializeWithXsiType(xmlWriter, obj, objTypeHandle, null /*type*/, declaredTypeID, declaredTypeHandle, Type.GetTypeFromHandle(declaredTypeHandle));
142                 }
143             }
144         }
145
146         internal void SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle)
147         {
148             if (OnHandleIsReference(xmlWriter, dataContract, obj))
149                 return;
150             if (dataContract.KnownDataContracts != null)
151             {
152                 scopedKnownTypes.Push(dataContract.KnownDataContracts);
153                 WriteDataContractValue(dataContract, xmlWriter, obj, declaredTypeHandle);
154                 scopedKnownTypes.Pop();
155             }
156             else
157             {
158                 WriteDataContractValue(dataContract, xmlWriter, obj, declaredTypeHandle);
159             }
160         }
161
162         internal virtual void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType)
163         {
164             bool verifyKnownType = false;
165             Type declaredType = rootTypeDataContract.OriginalUnderlyingType;
166
167             if (declaredType.IsInterface && CollectionDataContract.IsCollectionInterface(declaredType))
168             {
169                 if (DataContractResolver != null)
170                 {
171                     WriteResolvedTypeInfo(xmlWriter, graphType, declaredType);
172                 }
173             }
174             else if (!declaredType.IsArray) //Array covariance is not supported in XSD. If declared type is array do not write xsi:type. Instead write xsi:type for each item
175             {
176                 verifyKnownType = WriteTypeInfo(xmlWriter, dataContract, rootTypeDataContract);
177             }
178             SerializeAndVerifyType(dataContract, xmlWriter, obj, verifyKnownType, originalDeclaredTypeHandle, declaredType);
179         }
180
181         protected virtual void SerializeWithXsiType(XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
182         {
183             DataContract dataContract;
184             bool verifyKnownType = false;
185             if (declaredType.IsInterface && CollectionDataContract.IsCollectionInterface(declaredType))
186             {
187                 dataContract = GetDataContractSkipValidation(DataContract.GetId(objectTypeHandle), objectTypeHandle, objectType);
188                 if (OnHandleIsReference(xmlWriter, dataContract, obj))
189                     return;
190                 if (this.Mode == SerializationMode.SharedType && dataContract.IsValidContract(this.Mode))
191                     dataContract = dataContract.GetValidContract(this.Mode);
192                 else
193                     dataContract = GetDataContract(declaredTypeHandle, declaredType);
194                 if (!WriteClrTypeInfo(xmlWriter, dataContract) && DataContractResolver != null)
195                 {
196                     if (objectType == null)
197                     {
198                         objectType = Type.GetTypeFromHandle(objectTypeHandle);
199                     }
200                     WriteResolvedTypeInfo(xmlWriter, objectType, declaredType);
201                 }
202             }
203             else if (declaredType.IsArray)//Array covariance is not supported in XSD. If declared type is array do not write xsi:type. Instead write xsi:type for each item
204             {
205                 // A call to OnHandleIsReference is not necessary here -- arrays cannot be IsReference
206                 dataContract = GetDataContract(objectTypeHandle, objectType);
207                 WriteClrTypeInfo(xmlWriter, dataContract);
208                 dataContract = GetDataContract(declaredTypeHandle, declaredType);
209             }
210             else
211             {
212                 dataContract = GetDataContract(objectTypeHandle, objectType);
213                 if (OnHandleIsReference(xmlWriter, dataContract, obj))
214                     return;
215                 if (!WriteClrTypeInfo(xmlWriter, dataContract))
216                 {
217                     DataContract declaredTypeContract = (declaredTypeID >= 0)
218                         ? GetDataContract(declaredTypeID, declaredTypeHandle)
219                         : GetDataContract(declaredTypeHandle, declaredType);
220                     verifyKnownType = WriteTypeInfo(xmlWriter, dataContract, declaredTypeContract);
221                 }
222             }
223
224             SerializeAndVerifyType(dataContract, xmlWriter, obj, verifyKnownType, declaredTypeHandle, declaredType);
225         }
226
227         internal bool OnHandleIsReference(XmlWriterDelegator xmlWriter, DataContract contract, object obj)
228         {
229             if (preserveObjectReferences || !contract.IsReference || isGetOnlyCollection)
230             {
231                 return false;
232             }
233
234             bool isNew = true;
235             int objectId = SerializedObjects.GetId(obj, ref isNew);
236             byValObjectsInScope.EnsureSetAsIsReference(obj);
237             if (isNew)
238             {
239                 xmlWriter.WriteAttributeString(Globals.SerPrefix, DictionaryGlobals.IdLocalName,
240                                             DictionaryGlobals.SerializationNamespace, string.Format(CultureInfo.InvariantCulture, "{0}{1}", "i", objectId));
241                 return false;
242             }
243             else
244             {
245                 xmlWriter.WriteAttributeString(Globals.SerPrefix, DictionaryGlobals.RefLocalName, DictionaryGlobals.SerializationNamespace, string.Format(CultureInfo.InvariantCulture, "{0}{1}", "i", objectId));
246                 return true;
247             }
248         }
249
250         protected void SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, bool verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
251         {
252             bool knownTypesAddedInCurrentScope = false;
253             if (dataContract.KnownDataContracts != null)
254             {
255                 scopedKnownTypes.Push(dataContract.KnownDataContracts);
256                 knownTypesAddedInCurrentScope = true;
257             }
258
259             if (verifyKnownType)
260             {
261                 if (!IsKnownType(dataContract, declaredType))
262                 {
263                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.StableName.Name, dataContract.StableName.Namespace)));
264                 }
265             }
266             WriteDataContractValue(dataContract, xmlWriter, obj, declaredTypeHandle);
267
268             if (knownTypesAddedInCurrentScope)
269             {
270                 scopedKnownTypes.Pop();
271             }
272         }
273
274         internal virtual bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, DataContract dataContract)
275         {
276             return false;
277         }
278
279         internal virtual bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, Type dataContractType, string clrTypeName, string clrAssemblyName)
280         {
281             return false;
282         }
283
284         internal virtual bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, Type dataContractType, SerializationInfo serInfo)
285         {
286             return false;
287         }
288
289         public virtual void WriteAnyType(XmlWriterDelegator xmlWriter, object value)
290         {
291             xmlWriter.WriteAnyType(value);
292         }
293
294         public virtual void WriteString(XmlWriterDelegator xmlWriter, string value)
295         {
296             xmlWriter.WriteString(value);
297         }
298         public virtual void WriteString(XmlWriterDelegator xmlWriter, string value, XmlDictionaryString name, XmlDictionaryString ns)
299         {
300             if (value == null)
301                 WriteNull(xmlWriter, typeof(string), true/*isMemberTypeSerializable*/, name, ns);
302             else
303             {
304                 xmlWriter.WriteStartElementPrimitive(name, ns);
305                 xmlWriter.WriteString(value);
306                 xmlWriter.WriteEndElementPrimitive();
307             }
308         }
309
310         public virtual void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value)
311         {
312             xmlWriter.WriteBase64(value);
313         }
314         public virtual void WriteBase64(XmlWriterDelegator xmlWriter, byte[] value, XmlDictionaryString name, XmlDictionaryString ns)
315         {
316             if (value == null)
317                 WriteNull(xmlWriter, typeof(byte[]), true/*isMemberTypeSerializable*/, name, ns);
318             else
319             {
320                 xmlWriter.WriteStartElementPrimitive(name, ns);
321                 xmlWriter.WriteBase64(value);
322                 xmlWriter.WriteEndElementPrimitive();
323             }
324         }
325
326         public virtual void WriteUri(XmlWriterDelegator xmlWriter, Uri value)
327         {
328             xmlWriter.WriteUri(value);
329         }
330         public virtual void WriteUri(XmlWriterDelegator xmlWriter, Uri value, XmlDictionaryString name, XmlDictionaryString ns)
331         {
332             if (value == null)
333                 WriteNull(xmlWriter, typeof(Uri), true/*isMemberTypeSerializable*/, name, ns);
334             else
335             {
336                 xmlWriter.WriteStartElementPrimitive(name, ns);
337                 xmlWriter.WriteUri(value);
338                 xmlWriter.WriteEndElementPrimitive();
339             }
340         }
341
342         public virtual void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName value)
343         {
344             xmlWriter.WriteQName(value);
345         }
346         public virtual void WriteQName(XmlWriterDelegator xmlWriter, XmlQualifiedName value, XmlDictionaryString name, XmlDictionaryString ns)
347         {
348             if (value == null)
349                 WriteNull(xmlWriter, typeof(XmlQualifiedName), true/*isMemberTypeSerializable*/, name, ns);
350             else
351             {
352                 if (ns != null && ns.Value != null && ns.Value.Length > 0)
353                     xmlWriter.WriteStartElement(Globals.ElementPrefix, name, ns);
354                 else
355                     xmlWriter.WriteStartElement(name, ns);
356                 xmlWriter.WriteQName(value);
357                 xmlWriter.WriteEndElement();
358             }
359         }
360
361         internal void HandleGraphAtTopLevel(XmlWriterDelegator writer, object obj, DataContract contract)
362         {
363             writer.WriteXmlnsAttribute(Globals.XsiPrefix, DictionaryGlobals.SchemaInstanceNamespace);
364             if (contract.IsISerializable)
365                 writer.WriteXmlnsAttribute(Globals.XsdPrefix, DictionaryGlobals.SchemaNamespace);
366             OnHandleReference(writer, obj, true /*canContainReferences*/);
367         }
368
369         internal virtual bool OnHandleReference(XmlWriterDelegator xmlWriter, object obj, bool canContainCyclicReference)
370         {
371             if (xmlWriter.depth < depthToCheckCyclicReference)
372                 return false;
373             if (canContainCyclicReference)
374             {
375                 if (byValObjectsInScope.Count == 0 && DiagnosticUtility.ShouldTraceWarning)
376                 {
377                     TraceUtility.Trace(TraceEventType.Warning, TraceCode.ObjectWithLargeDepth, SR.GetString(SR.TraceCodeObjectWithLargeDepth));
378                 }
379                 if (byValObjectsInScope.Contains(obj))
380                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.CannotSerializeObjectWithCycles, DataContract.GetClrTypeFullName(obj.GetType()))));
381                 byValObjectsInScope.Push(obj);
382             }
383             return false;
384         }
385
386         internal virtual void OnEndHandleReference(XmlWriterDelegator xmlWriter, object obj, bool canContainCyclicReference)
387         {
388             if (xmlWriter.depth < depthToCheckCyclicReference)
389                 return;
390             if (canContainCyclicReference)
391             {
392                 byValObjectsInScope.Pop(obj);
393             }
394         }
395
396         public void WriteNull(XmlWriterDelegator xmlWriter, Type memberType, bool isMemberTypeSerializable)
397         {
398             CheckIfTypeSerializable(memberType, isMemberTypeSerializable);
399             WriteNull(xmlWriter);
400         }
401
402         internal void WriteNull(XmlWriterDelegator xmlWriter, Type memberType, bool isMemberTypeSerializable, XmlDictionaryString name, XmlDictionaryString ns)
403         {
404             xmlWriter.WriteStartElement(name, ns);
405             WriteNull(xmlWriter, memberType, isMemberTypeSerializable);
406             xmlWriter.WriteEndElement();
407         }
408
409         public void IncrementArrayCount(XmlWriterDelegator xmlWriter, Array array)
410         {
411             IncrementCollectionCount(xmlWriter, array.GetLength(0));
412         }
413
414         public void IncrementCollectionCount(XmlWriterDelegator xmlWriter, ICollection collection)
415         {
416             IncrementCollectionCount(xmlWriter, collection.Count);
417         }
418
419         public void IncrementCollectionCountGeneric<T>(XmlWriterDelegator xmlWriter, ICollection<T> collection)
420         {
421             IncrementCollectionCount(xmlWriter, collection.Count);
422         }
423
424         void IncrementCollectionCount(XmlWriterDelegator xmlWriter, int size)
425         {
426             IncrementItemCount(size);
427             WriteArraySize(xmlWriter, size);
428         }
429
430         internal virtual void WriteArraySize(XmlWriterDelegator xmlWriter, int size)
431         {
432         }
433
434         public static T GetDefaultValue<T>()
435         {
436             return default(T);
437         }
438
439         public static T GetNullableValue<T>(Nullable<T> value) where T : struct
440         {
441             // value.Value will throw if hasValue is false
442             return value.Value;
443         }
444
445         public static void ThrowRequiredMemberMustBeEmitted(string memberName, Type type)
446         {
447             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.GetString(SR.RequiredMemberMustBeEmitted, memberName, type.FullName)));
448         }
449
450         public static bool GetHasValue<T>(Nullable<T> value) where T : struct
451         {
452             return value.HasValue;
453         }
454
455         internal void WriteIXmlSerializable(XmlWriterDelegator xmlWriter, object obj)
456         {
457             if (xmlSerializableWriter == null)
458                 xmlSerializableWriter = new XmlSerializableWriter();
459             WriteIXmlSerializable(xmlWriter, obj, xmlSerializableWriter);
460         }
461
462         internal static void WriteRootIXmlSerializable(XmlWriterDelegator xmlWriter, object obj)
463         {
464             WriteIXmlSerializable(xmlWriter, obj, new XmlSerializableWriter());
465         }
466
467         static void WriteIXmlSerializable(XmlWriterDelegator xmlWriter, object obj, XmlSerializableWriter xmlSerializableWriter)
468         {
469             xmlSerializableWriter.BeginWrite(xmlWriter.Writer, obj);
470             IXmlSerializable xmlSerializable = obj as IXmlSerializable;
471             if (xmlSerializable != null)
472                 xmlSerializable.WriteXml(xmlSerializableWriter);
473             else
474             {
475                 XmlElement xmlElement = obj as XmlElement;
476                 if (xmlElement != null)
477                     xmlElement.WriteTo(xmlSerializableWriter);
478                 else
479                 {
480                     XmlNode[] xmlNodes = obj as XmlNode[];
481                     if (xmlNodes != null)
482                         foreach (XmlNode xmlNode in xmlNodes)
483                             xmlNode.WriteTo(xmlSerializableWriter);
484                     else
485                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.UnknownXmlType, DataContract.GetClrTypeFullName(obj.GetType()))));
486                 }
487             }
488             xmlSerializableWriter.EndWrite();
489         }
490
491         [Fx.Tag.SecurityNote(Critical = "Calls the critical methods of ISerializable", 
492             Safe = "Demanding Serialization formatter permission is enough.")]
493         [SecuritySafeCritical]
494         [MethodImpl(MethodImplOptions.NoInlining)]
495         internal void GetObjectData(ISerializable obj, SerializationInfo serInfo, StreamingContext context)
496         {
497 #if !DISABLE_CAS_USE
498             // Demand the serialization formatter permission every time
499             Globals.SerializationFormatterPermission.Demand();
500 #endif
501             obj.GetObjectData(serInfo, context);
502         }
503
504         public void WriteISerializable(XmlWriterDelegator xmlWriter, ISerializable obj)
505         {
506             Type objType = obj.GetType();
507             SerializationInfo serInfo = new SerializationInfo(objType, XmlObjectSerializer.FormatterConverter, !this.UnsafeTypeForwardingEnabled);
508             GetObjectData(obj, serInfo, GetStreamingContext());
509
510             if (!this.UnsafeTypeForwardingEnabled && serInfo.AssemblyName == Globals.MscorlibAssemblyName)
511             {
512                 // Throw if a malicious type tries to set its assembly name to "0" to get deserialized in mscorlib
513                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ISerializableAssemblyNameSetToZero, DataContract.GetClrTypeFullName(obj.GetType()))));
514             }
515
516             WriteSerializationInfo(xmlWriter, objType, serInfo);
517         }
518
519         internal void WriteSerializationInfo(XmlWriterDelegator xmlWriter, Type objType, SerializationInfo serInfo)
520         {
521             if (DataContract.GetClrTypeFullName(objType) != serInfo.FullTypeName)
522             {
523                 if (DataContractResolver != null)
524                 {
525                     XmlDictionaryString typeName, typeNs;
526                     if (ResolveType(serInfo.ObjectType, objType, out typeName, out typeNs))
527                     {
528                         xmlWriter.WriteAttributeQualifiedName(Globals.SerPrefix, DictionaryGlobals.ISerializableFactoryTypeLocalName, DictionaryGlobals.SerializationNamespace, typeName, typeNs);
529                     }
530                 }
531                 else
532                 {
533                     string typeName, typeNs;
534                     DataContract.GetDefaultStableName(serInfo.FullTypeName, out typeName, out typeNs);
535                     xmlWriter.WriteAttributeQualifiedName(Globals.SerPrefix, DictionaryGlobals.ISerializableFactoryTypeLocalName, DictionaryGlobals.SerializationNamespace, DataContract.GetClrTypeString(typeName), DataContract.GetClrTypeString(typeNs));
536                 }
537             }
538
539             WriteClrTypeInfo(xmlWriter, objType, serInfo);
540             IncrementItemCount(serInfo.MemberCount);
541             foreach (SerializationEntry serEntry in serInfo)
542             {
543                 XmlDictionaryString name = DataContract.GetClrTypeString(DataContract.EncodeLocalName(serEntry.Name));
544                 xmlWriter.WriteStartElement(name, DictionaryGlobals.EmptyString);
545                 object obj = serEntry.Value;
546                 if (obj == null)
547                     WriteNull(xmlWriter);
548                 else
549                     InternalSerializeReference(xmlWriter, obj, false /*isDeclaredType*/, false /*writeXsiType*/, -1, Globals.TypeOfObject.TypeHandle);
550                 xmlWriter.WriteEndElement();
551             }
552         }
553
554         public void WriteExtensionData(XmlWriterDelegator xmlWriter, ExtensionDataObject extensionData, int memberIndex)
555         {
556             if (IgnoreExtensionDataObject || extensionData == null)
557                 return;
558
559             IList<ExtensionDataMember> members = extensionData.Members;
560             if (members != null)
561             {
562                 for (int i = 0; i < extensionData.Members.Count; i++)
563                 {
564                     ExtensionDataMember member = extensionData.Members[i];
565                     if (member.MemberIndex == memberIndex)
566                     {
567                         WriteExtensionDataMember(xmlWriter, member);
568                     }
569                 }
570             }
571         }
572
573         void WriteExtensionDataMember(XmlWriterDelegator xmlWriter, ExtensionDataMember member)
574         {
575             xmlWriter.WriteStartElement(member.Name, member.Namespace);
576             IDataNode dataNode = member.Value;
577             WriteExtensionDataValue(xmlWriter, dataNode);
578             xmlWriter.WriteEndElement();
579         }
580
581         internal virtual void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, IDataNode dataNode)
582         {
583             if (dataNode.DataContractName != null)
584                 WriteTypeInfo(xmlWriter, dataNode.DataContractName, dataNode.DataContractNamespace);
585
586             WriteClrTypeInfo(xmlWriter, dataNode.DataType, dataNode.ClrTypeName, dataNode.ClrAssemblyName);
587         }
588
589         internal void WriteExtensionDataValue(XmlWriterDelegator xmlWriter, IDataNode dataNode)
590         {
591             IncrementItemCount(1);
592             if (dataNode == null)
593             {
594                 WriteNull(xmlWriter);
595                 return;
596             }
597
598             if (dataNode.PreservesReferences
599                 && OnHandleReference(xmlWriter, (dataNode.Value == null ? dataNode : dataNode.Value), true /*canContainCyclicReference*/))
600                 return;
601
602             Type dataType = dataNode.DataType;
603             if (dataType == Globals.TypeOfClassDataNode)
604                 WriteExtensionClassData(xmlWriter, (ClassDataNode)dataNode);
605             else if (dataType == Globals.TypeOfCollectionDataNode)
606                 WriteExtensionCollectionData(xmlWriter, (CollectionDataNode)dataNode);
607             else if (dataType == Globals.TypeOfXmlDataNode)
608                 WriteExtensionXmlData(xmlWriter, (XmlDataNode)dataNode);
609             else if (dataType == Globals.TypeOfISerializableDataNode)
610                 WriteExtensionISerializableData(xmlWriter, (ISerializableDataNode)dataNode);
611             else
612             {
613                 WriteExtensionDataTypeInfo(xmlWriter, dataNode);
614
615                 if (dataType == Globals.TypeOfObject)
616                 {
617                     // NOTE: serialize value in DataNode<object> since it may contain non-primitive 
618                     // deserialized object (ex. empty class)
619                     object o = dataNode.Value;
620                     if (o != null)
621                         InternalSerialize(xmlWriter, o, false /*isDeclaredType*/, false /*writeXsiType*/, -1, o.GetType().TypeHandle);
622                 }
623                 else
624                     xmlWriter.WriteExtensionData(dataNode);
625             }
626             if (dataNode.PreservesReferences)
627                 OnEndHandleReference(xmlWriter, (dataNode.Value == null ? dataNode : dataNode.Value), true  /*canContainCyclicReference*/);
628         }
629
630         internal bool TryWriteDeserializedExtensionData(XmlWriterDelegator xmlWriter, IDataNode dataNode)
631         {
632             object o = dataNode.Value;
633             if (o == null)
634                 return false;
635
636             Type declaredType = (dataNode.DataContractName == null) ? o.GetType() : Globals.TypeOfObject;
637             InternalSerialize(xmlWriter, o, false /*isDeclaredType*/, false /*writeXsiType*/, -1, declaredType.TypeHandle);
638             return true;
639         }
640
641         void WriteExtensionClassData(XmlWriterDelegator xmlWriter, ClassDataNode dataNode)
642         {
643             if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode))
644             {
645                 WriteExtensionDataTypeInfo(xmlWriter, dataNode);
646
647                 IList<ExtensionDataMember> members = dataNode.Members;
648                 if (members != null)
649                 {
650                     for (int i = 0; i < members.Count; i++)
651                     {
652                         WriteExtensionDataMember(xmlWriter, members[i]);
653                     }
654                 }
655             }
656         }
657
658         void WriteExtensionCollectionData(XmlWriterDelegator xmlWriter, CollectionDataNode dataNode)
659         {
660             if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode))
661             {
662                 WriteExtensionDataTypeInfo(xmlWriter, dataNode);
663
664                 WriteArraySize(xmlWriter, dataNode.Size);
665
666                 IList<IDataNode> items = dataNode.Items;
667                 if (items != null)
668                 {
669                     for (int i = 0; i < items.Count; i++)
670                     {
671                         xmlWriter.WriteStartElement(dataNode.ItemName, dataNode.ItemNamespace);
672                         WriteExtensionDataValue(xmlWriter, items[i]);
673                         xmlWriter.WriteEndElement();
674                     }
675                 }
676             }
677         }
678
679         void WriteExtensionISerializableData(XmlWriterDelegator xmlWriter, ISerializableDataNode dataNode)
680         {
681             if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode))
682             {
683                 WriteExtensionDataTypeInfo(xmlWriter, dataNode);
684
685                 if (dataNode.FactoryTypeName != null)
686                     xmlWriter.WriteAttributeQualifiedName(Globals.SerPrefix, DictionaryGlobals.ISerializableFactoryTypeLocalName, DictionaryGlobals.SerializationNamespace, dataNode.FactoryTypeName, dataNode.FactoryTypeNamespace);
687
688                 IList<ISerializableDataMember> members = dataNode.Members;
689                 if (members != null)
690                 {
691                     for (int i = 0; i < members.Count; i++)
692                     {
693                         ISerializableDataMember member = members[i];
694                         xmlWriter.WriteStartElement(member.Name, String.Empty);
695                         WriteExtensionDataValue(xmlWriter, member.Value);
696                         xmlWriter.WriteEndElement();
697                     }
698                 }
699             }
700         }
701
702         void WriteExtensionXmlData(XmlWriterDelegator xmlWriter, XmlDataNode dataNode)
703         {
704             if (!TryWriteDeserializedExtensionData(xmlWriter, dataNode))
705             {
706                 IList<XmlAttribute> xmlAttributes = dataNode.XmlAttributes;
707                 if (xmlAttributes != null)
708                 {
709                     foreach (XmlAttribute attribute in xmlAttributes)
710                         attribute.WriteTo(xmlWriter.Writer);
711                 }
712                 WriteExtensionDataTypeInfo(xmlWriter, dataNode);
713
714                 IList<XmlNode> xmlChildNodes = dataNode.XmlChildNodes;
715                 if (xmlChildNodes != null)
716                 {
717                     foreach (XmlNode node in xmlChildNodes)
718                         node.WriteTo(xmlWriter.Writer);
719                 }
720             }
721         }
722
723         protected virtual void WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle)
724         {
725             dataContract.WriteXmlValue(xmlWriter, obj, this);
726         }
727
728         protected virtual void WriteNull(XmlWriterDelegator xmlWriter)
729         {
730             XmlObjectSerializer.WriteNull(xmlWriter);
731         }
732
733         void WriteResolvedTypeInfo(XmlWriterDelegator writer, Type objectType, Type declaredType)
734         {
735             XmlDictionaryString typeName, typeNamespace;
736             if (ResolveType(objectType, declaredType, out typeName, out typeNamespace))
737             {
738                 WriteTypeInfo(writer, typeName, typeNamespace);
739             }
740         }
741
742         bool ResolveType(Type objectType, Type declaredType, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
743         {
744             if (!DataContractResolver.TryResolveType(objectType, declaredType, KnownTypeResolver, out typeName, out typeNamespace))
745             {
746                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ResolveTypeReturnedFalse, DataContract.GetClrTypeFullName(DataContractResolver.GetType()), DataContract.GetClrTypeFullName(objectType))));
747             }
748             if (typeName == null)
749             {
750                 if (typeNamespace == null)
751                 {
752                     return false;
753                 }
754                 else
755                 {
756                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ResolveTypeReturnedNull, DataContract.GetClrTypeFullName(DataContractResolver.GetType()), DataContract.GetClrTypeFullName(objectType))));
757                 }
758             }
759             if (typeNamespace == null)
760             {
761                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ResolveTypeReturnedNull, DataContract.GetClrTypeFullName(DataContractResolver.GetType()), DataContract.GetClrTypeFullName(objectType))));
762             }
763             return true;
764         }
765
766         protected virtual bool WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract)
767         {
768             if (!XmlObjectSerializer.IsContractDeclared(contract, declaredContract))
769             {
770                 if (DataContractResolver == null)
771                 {
772                     WriteTypeInfo(writer, contract.Name, contract.Namespace);
773                     return true;
774                 }
775                 else
776                 {
777                     WriteResolvedTypeInfo(writer, contract.OriginalUnderlyingType, declaredContract.OriginalUnderlyingType);
778                     return false;
779                 }
780             }
781             return false;
782         }
783
784         protected virtual void WriteTypeInfo(XmlWriterDelegator writer, string dataContractName, string dataContractNamespace)
785         {
786             writer.WriteAttributeQualifiedName(Globals.XsiPrefix, DictionaryGlobals.XsiTypeLocalName, DictionaryGlobals.SchemaInstanceNamespace, dataContractName, dataContractNamespace);
787         }
788
789         protected virtual void WriteTypeInfo(XmlWriterDelegator writer, XmlDictionaryString dataContractName, XmlDictionaryString dataContractNamespace)
790         {
791             writer.WriteAttributeQualifiedName(Globals.XsiPrefix, DictionaryGlobals.XsiTypeLocalName, DictionaryGlobals.SchemaInstanceNamespace, dataContractName, dataContractNamespace);
792         }
793     }
794 }
795