This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / XmlSerializationWriter.cs
1 //
2 // System.Xml.Serialization.XmlSerializationWriter.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // Copyright (C) Tim Coleman, 2002
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections;
34 using System.Globalization;
35 using System.Text;
36 using System.Xml;
37 using System.Xml.Schema;
38 using System.Runtime.Serialization;
39
40 namespace System.Xml.Serialization {
41         public abstract class XmlSerializationWriter {
42
43                 #region Fields
44
45                 ObjectIDGenerator idGenerator;
46                 int qnameCount;
47                 bool topLevelElement = false;
48
49                 ArrayList namespaces;
50                 XmlWriter writer;
51                 Queue referencedElements;
52                 Hashtable callbacks;
53                 Hashtable serializedObjects;
54                 const string xmlNamespace = "http://www.w3.org/2000/xmlns/";
55                 const string unexpectedTypeError = "The type {0} was not expected. Use the" +
56                         " XmlInclude or SoapInclude attribute to specify types that are not known statically.";
57
58                 #endregion // Fields
59
60                 #region Constructors
61
62                 protected XmlSerializationWriter ()
63                 {
64                         qnameCount = 0;
65                         serializedObjects = new Hashtable ();
66                 }
67                 
68                 internal void Initialize (XmlWriter writer, XmlSerializerNamespaces nss)
69                 {
70                         this.writer = writer;
71                         if (nss != null)
72                         {
73                                 namespaces = new ArrayList ();
74                                 foreach (XmlQualifiedName ns in nss.ToArray())
75                                         if (ns.Name != "")
76                                                 namespaces.Add (ns);
77                         }       
78                 }
79
80                 #endregion // Constructors
81
82                 #region Properties
83
84                 protected ArrayList Namespaces {
85                         get { return namespaces; }
86                         set { namespaces = value; }
87                 }
88
89                 protected XmlWriter Writer {
90                         get { return writer; }
91                         set { writer = value; }
92                 }
93
94                 #endregion // Properties
95
96                 #region Methods
97                 
98                 protected void AddWriteCallback (Type type, string typeName, string typeNs, XmlSerializationWriteCallback callback)
99                 {
100                         WriteCallbackInfo info = new WriteCallbackInfo ();
101                         info.Type = type;
102                         info.TypeName = typeName;
103                         info.TypeNs = typeNs;
104                         info.Callback = callback;
105
106                         if (callbacks == null) callbacks = new Hashtable ();
107                         callbacks.Add (type, info);
108                 }
109
110                 protected Exception CreateChoiceIdentifierValueException (string value, string identifier, string name, string ns)
111                 {
112                         string message = string.Format ("Value '{0}' of the choice"
113                                 + " identifier '{1}' does not match element '{2}'"
114                                 + " from namespace '{3}'.", value, identifier,
115                                 name, ns);
116                         return new InvalidOperationException (message);
117                 }
118
119                 protected Exception CreateInvalidChoiceIdentifierValueException (string type, string identifier)
120                 {
121                         string message = string.Format ("Invalid or missing choice"
122                                 + " identifier '{0}' of type '{1}'.", identifier,
123                                 type);
124                         return new InvalidOperationException (message);
125                 }
126
127                 protected Exception CreateMismatchChoiceException (string value, string elementName, string enumValue)
128                 {
129                         string message = String.Format ("Value of {0} mismatches the type of {1}, you need to set it to {2}.", elementName, value, enumValue);
130                         return new InvalidOperationException (message);
131                 }
132
133                 protected Exception CreateUnknownAnyElementException (string name, string ns)
134                 {
135                         string message = String.Format ("The XML element named '{0}' from namespace '{1}' was not expected. The XML element name and namespace must match those provided via XmlAnyElementAttribute(s).", name, ns);
136                         return new InvalidOperationException (message);
137                 }
138
139                 protected Exception CreateUnknownTypeException (object o)
140                 {
141                         return CreateUnknownTypeException (o.GetType ());
142                 }
143
144                 protected Exception CreateUnknownTypeException (Type type)
145                 {
146                         string message = String.Format ("The type {0} may not be used in this context.", type);
147                         return new InvalidOperationException (message);
148                 }
149
150                 protected static byte[] FromByteArrayBase64 (byte[] value)
151                 {
152                         return value;
153                 }
154
155                 protected static string FromByteArrayHex (byte[] value)
156                 {
157                         return XmlCustomFormatter.FromByteArrayHex (value);
158                 }
159
160                 protected static string FromChar (char value)
161                 {
162                         return XmlCustomFormatter.FromChar (value);
163                 }
164
165                 protected static string FromDate (DateTime value)
166                 {
167                         return XmlCustomFormatter.FromDate (value);
168                 }
169
170                 protected static string FromDateTime (DateTime value)
171                 {
172                         return XmlCustomFormatter.FromDateTime (value);
173                 }
174
175                 protected static string FromEnum (long value, string[] values, long[] ids)
176                 {
177                         return XmlCustomFormatter.FromEnum (value, values, ids);
178                 }
179
180                 protected static string FromTime (DateTime value)
181                 {
182                         return XmlCustomFormatter.FromTime (value);
183                 }
184
185                 protected static string FromXmlName (string name)
186                 {
187                         return XmlCustomFormatter.FromXmlName (name);
188                 }
189
190                 protected static string FromXmlNCName (string ncName)
191                 {
192                         return XmlCustomFormatter.FromXmlNCName (ncName);
193                 }
194
195                 protected static string FromXmlNmToken (string nmToken)
196                 {
197                         return XmlCustomFormatter.FromXmlNmToken (nmToken);
198                 }
199
200                 protected static string FromXmlNmTokens (string nmTokens)
201                 {
202                         return XmlCustomFormatter.FromXmlNmTokens (nmTokens);
203                 }
204
205                 protected string FromXmlQualifiedName (XmlQualifiedName xmlQualifiedName)
206                 {
207                         if (xmlQualifiedName == null || xmlQualifiedName == XmlQualifiedName.Empty)
208                                 return null;
209                                 
210                         return GetQualifiedName (xmlQualifiedName.Name, xmlQualifiedName.Namespace);
211                 }
212
213                 private string GetId (object o, bool addToReferencesList)
214                 {
215                         if (idGenerator == null) idGenerator = new ObjectIDGenerator ();
216
217                         bool firstTime;
218                         long lid = idGenerator.GetId (o, out firstTime);
219                         return String.Format (CultureInfo.InvariantCulture, "id{0}", lid);
220                 }
221
222                 
223                 bool AlreadyQueued (object ob)
224                 {
225                         if (idGenerator == null) return false;
226
227                         bool firstTime;
228                         idGenerator.HasId (ob, out firstTime);
229                         return !firstTime;
230                 }
231
232                 private string GetNamespacePrefix (string ns)
233                 {
234                         string prefix = Writer.LookupPrefix (ns);
235                         if (prefix == null) 
236                         {
237                                 prefix = String.Format (CultureInfo.InvariantCulture, "q{0}", ++qnameCount);
238                                 WriteAttribute ("xmlns", prefix, null, ns);
239                         }
240                         return prefix;
241                 }
242
243                 private string GetQualifiedName (string name, string ns)
244                 {
245                         if (ns == String.Empty) return name;
246                         
247                         string prefix = GetNamespacePrefix (ns);
248                         if (prefix == String.Empty)
249                                 return name;
250                         else
251                                 return String.Format ("{0}:{1}", prefix, name);
252                 }
253
254                 protected abstract void InitCallbacks ();
255
256                 protected void TopLevelElement ()
257                 {
258                         topLevelElement = true;
259                 }
260
261                 protected void WriteAttribute (string localName, byte[] value)
262                 {
263                         WriteAttribute (localName, String.Empty, value);
264                 }
265
266                 protected void WriteAttribute (string localName, string value)
267                 {
268                         WriteAttribute (String.Empty, localName, String.Empty, value);
269                 }
270
271                 protected void WriteAttribute (string localName, string ns, byte[] value)
272                 {
273                         if (value == null)
274                                 return;
275
276                         Writer.WriteStartAttribute (localName, ns);
277                         WriteValue (value);
278                         Writer.WriteEndAttribute ();
279                 }
280
281                 protected void WriteAttribute (string localName, string ns, string value)
282                 {
283                         WriteAttribute (null, localName, ns, value);
284                 }
285
286                 protected void WriteAttribute (string prefix, string localName, string ns, string value)
287                 {
288                         if (value == null)
289                                 return;
290
291                         Writer.WriteAttributeString (prefix, localName, ns, value);
292                 }
293
294                 protected void WriteElementEncoded (XmlNode node, string name, string ns, bool isNullable, bool any)
295                 {
296                         if (name != string.Empty)
297                         {
298                                 if (node == null)
299                                 {
300                                         if (isNullable)
301                                                 WriteNullTagEncoded (name, ns);
302                                 }
303                                 else
304                                 {
305                                         Writer.WriteStartElement (name, ns);
306                                         node.WriteTo (Writer);
307                                         Writer.WriteEndElement ();
308                                 }
309                         }
310                         else
311                                 node.WriteTo (Writer);
312                 }
313
314                 protected void WriteElementLiteral (XmlNode node, string name, string ns, bool isNullable, bool any)
315                 {
316                         if (name != string.Empty)
317                         {
318                                 if (node == null)
319                                 {
320                                         if (isNullable)
321                                                 WriteNullTagLiteral (name, ns);
322                                 }
323                                 else
324                                 {
325                                         Writer.WriteStartElement (name, ns);
326                                         node.WriteTo (Writer);
327                                         Writer.WriteEndElement ();
328                                 }
329                         }
330                         else
331                                 node.WriteTo (Writer);
332                 }
333
334                 protected void WriteElementQualifiedName (string localName, XmlQualifiedName value)
335                 {
336                         WriteElementQualifiedName (localName, String.Empty, value, null);
337                 }
338
339                 protected void WriteElementQualifiedName (string localName, string ns, XmlQualifiedName value)
340                 {
341                         WriteElementQualifiedName (localName, ns, value, null);
342                 }
343
344                 protected void WriteElementQualifiedName (string localName, XmlQualifiedName value, XmlQualifiedName xsiType)
345                 {
346                         WriteElementQualifiedName (localName, String.Empty, value, xsiType);
347                 }
348
349                 protected void WriteElementQualifiedName (string localName, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)
350                 {
351                         localName = XmlCustomFormatter.FromXmlNCName (localName);
352                         WriteStartElement (localName, ns);
353                         if (xsiType != null) WriteXsiType (xsiType.Name, xsiType.Namespace);
354                         Writer.WriteString (FromXmlQualifiedName (value));
355                         WriteEndElement ();
356                 }
357
358                 protected void WriteElementString (string localName, string value)
359                 {
360                         WriteElementString (localName, String.Empty, value, null);
361                 }
362
363                 protected void WriteElementString (string localName, string ns, string value)
364                 {
365                         WriteElementString (localName, ns, value, null);
366                 }
367
368                 protected void WriteElementString (string localName, string value, XmlQualifiedName xsiType)
369                 {
370                         WriteElementString (localName, String.Empty, value, xsiType);
371                 }
372
373                 protected void WriteElementString (string localName, string ns, string value, XmlQualifiedName xsiType)
374                 {
375                         if (value == null) return;
376
377                         if (xsiType != null) {
378                                 localName = XmlCustomFormatter.FromXmlNCName (localName);
379                                 WriteStartElement (localName, ns);
380                                 WriteXsiType (xsiType.Name, xsiType.Namespace);
381                                 Writer.WriteString (value);
382                                 WriteEndElement ();
383                         } 
384                         else
385                                 Writer.WriteElementString (localName, ns, value);
386                 }
387
388                 protected void WriteElementStringRaw (string localName, byte[] value)
389                 {
390                         WriteElementStringRaw (localName, String.Empty, value, null);
391                 }
392
393                 protected void WriteElementStringRaw (string localName, string value)
394                 {
395                         WriteElementStringRaw (localName, String.Empty, value, null);
396                 }
397
398                 protected void WriteElementStringRaw (string localName, byte[] value, XmlQualifiedName xsiType)
399                 {
400                         WriteElementStringRaw (localName, String.Empty, value, xsiType);
401                 }
402
403                 protected void WriteElementStringRaw (string localName, string ns, byte[] value)
404                 {
405                         WriteElementStringRaw (localName, ns, value, null);
406                 }
407
408                 protected void WriteElementStringRaw (string localName, string ns, string value)
409                 {
410                         WriteElementStringRaw (localName, ns, value, null);
411                 }
412
413                 protected void WriteElementStringRaw (string localName, string value, XmlQualifiedName xsiType)
414                 {
415                         WriteElementStringRaw (localName, String.Empty, value, null);
416                 }
417
418                 protected void WriteElementStringRaw (string localName, string ns, byte[] value, XmlQualifiedName xsiType)
419                 {
420                         if (value == null)
421                                 return;
422
423                         WriteStartElement (localName, ns);
424
425                         if (xsiType != null)
426                                 WriteXsiType (xsiType.Name, xsiType.Namespace);
427
428                         if (value.Length > 0) 
429                                 Writer.WriteBase64(value,0,value.Length);
430                         WriteEndElement ();
431                 }
432
433                 protected void WriteElementStringRaw (string localName, string ns, string value, XmlQualifiedName xsiType)
434                 {
435                         localName = XmlCustomFormatter.FromXmlNCName (localName);
436                         WriteStartElement (localName, ns);
437
438                         if (xsiType != null)
439                                 WriteXsiType (xsiType.Name, xsiType.Namespace);
440
441                         Writer.WriteRaw (value);
442                         WriteEndElement ();
443                 }
444
445                 protected void WriteEmptyTag (string name)
446                 {
447                         WriteEmptyTag (name, String.Empty);
448                 }
449
450                 protected void WriteEmptyTag (string name, string ns)
451                 {
452                         name = XmlCustomFormatter.FromXmlName (name);
453                         WriteStartElement (name, ns);
454                         WriteEndElement ();
455                 }
456
457                 protected void WriteEndElement ()
458                 {
459                         WriteEndElement (null);
460                 }
461
462                 protected void WriteEndElement (object o)
463                 {
464                         if (o != null)
465                                 serializedObjects.Remove (o);
466                                 
467                         Writer.WriteEndElement ();
468                 }
469
470                 protected void WriteId (object o)
471                 {
472                         WriteAttribute ("id", GetId (o, true));
473                 }
474
475                 protected void WriteNamespaceDeclarations (XmlSerializerNamespaces ns)
476                 {
477                         if (ns == null)
478                                 return;
479
480                         ICollection namespaces = ns.Namespaces.Values;
481                         foreach (XmlQualifiedName qn in namespaces) {
482                                 if (qn.Namespace != String.Empty && Writer.LookupPrefix (qn.Namespace) == null)
483                                         WriteAttribute ("xmlns", qn.Name, xmlNamespace, qn.Namespace);
484                         }
485                 }
486
487                 protected void WriteNullableQualifiedNameEncoded (string name, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)
488                 {
489                         if (value != null)
490                                 WriteElementQualifiedName (name, ns, value, xsiType);
491                         else
492                                 WriteNullTagEncoded (name, ns);
493                 }
494
495                 protected void WriteNullableQualifiedNameLiteral (string name, string ns, XmlQualifiedName value)
496                 {
497                         if (value != null)
498                                 WriteElementQualifiedName (name, ns, value);
499                         else
500                                 WriteNullTagLiteral (name, ns);
501                 }
502
503                 protected void WriteNullableStringEncoded (string name, string ns, string value, XmlQualifiedName xsiType)
504                 {
505                         if (value != null)
506                                 WriteElementString (name, ns, value, xsiType);
507                         else
508                                 WriteNullTagEncoded (name, ns);
509                 }
510
511                 protected void WriteNullableStringEncodedRaw (string name, string ns, byte[] value, XmlQualifiedName xsiType)
512                 {
513                         if (value == null)
514                                 WriteNullTagEncoded (name, ns);
515                         else
516                                 WriteElementStringRaw (name, ns, value, xsiType);
517                 }
518
519                 protected void WriteNullableStringEncodedRaw (string name, string ns, string value, XmlQualifiedName xsiType)
520                 {
521                         if (value == null)
522                                 WriteNullTagEncoded (name, ns);
523                         else
524                                 WriteElementStringRaw (name, ns, value, xsiType);
525                 }
526
527                 protected void WriteNullableStringLiteral (string name, string ns, string value)
528                 {
529                         if (value != null)
530                                 WriteElementString (name, ns, value, null);
531                         else
532                                 WriteNullTagLiteral (name, ns);
533                 }
534
535                 protected void WriteNullableStringLiteralRaw (string name, string ns, byte[] value)
536                 {
537                         if (value == null)
538                                 WriteNullTagLiteral (name, ns);
539                         else
540                                 WriteElementStringRaw (name, ns, value);
541                 }
542
543                 protected void WriteNullableStringLiteralRaw (string name, string ns, string value)
544                 {
545                         if (value == null)
546                                 WriteNullTagLiteral (name, ns);
547                         else
548                                 WriteElementStringRaw (name, ns, value);
549                 }
550
551                 protected void WriteNullTagEncoded (string name)
552                 {
553                         WriteNullTagEncoded (name, String.Empty);
554                 }
555
556                 protected void WriteNullTagEncoded (string name, string ns)
557                 {
558                         Writer.WriteStartElement (name, ns);
559 #if NET_1_1
560                         Writer.WriteAttributeString ("nil", XmlSchema.InstanceNamespace, "true");
561 #else
562                         Writer.WriteAttributeString ("null", XmlSchema.InstanceNamespace, "1");
563 #endif
564                         Writer.WriteEndElement ();
565                 }
566
567                 protected void WriteNullTagLiteral (string name)
568                 {
569                         WriteNullTagLiteral (name, String.Empty);
570                 }
571
572                 protected void WriteNullTagLiteral (string name, string ns)
573                 {
574                         WriteStartElement (name, ns);
575                         Writer.WriteAttributeString ("nil", XmlSchema.InstanceNamespace, "true");
576                         WriteEndElement ();
577                 }
578
579                 protected void WritePotentiallyReferencingElement (string n, string ns, object o)
580                 {
581                         WritePotentiallyReferencingElement (n, ns, o, null, false, false);
582                 }
583
584                 protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType)
585                 {
586                         WritePotentiallyReferencingElement (n, ns, o, ambientType, false, false);
587                 }
588
589                 protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType, bool suppressReference)
590                 {
591                         WritePotentiallyReferencingElement (n, ns, o, ambientType, suppressReference, false);
592                 }
593
594                 protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType, bool suppressReference, bool isNullable)
595                 {
596                         if (o == null) 
597                         {
598                                 if (isNullable) WriteNullTagEncoded (n, ns);
599                                 return;
600                         }
601
602                         WriteStartElement (n, ns, true);
603
604                         CheckReferenceQueue ();
605
606                         if (callbacks.ContainsKey (o.GetType ()))
607                         {
608                                 WriteCallbackInfo info = (WriteCallbackInfo) callbacks[o.GetType()];
609                                 if (o.GetType ().IsEnum) {
610                                         info.Callback (o);
611                                 }
612                                 else if (suppressReference) {
613                                         Writer.WriteAttributeString ("id", GetId (o, false));
614                                         if (ambientType != o.GetType ()) WriteXsiType(info.TypeName, info.TypeNs);
615                                         info.Callback (o);
616                                 }
617                                 else {
618                                         if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
619                                         Writer.WriteAttributeString ("href", "#" + GetId (o, true));
620                                 }
621                         }
622                         else
623                         {
624                                 // Must be a primitive type
625                                 TypeData td = TypeTranslator.GetTypeData (o.GetType ());
626                                 if (td.SchemaType != SchemaTypes.Primitive)
627                                         throw new InvalidOperationException ("Invalid type: " + o.GetType().FullName);
628                                 WriteXsiType(td.XmlType, XmlSchema.Namespace);
629                                 Writer.WriteString (XmlCustomFormatter.ToXmlString (td, o));
630                         }
631
632                         WriteEndElement ();
633                 }
634
635                 protected void WriteReferencedElements ()
636                 {
637                         if (referencedElements == null) return;
638                         if (callbacks == null) return;
639
640                         while (referencedElements.Count > 0)
641                         {
642                                 object o = referencedElements.Dequeue ();
643                                 TypeData td = TypeTranslator.GetTypeData (o.GetType ());
644                                 WriteCallbackInfo info = (WriteCallbackInfo) callbacks[o.GetType()];
645                                 WriteStartElement (info.TypeName, info.TypeNs, true);
646                                 Writer.WriteAttributeString ("id", GetId (o, false));
647
648                                 if (td.SchemaType != SchemaTypes.Array) // Array use its own "arrayType" attribute
649                                         WriteXsiType(info.TypeName, info.TypeNs);
650
651                                 info.Callback (o);
652                                 WriteEndElement ();
653                         }
654                 }
655
656                 protected void WriteReferencingElement (string n, string ns, object o)
657                 {
658                         WriteReferencingElement (n, ns, o, false);
659                 }
660
661                 protected void WriteReferencingElement (string n, string ns, object o, bool isNullable)
662                 {
663                         if (o == null) 
664                         {
665                                 if (isNullable) WriteNullTagEncoded (n, ns);
666                                 return;
667                         }
668                         else
669                         {
670                                 CheckReferenceQueue ();
671                                 if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
672
673                                 Writer.WriteStartElement (n, ns);
674                                 Writer.WriteAttributeString ("href", "#" + GetId (o, true));
675                                 Writer.WriteEndElement ();
676                         }
677                 }
678
679                 void CheckReferenceQueue ()
680                 {
681                         if (referencedElements == null)  
682                         {
683                                 referencedElements = new Queue ();
684                                 InitCallbacks ();
685                         }
686                 }
687
688                 [MonoTODO]
689                 protected void WriteRpcResult (string name, string ns)
690                 {
691                         throw new NotImplementedException ();
692                 }
693
694                 protected void WriteSerializable (IXmlSerializable serializable, string name, string ns, bool isNullable)
695                 {
696                         if (serializable == null)
697                         {
698                                 if (isNullable) WriteNullTagLiteral (name, ns);
699                                 return;
700                         }
701                         else
702                         {
703                                 Writer.WriteStartElement (name, ns);
704                                 serializable.WriteXml (Writer);
705                                 Writer.WriteEndElement ();
706                         }
707                 }
708
709                 protected void WriteStartDocument ()
710                 {
711                         if (Writer.WriteState == WriteState.Start)
712                                 Writer.WriteStartDocument ();
713                 }
714
715                 protected void WriteStartElement (string name)
716                 {
717                         WriteStartElement (name, String.Empty, null, false);
718                 }
719
720                 protected void WriteStartElement (string name, string ns)
721                 {
722                         WriteStartElement (name, ns, null, false);
723                 }
724
725                 protected void WriteStartElement (string name, string ns, bool writePrefixed)
726                 {
727                         WriteStartElement (name, ns, null, writePrefixed);
728                 }
729
730                 protected void WriteStartElement (string name, string ns, object o)
731                 {
732                         WriteStartElement (name, ns, o, false);
733                 }
734
735                 protected void WriteStartElement (string name, string ns, object o, bool writePrefixed)
736                 {
737                         if (o != null)
738                         {
739                                 if (serializedObjects.Contains (o))
740                                         throw new InvalidOperationException ("A cirtular reference was detected while serializing an object of type " + o.GetType().Name);
741                                 else
742                                         serializedObjects [o] = o;
743                         }
744                         
745                         WriteState oldState = Writer.WriteState;
746                         
747                         string prefix = null;
748                         
749                         if (topLevelElement && ns != null && ns.Length != 0)
750                         {
751                                 foreach (XmlQualifiedName qn in namespaces)
752                                         if (qn.Namespace == ns) {
753                                                 prefix = qn.Name;
754                                                 writePrefixed = true;
755                                                 break;
756                                         }
757                         }
758
759                         if (writePrefixed && ns != string.Empty)
760                         {
761                                 name = XmlCustomFormatter.FromXmlName (name);
762                                 
763                                 if (prefix == null)
764                                         prefix = Writer.LookupPrefix (ns);
765                                 if (prefix == null || prefix.Length == 0)
766                                         prefix = "q" + (++qnameCount);
767                                 Writer.WriteStartElement (prefix, name, ns);
768                         } else
769                                 Writer.WriteStartElement (name, ns);
770
771                         if (topLevelElement) 
772                         {
773                                 if (namespaces != null) {
774                                         foreach (XmlQualifiedName qn in namespaces)
775                                         {
776                                                 string currentPrefix = Writer.LookupPrefix (qn.Namespace);
777                                                 if (currentPrefix != null && currentPrefix.Length != 0) continue;
778                                                 
779                                                 WriteAttribute ("xmlns",qn.Name,xmlNamespace,qn.Namespace);
780                                         }
781                                 }
782                                 topLevelElement = false;
783                         }
784                 }
785
786                 protected void WriteTypedPrimitive (string name, string ns, object o, bool xsiType)
787                 {
788                         string value;
789                         TypeData td = TypeTranslator.GetTypeData (o.GetType ());
790
791                         name = XmlCustomFormatter.FromXmlName (name);
792                         Writer.WriteStartElement (name, ns);
793
794                         if (o is XmlQualifiedName)
795                                 value = FromXmlQualifiedName ((XmlQualifiedName) o);
796                         else
797                                 value = XmlCustomFormatter.ToXmlString (td, o);
798
799                         if (xsiType)
800                         {
801                                 if (td.SchemaType != SchemaTypes.Primitive)
802                                         throw new InvalidOperationException (string.Format (unexpectedTypeError, o.GetType().FullName));
803                                 WriteXsiType (td.XmlType, XmlSchema.Namespace);
804                         }
805
806                         WriteValue (value);
807                         Writer.WriteEndElement ();
808                 }
809
810                 protected void WriteValue (byte[] value)
811                 {
812                         Writer.WriteBase64 (value, 0, value.Length);
813                 }
814
815                 protected void WriteValue (string value)
816                 {
817                         if (value != null)
818                                 Writer.WriteString (value);
819                 }
820
821                 protected void WriteXmlAttribute (XmlNode node)
822                 {
823                         WriteXmlAttribute (node, null);
824                 }
825
826                 protected void WriteXmlAttribute (XmlNode node, object container)
827                 {
828                         XmlAttribute attr = node as XmlAttribute;
829                         if (attr == null)
830                                 throw new InvalidOperationException ("The node must be either type XmlAttribute or a derived type.");
831                         
832                         if (attr.NamespaceURI == XmlSerializer.WsdlNamespace)
833                         {
834                                 // The wsdl arrayType attribute needs special handling
835                                 if (attr.LocalName == "arrayType") {
836                                         string ns, type, dimensions;
837                                         TypeTranslator.ParseArrayType (attr.Value, out type, out ns, out dimensions);
838                                         string value = GetQualifiedName (type + dimensions, ns);
839                                         WriteAttribute (attr.Prefix, attr.LocalName, attr.NamespaceURI, value);
840                                         return;
841                                 }
842                         }
843                         
844                         WriteAttribute (attr.Prefix, attr.LocalName, attr.NamespaceURI, attr.Value);
845                 }
846
847                 protected void WriteXsiType (string name, string ns)
848                 {
849                         if (ns != null && ns != string.Empty)
850                                 WriteAttribute ("type", XmlSchema.InstanceNamespace, GetQualifiedName (name, ns));
851                         else
852                                 WriteAttribute ("type", XmlSchema.InstanceNamespace, name);
853                 }
854                 
855                 #endregion
856
857                 class WriteCallbackInfo
858                 {
859                         public Type Type;
860                         public string TypeName;
861                         public string TypeNs;
862                         public XmlSerializationWriteCallback Callback;
863                 }
864         }
865 }