2 // System.Xml.Serialization.XmlSerializationWriter.cs
5 // Tim Coleman (tim@timcoleman.com)
6 // Lluis Sanchez Gual (lluis@ximian.com)
8 // Copyright (C) Tim Coleman, 2002
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
33 using System.Collections;
34 using System.Globalization;
37 using System.Xml.Schema;
38 using System.Runtime.Serialization;
39 using System.Reflection;
41 namespace System.Xml.Serialization
43 public abstract class XmlSerializationWriter
45 : XmlSerializationGeneratedCode
51 ObjectIDGenerator idGenerator;
53 bool topLevelElement = false;
57 Queue referencedElements;
59 Hashtable serializedObjects;
60 const string xmlNamespace = "http://www.w3.org/2000/xmlns/";
61 const string unexpectedTypeError = "The type {0} was not expected. Use the" +
62 " XmlInclude or SoapInclude attribute to specify types that are not known statically.";
68 protected XmlSerializationWriter ()
71 serializedObjects = new Hashtable ();
74 internal void Initialize (XmlWriter writer, XmlSerializerNamespaces nss)
79 namespaces = new ArrayList ();
80 foreach (XmlQualifiedName ns in nss.ToArray())
81 if (ns.Name != "" && ns.Namespace != "")
86 #endregion // Constructors
90 protected ArrayList Namespaces {
91 get { return namespaces; }
92 set { namespaces = value; }
95 protected XmlWriter Writer {
96 get { return writer; }
97 set { writer = value; }
100 #endregion // Properties
104 protected void AddWriteCallback (Type type, string typeName, string typeNs, XmlSerializationWriteCallback callback)
106 WriteCallbackInfo info = new WriteCallbackInfo ();
108 info.TypeName = typeName;
109 info.TypeNs = typeNs;
110 info.Callback = callback;
112 if (callbacks == null) callbacks = new Hashtable ();
113 callbacks.Add (type, info);
116 protected Exception CreateChoiceIdentifierValueException (string value, string identifier, string name, string ns)
118 string message = string.Format ("Value '{0}' of the choice"
119 + " identifier '{1}' does not match element '{2}'"
120 + " from namespace '{3}'.", value, identifier,
122 return new InvalidOperationException (message);
125 protected Exception CreateInvalidChoiceIdentifierValueException (string type, string identifier)
127 string message = string.Format ("Invalid or missing choice"
128 + " identifier '{0}' of type '{1}'.", identifier,
130 return new InvalidOperationException (message);
133 protected Exception CreateMismatchChoiceException (string value, string elementName, string enumValue)
135 string message = String.Format ("Value of {0} mismatches the type of {1}, you need to set it to {2}.", elementName, value, enumValue);
136 return new InvalidOperationException (message);
139 protected Exception CreateUnknownAnyElementException (string name, string ns)
141 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);
142 return new InvalidOperationException (message);
145 protected Exception CreateUnknownTypeException (object o)
147 return CreateUnknownTypeException (o.GetType ());
150 protected Exception CreateUnknownTypeException (Type type)
152 string message = String.Format ("The type {0} may not be used in this context.", type);
153 return new InvalidOperationException (message);
156 protected static byte[] FromByteArrayBase64 (byte[] value)
161 protected static string FromByteArrayHex (byte[] value)
163 return XmlCustomFormatter.FromByteArrayHex (value);
166 protected static string FromChar (char value)
168 return XmlCustomFormatter.FromChar (value);
171 protected static string FromDate (DateTime value)
173 return XmlCustomFormatter.FromDate (value);
176 protected static string FromDateTime (DateTime value)
178 return XmlCustomFormatter.FromDateTime (value);
181 protected static string FromEnum (long value, string[] values, long[] ids)
183 return XmlCustomFormatter.FromEnum (value, values, ids);
186 protected static string FromTime (DateTime value)
188 return XmlCustomFormatter.FromTime (value);
191 protected static string FromXmlName (string name)
193 return XmlCustomFormatter.FromXmlName (name);
196 protected static string FromXmlNCName (string ncName)
198 return XmlCustomFormatter.FromXmlNCName (ncName);
201 protected static string FromXmlNmToken (string nmToken)
203 return XmlCustomFormatter.FromXmlNmToken (nmToken);
206 protected static string FromXmlNmTokens (string nmTokens)
208 return XmlCustomFormatter.FromXmlNmTokens (nmTokens);
211 protected string FromXmlQualifiedName (XmlQualifiedName xmlQualifiedName)
213 if (xmlQualifiedName == null || xmlQualifiedName == XmlQualifiedName.Empty)
216 return GetQualifiedName (xmlQualifiedName.Name, xmlQualifiedName.Namespace);
219 private string GetId (object o, bool addToReferencesList)
221 if (idGenerator == null) idGenerator = new ObjectIDGenerator ();
224 long lid = idGenerator.GetId (o, out firstTime);
225 return String.Format (CultureInfo.InvariantCulture, "id{0}", lid);
229 bool AlreadyQueued (object ob)
231 if (idGenerator == null) return false;
234 idGenerator.HasId (ob, out firstTime);
238 private string GetNamespacePrefix (string ns)
240 string prefix = Writer.LookupPrefix (ns);
243 prefix = String.Format (CultureInfo.InvariantCulture, "q{0}", ++qnameCount);
244 WriteAttribute ("xmlns", prefix, null, ns);
249 private string GetQualifiedName (string name, string ns)
251 if (ns == String.Empty) return name;
253 string prefix = GetNamespacePrefix (ns);
254 if (prefix == String.Empty)
257 return String.Format ("{0}:{1}", prefix, name);
260 protected abstract void InitCallbacks ();
262 protected void TopLevelElement ()
264 topLevelElement = true;
267 protected void WriteAttribute (string localName, byte[] value)
269 WriteAttribute (localName, String.Empty, value);
272 protected void WriteAttribute (string localName, string value)
274 WriteAttribute (String.Empty, localName, String.Empty, value);
277 protected void WriteAttribute (string localName, string ns, byte[] value)
282 Writer.WriteStartAttribute (localName, ns);
284 Writer.WriteEndAttribute ();
287 protected void WriteAttribute (string localName, string ns, string value)
289 WriteAttribute (null, localName, ns, value);
292 protected void WriteAttribute (string prefix, string localName, string ns, string value)
297 Writer.WriteAttributeString (prefix, localName, ns, value);
300 protected void WriteElementEncoded (XmlNode node, string name, string ns, bool isNullable, bool any)
302 if (name != string.Empty)
307 WriteNullTagEncoded (name, ns);
311 Writer.WriteStartElement (name, ns);
312 node.WriteTo (Writer);
313 Writer.WriteEndElement ();
317 node.WriteTo (Writer);
320 protected void WriteElementLiteral (XmlNode node, string name, string ns, bool isNullable, bool any)
322 if (name != string.Empty)
327 WriteNullTagLiteral (name, ns);
331 Writer.WriteStartElement (name, ns);
332 node.WriteTo (Writer);
333 Writer.WriteEndElement ();
337 node.WriteTo (Writer);
340 protected void WriteElementQualifiedName (string localName, XmlQualifiedName value)
342 WriteElementQualifiedName (localName, String.Empty, value, null);
345 protected void WriteElementQualifiedName (string localName, string ns, XmlQualifiedName value)
347 WriteElementQualifiedName (localName, ns, value, null);
350 protected void WriteElementQualifiedName (string localName, XmlQualifiedName value, XmlQualifiedName xsiType)
352 WriteElementQualifiedName (localName, String.Empty, value, xsiType);
355 protected void WriteElementQualifiedName (string localName, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)
357 localName = XmlCustomFormatter.FromXmlNCName (localName);
358 WriteStartElement (localName, ns);
359 if (xsiType != null) WriteXsiType (xsiType.Name, xsiType.Namespace);
360 Writer.WriteString (FromXmlQualifiedName (value));
364 protected void WriteElementString (string localName, string value)
366 WriteElementString (localName, String.Empty, value, null);
369 protected void WriteElementString (string localName, string ns, string value)
371 WriteElementString (localName, ns, value, null);
374 protected void WriteElementString (string localName, string value, XmlQualifiedName xsiType)
376 WriteElementString (localName, String.Empty, value, xsiType);
379 protected void WriteElementString (string localName, string ns, string value, XmlQualifiedName xsiType)
381 if (value == null) return;
383 if (xsiType != null) {
384 localName = XmlCustomFormatter.FromXmlNCName (localName);
385 WriteStartElement (localName, ns);
386 WriteXsiType (xsiType.Name, xsiType.Namespace);
387 Writer.WriteString (value);
391 Writer.WriteElementString (localName, ns, value);
394 protected void WriteElementStringRaw (string localName, byte[] value)
396 WriteElementStringRaw (localName, String.Empty, value, null);
399 protected void WriteElementStringRaw (string localName, string value)
401 WriteElementStringRaw (localName, String.Empty, value, null);
404 protected void WriteElementStringRaw (string localName, byte[] value, XmlQualifiedName xsiType)
406 WriteElementStringRaw (localName, String.Empty, value, xsiType);
409 protected void WriteElementStringRaw (string localName, string ns, byte[] value)
411 WriteElementStringRaw (localName, ns, value, null);
414 protected void WriteElementStringRaw (string localName, string ns, string value)
416 WriteElementStringRaw (localName, ns, value, null);
419 protected void WriteElementStringRaw (string localName, string value, XmlQualifiedName xsiType)
421 WriteElementStringRaw (localName, String.Empty, value, null);
424 protected void WriteElementStringRaw (string localName, string ns, byte[] value, XmlQualifiedName xsiType)
429 WriteStartElement (localName, ns);
432 WriteXsiType (xsiType.Name, xsiType.Namespace);
434 if (value.Length > 0)
435 Writer.WriteBase64(value,0,value.Length);
439 protected void WriteElementStringRaw (string localName, string ns, string value, XmlQualifiedName xsiType)
441 localName = XmlCustomFormatter.FromXmlNCName (localName);
442 WriteStartElement (localName, ns);
445 WriteXsiType (xsiType.Name, xsiType.Namespace);
447 Writer.WriteRaw (value);
451 protected void WriteEmptyTag (string name)
453 WriteEmptyTag (name, String.Empty);
456 protected void WriteEmptyTag (string name, string ns)
458 name = XmlCustomFormatter.FromXmlName (name);
459 WriteStartElement (name, ns);
463 protected void WriteEndElement ()
465 WriteEndElement (null);
468 protected void WriteEndElement (object o)
471 serializedObjects.Remove (o);
473 Writer.WriteEndElement ();
476 protected void WriteId (object o)
478 WriteAttribute ("id", GetId (o, true));
481 protected void WriteNamespaceDeclarations (XmlSerializerNamespaces ns)
486 ICollection namespaces = ns.Namespaces.Values;
487 foreach (XmlQualifiedName qn in namespaces) {
488 if (qn.Namespace != String.Empty && Writer.LookupPrefix (qn.Namespace) == null)
489 WriteAttribute ("xmlns", qn.Name, xmlNamespace, qn.Namespace);
493 protected void WriteNullableQualifiedNameEncoded (string name, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)
496 WriteElementQualifiedName (name, ns, value, xsiType);
498 WriteNullTagEncoded (name, ns);
501 protected void WriteNullableQualifiedNameLiteral (string name, string ns, XmlQualifiedName value)
504 WriteElementQualifiedName (name, ns, value);
506 WriteNullTagLiteral (name, ns);
509 protected void WriteNullableStringEncoded (string name, string ns, string value, XmlQualifiedName xsiType)
512 WriteElementString (name, ns, value, xsiType);
514 WriteNullTagEncoded (name, ns);
517 protected void WriteNullableStringEncodedRaw (string name, string ns, byte[] value, XmlQualifiedName xsiType)
520 WriteNullTagEncoded (name, ns);
522 WriteElementStringRaw (name, ns, value, xsiType);
525 protected void WriteNullableStringEncodedRaw (string name, string ns, string value, XmlQualifiedName xsiType)
528 WriteNullTagEncoded (name, ns);
530 WriteElementStringRaw (name, ns, value, xsiType);
533 protected void WriteNullableStringLiteral (string name, string ns, string value)
536 WriteElementString (name, ns, value, null);
538 WriteNullTagLiteral (name, ns);
541 protected void WriteNullableStringLiteralRaw (string name, string ns, byte[] value)
544 WriteNullTagLiteral (name, ns);
546 WriteElementStringRaw (name, ns, value);
549 protected void WriteNullableStringLiteralRaw (string name, string ns, string value)
552 WriteNullTagLiteral (name, ns);
554 WriteElementStringRaw (name, ns, value);
557 protected void WriteNullTagEncoded (string name)
559 WriteNullTagEncoded (name, String.Empty);
562 protected void WriteNullTagEncoded (string name, string ns)
564 Writer.WriteStartElement (name, ns);
566 Writer.WriteAttributeString ("nil", XmlSchema.InstanceNamespace, "true");
568 Writer.WriteAttributeString ("null", XmlSchema.InstanceNamespace, "1");
570 Writer.WriteEndElement ();
573 protected void WriteNullTagLiteral (string name)
575 WriteNullTagLiteral (name, String.Empty);
578 protected void WriteNullTagLiteral (string name, string ns)
580 WriteStartElement (name, ns);
581 Writer.WriteAttributeString ("nil", XmlSchema.InstanceNamespace, "true");
585 protected void WritePotentiallyReferencingElement (string n, string ns, object o)
587 WritePotentiallyReferencingElement (n, ns, o, null, false, false);
590 protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType)
592 WritePotentiallyReferencingElement (n, ns, o, ambientType, false, false);
595 protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType, bool suppressReference)
597 WritePotentiallyReferencingElement (n, ns, o, ambientType, suppressReference, false);
600 protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType, bool suppressReference, bool isNullable)
604 if (isNullable) WriteNullTagEncoded (n, ns);
608 WriteStartElement (n, ns, true);
610 CheckReferenceQueue ();
612 if (callbacks.ContainsKey (o.GetType ()))
614 WriteCallbackInfo info = (WriteCallbackInfo) callbacks[o.GetType()];
615 if (o.GetType ().IsEnum) {
618 else if (suppressReference) {
619 Writer.WriteAttributeString ("id", GetId (o, false));
620 if (ambientType != o.GetType ()) WriteXsiType(info.TypeName, info.TypeNs);
624 if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
625 Writer.WriteAttributeString ("href", "#" + GetId (o, true));
630 // Must be a primitive type
631 TypeData td = TypeTranslator.GetTypeData (o.GetType ());
632 if (td.SchemaType != SchemaTypes.Primitive)
633 throw new InvalidOperationException ("Invalid type: " + o.GetType().FullName);
634 WriteXsiType(td.XmlType, XmlSchema.Namespace);
635 Writer.WriteString (XmlCustomFormatter.ToXmlString (td, o));
641 protected void WriteReferencedElements ()
643 if (referencedElements == null) return;
644 if (callbacks == null) return;
646 while (referencedElements.Count > 0)
648 object o = referencedElements.Dequeue ();
649 TypeData td = TypeTranslator.GetTypeData (o.GetType ());
650 WriteCallbackInfo info = (WriteCallbackInfo) callbacks[o.GetType()];
651 WriteStartElement (info.TypeName, info.TypeNs, true);
652 Writer.WriteAttributeString ("id", GetId (o, false));
654 if (td.SchemaType != SchemaTypes.Array) // Array use its own "arrayType" attribute
655 WriteXsiType(info.TypeName, info.TypeNs);
662 protected void WriteReferencingElement (string n, string ns, object o)
664 WriteReferencingElement (n, ns, o, false);
667 protected void WriteReferencingElement (string n, string ns, object o, bool isNullable)
671 if (isNullable) WriteNullTagEncoded (n, ns);
676 CheckReferenceQueue ();
677 if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
679 Writer.WriteStartElement (n, ns);
680 Writer.WriteAttributeString ("href", "#" + GetId (o, true));
681 Writer.WriteEndElement ();
685 void CheckReferenceQueue ()
687 if (referencedElements == null)
689 referencedElements = new Queue ();
695 protected void WriteRpcResult (string name, string ns)
697 throw new NotImplementedException ();
700 protected void WriteSerializable (IXmlSerializable serializable, string name, string ns, bool isNullable)
702 if (serializable == null)
704 if (isNullable) WriteNullTagLiteral (name, ns);
709 Writer.WriteStartElement (name, ns);
710 serializable.WriteXml (Writer);
711 Writer.WriteEndElement ();
715 protected void WriteStartDocument ()
717 if (Writer.WriteState == WriteState.Start)
718 Writer.WriteStartDocument ();
721 protected void WriteStartElement (string name)
723 WriteStartElement (name, String.Empty, null, false);
726 protected void WriteStartElement (string name, string ns)
728 WriteStartElement (name, ns, null, false);
731 protected void WriteStartElement (string name, string ns, bool writePrefixed)
733 WriteStartElement (name, ns, null, writePrefixed);
736 protected void WriteStartElement (string name, string ns, object o)
738 WriteStartElement (name, ns, o, false);
741 protected void WriteStartElement (string name, string ns, object o, bool writePrefixed)
745 if (serializedObjects.Contains (o))
746 throw new InvalidOperationException ("A cirtular reference was detected while serializing an object of type " + o.GetType().Name);
748 serializedObjects [o] = o;
751 WriteState oldState = Writer.WriteState;
753 string prefix = null;
755 if (topLevelElement && ns != null && ns.Length != 0)
757 foreach (XmlQualifiedName qn in namespaces)
758 if (qn.Namespace == ns) {
760 writePrefixed = true;
765 if (writePrefixed && ns != string.Empty)
767 name = XmlCustomFormatter.FromXmlName (name);
770 prefix = Writer.LookupPrefix (ns);
771 if (prefix == null || prefix.Length == 0)
772 prefix = "q" + (++qnameCount);
773 Writer.WriteStartElement (prefix, name, ns);
775 Writer.WriteStartElement (name, ns);
779 if (namespaces != null) {
780 foreach (XmlQualifiedName qn in namespaces)
782 string currentPrefix = Writer.LookupPrefix (qn.Namespace);
783 if (currentPrefix != null && currentPrefix.Length != 0) continue;
785 WriteAttribute ("xmlns",qn.Name,xmlNamespace,qn.Namespace);
788 topLevelElement = false;
792 protected void WriteTypedPrimitive (string name, string ns, object o, bool xsiType)
795 TypeData td = TypeTranslator.GetTypeData (o.GetType ());
797 name = XmlCustomFormatter.FromXmlName (name);
798 Writer.WriteStartElement (name, ns);
800 if (o is XmlQualifiedName)
801 value = FromXmlQualifiedName ((XmlQualifiedName) o);
803 value = XmlCustomFormatter.ToXmlString (td, o);
807 if (td.SchemaType != SchemaTypes.Primitive)
808 throw new InvalidOperationException (string.Format (unexpectedTypeError, o.GetType().FullName));
809 WriteXsiType (td.XmlType, XmlSchema.Namespace);
813 Writer.WriteEndElement ();
816 protected void WriteValue (byte[] value)
818 Writer.WriteBase64 (value, 0, value.Length);
821 protected void WriteValue (string value)
824 Writer.WriteString (value);
827 protected void WriteXmlAttribute (XmlNode node)
829 WriteXmlAttribute (node, null);
832 protected void WriteXmlAttribute (XmlNode node, object container)
834 XmlAttribute attr = node as XmlAttribute;
836 throw new InvalidOperationException ("The node must be either type XmlAttribute or a derived type.");
838 if (attr.NamespaceURI == XmlSerializer.WsdlNamespace)
840 // The wsdl arrayType attribute needs special handling
841 if (attr.LocalName == "arrayType") {
842 string ns, type, dimensions;
843 TypeTranslator.ParseArrayType (attr.Value, out type, out ns, out dimensions);
844 string value = GetQualifiedName (type + dimensions, ns);
845 WriteAttribute (attr.Prefix, attr.LocalName, attr.NamespaceURI, value);
850 WriteAttribute (attr.Prefix, attr.LocalName, attr.NamespaceURI, attr.Value);
853 protected void WriteXsiType (string name, string ns)
855 if (ns != null && ns != string.Empty)
856 WriteAttribute ("type", XmlSchema.InstanceNamespace, GetQualifiedName (name, ns));
858 WriteAttribute ("type", XmlSchema.InstanceNamespace, name);
864 protected Exception CreateInvalidAnyTypeException (object o)
866 throw new NotImplementedException ();
870 protected Exception CreateInvalidAnyTypeException (Type t)
872 throw new NotImplementedException ();
876 protected Exception CreateInvalidEnumValueException (object value, string typeName)
878 throw new NotImplementedException ();
882 protected static string FromEnum (long value, string[] values, long[] ids, string typeName)
884 throw new NotImplementedException ();
888 protected string FromXmlQualifiedName (XmlQualifiedName xmlQualifiedName, bool ignoreEmpty)
890 throw new NotImplementedException ();
894 protected static Assembly ResolveDynamicAssembly (string assemblyFullName)
896 throw new NotImplementedException ();
900 protected void WriteSerializable (IXmlSerializable serializable, string name, string ns, bool isNullable, bool any)
902 throw new NotImplementedException ();
906 protected bool EscapeName
908 get { throw new NotImplementedException(); }
909 set { throw new NotImplementedException(); }
915 class WriteCallbackInfo
918 public string TypeName;
919 public string TypeNs;
920 public XmlSerializationWriteCallback Callback;