2 // System.Xml.XmlWriter
5 // Kral Ferch <kral_ferch@hotmail.com>
6 // Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>
9 // (C) 2002-2003 Atsushi Enomoto
10 // (C) 2004-2007 Novell, Inc.
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
39 using System.Xml.XPath;
44 public abstract class XmlWriter : IDisposable
47 XmlWriterSettings settings;
52 protected XmlWriter () { }
59 public virtual XmlWriterSettings Settings {
60 get { return settings; }
64 public abstract WriteState WriteState { get; }
68 public virtual string XmlLang {
72 public virtual XmlSpace XmlSpace {
73 get { return XmlSpace.None; }
76 public abstract string XmlLang { get; }
78 public abstract XmlSpace XmlSpace { get; }
85 public abstract void Close ();
88 public static XmlWriter Create (Stream stream)
90 return Create (stream, null);
93 public static XmlWriter Create (string file)
95 return Create (file, null);
98 public static XmlWriter Create (TextWriter writer)
100 return Create (writer, null);
103 public static XmlWriter Create (XmlWriter writer)
105 return Create (writer, null);
108 public static XmlWriter Create (StringBuilder builder)
110 return Create (builder, null);
113 public static XmlWriter Create (Stream stream, XmlWriterSettings settings)
115 Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
116 return Create (new StreamWriter (stream, enc), settings);
119 public static XmlWriter Create (string file, XmlWriterSettings settings)
121 Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
122 return CreateTextWriter (new StreamWriter (file, false, enc), settings, true);
125 public static XmlWriter Create (StringBuilder builder, XmlWriterSettings settings)
127 return Create (new StringWriter (builder), settings);
130 public static XmlWriter Create (TextWriter writer, XmlWriterSettings settings)
132 if (settings == null)
133 settings = new XmlWriterSettings ();
134 return CreateTextWriter (writer, settings, settings.CloseOutput);
137 public static XmlWriter Create (XmlWriter writer, XmlWriterSettings settings)
139 if (settings == null)
140 settings = new XmlWriterSettings ();
142 settings = settings.Clone ();
144 var src = writer.Settings;
146 settings.ConformanceLevel = ConformanceLevel.Document; // Huh? Why??
147 writer = new DefaultXmlWriter (writer);
148 writer.settings = settings;
150 ConformanceLevel dst = src.ConformanceLevel;
151 switch (src.ConformanceLevel) {
152 case ConformanceLevel.Auto:
153 dst = settings.ConformanceLevel;
155 case ConformanceLevel.Document:
156 case ConformanceLevel.Fragment:
157 if (settings.ConformanceLevel != ConformanceLevel.Auto)
158 dst = settings.ConformanceLevel;
162 settings.MergeFrom (src);
164 // It returns a new XmlWriter instance if 1) Settings is null, or 2) Settings ConformanceLevel (or might be other members as well) give significant difference.
165 if (src.ConformanceLevel != dst) {
166 writer = new DefaultXmlWriter (writer, false);
167 writer.settings = settings;
174 private static XmlWriter CreateTextWriter (TextWriter writer, XmlWriterSettings settings, bool closeOutput)
176 if (settings == null)
177 settings = new XmlWriterSettings ();
178 XmlTextWriter xtw = new XmlTextWriter (writer, settings, closeOutput);
179 return Create (xtw, settings);
182 protected virtual void Dispose (bool disposing)
187 #if NET_4_0 || MOBILE
188 public void Dispose ()
190 void IDisposable.Dispose()
197 public abstract void Flush ();
199 public abstract string LookupPrefix (string ns);
201 private void WriteAttribute (XmlReader reader, bool defattr)
203 if (!defattr && reader.IsDefault)
206 WriteStartAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI);
208 // no ReadAttributeValue() in 2.1 profile.
209 WriteString (reader.Value);
211 while (reader.ReadAttributeValue ()) {
212 switch (reader.NodeType) {
213 case XmlNodeType.Text:
214 WriteString (reader.Value);
216 case XmlNodeType.EntityReference:
217 WriteEntityRef (reader.Name);
222 WriteEndAttribute ();
225 public virtual void WriteAttributes (XmlReader reader, bool defattr)
228 throw new ArgumentException("null XmlReader specified.", "reader");
230 switch (reader.NodeType) {
231 case XmlNodeType.XmlDeclaration:
232 WriteAttributeString ("version", reader ["version"]);
233 if (reader ["encoding"] != null)
234 WriteAttributeString ("encoding", reader ["encoding"]);
235 if (reader ["standalone"] != null)
236 WriteAttributeString ("standalone", reader ["standalone"]);
238 case XmlNodeType.Element:
239 if (reader.MoveToFirstAttribute ())
240 goto case XmlNodeType.Attribute;
242 case XmlNodeType.Attribute:
244 WriteAttribute (reader, defattr);
245 } while (reader.MoveToNextAttribute ());
246 reader.MoveToElement ();
249 throw new XmlException("NodeType is not one of Element, Attribute, nor XmlDeclaration.");
253 public void WriteAttributeString (string localName, string value)
255 WriteAttributeString ("", localName, null, value);
258 public void WriteAttributeString (string localName, string ns, string value)
260 WriteAttributeString ("", localName, ns, value);
263 public void WriteAttributeString (string prefix, string localName, string ns, string value)
265 // In MS.NET (1.0), this check is done *here*, not at WriteStartAttribute.
266 // (XmlTextWriter.WriteStartAttribute("xmlns", "anyname", null) throws an exception.
268 WriteStartAttribute (prefix, localName, ns);
269 if (value != null && value.Length > 0)
271 WriteEndAttribute ();
274 public abstract void WriteBase64 (byte[] buffer, int index, int count);
277 public virtual void WriteBinHex (byte [] buffer, int index, int count)
279 StringWriter sw = new StringWriter ();
280 XmlConvert.WriteBinHex (buffer, index, count, sw);
281 WriteString (sw.ToString ());
284 public abstract void WriteBinHex (byte[] buffer, int index, int count);
287 public abstract void WriteCData (string text);
289 public abstract void WriteCharEntity (char ch);
291 public abstract void WriteChars (char[] buffer, int index, int count);
293 public abstract void WriteComment (string text);
295 public abstract void WriteDocType (string name, string pubid, string sysid, string subset);
297 public void WriteElementString (string localName, string value)
299 WriteStartElement(localName);
300 if (value != null && value.Length > 0)
305 public void WriteElementString (string localName, string ns, string value)
307 WriteStartElement(localName, ns);
308 if (value != null && value.Length > 0)
314 public void WriteElementString (string prefix, string localName, string ns, string value)
316 WriteStartElement(prefix, localName, ns);
317 if (value != null && value.Length > 0)
323 public abstract void WriteEndAttribute ();
325 public abstract void WriteEndDocument ();
327 public abstract void WriteEndElement ();
329 public abstract void WriteEntityRef (string name);
331 public abstract void WriteFullEndElement ();
334 public virtual void WriteName (string name)
336 WriteNameInternal (name);
339 public virtual void WriteNmToken (string name)
341 WriteNmTokenInternal (name);
344 public virtual void WriteQualifiedName (string localName, string ns)
346 WriteQualifiedNameInternal (localName, ns);
349 public abstract void WriteName (string name);
351 public abstract void WriteNmToken (string name);
353 public abstract void WriteQualifiedName (string localName, string ns);
356 internal void WriteNameInternal (string name)
359 switch (Settings.ConformanceLevel) {
360 case ConformanceLevel.Document:
361 case ConformanceLevel.Fragment:
362 XmlConvert.VerifyName (name);
366 XmlConvert.VerifyName (name);
371 internal virtual void WriteNmTokenInternal (string name)
375 switch (Settings.ConformanceLevel) {
376 case ConformanceLevel.Document:
377 case ConformanceLevel.Fragment:
378 valid = XmlChar.IsNmToken (name);
382 valid = XmlChar.IsNmToken (name);
385 throw new ArgumentException ("Argument name is not a valid NMTOKEN.");
389 internal void WriteQualifiedNameInternal (string localName, string ns)
391 if (localName == null || localName == String.Empty)
392 throw new ArgumentException ();
397 if (Settings != null) {
398 switch (Settings.ConformanceLevel) {
399 case ConformanceLevel.Document:
400 case ConformanceLevel.Fragment:
401 XmlConvert.VerifyNCName (localName);
406 XmlConvert.VerifyNCName (localName);
408 XmlConvert.VerifyNCName (localName);
411 string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
413 throw new ArgumentException (String.Format ("Namespace '{0}' is not declared.", ns));
415 if (prefix != String.Empty) {
416 WriteString (prefix);
418 WriteString (localName);
421 WriteString (localName);
425 public virtual void WriteNode (XPathNavigator navigator, bool defattr)
427 if (navigator == null)
428 throw new ArgumentNullException ("navigator");
429 switch (navigator.NodeType) {
430 case XPathNodeType.Attribute:
433 case XPathNodeType.Namespace:
436 case XPathNodeType.Text:
437 WriteString (navigator.Value);
439 case XPathNodeType.SignificantWhitespace:
440 WriteWhitespace (navigator.Value);
442 case XPathNodeType.Whitespace:
443 WriteWhitespace (navigator.Value);
445 case XPathNodeType.Comment:
446 WriteComment (navigator.Value);
448 case XPathNodeType.ProcessingInstruction:
449 WriteProcessingInstruction (navigator.Name, navigator.Value);
451 case XPathNodeType.Root:
452 if (navigator.MoveToFirstChild ()) {
454 WriteNode (navigator, defattr);
455 } while (navigator.MoveToNext ());
456 navigator.MoveToParent ();
459 case XPathNodeType.Element:
460 WriteStartElement (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
461 if (navigator.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
463 if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault)
464 WriteAttributeString (navigator.Prefix,
465 navigator.LocalName == String.Empty ? "xmlns" : navigator.LocalName,
466 "http://www.w3.org/2000/xmlns/",
468 } while (navigator.MoveToNextNamespace (XPathNamespaceScope.Local));
469 navigator.MoveToParent ();
471 if (navigator.MoveToFirstAttribute ()) {
473 if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault)
474 WriteAttributeString (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI, navigator.Value);
476 } while (navigator.MoveToNextAttribute ());
477 navigator.MoveToParent ();
479 if (navigator.MoveToFirstChild ()) {
481 WriteNode (navigator, defattr);
482 } while (navigator.MoveToNext ());
483 navigator.MoveToParent ();
485 if (navigator.IsEmptyElement)
488 WriteFullEndElement ();
491 throw new NotSupportedException ();
496 public virtual void WriteNode (XmlReader reader, bool defattr)
499 throw new ArgumentException ();
501 if (reader.ReadState == ReadState.Initial) {
504 WriteNode (reader, defattr);
505 } while (!reader.EOF);
509 switch (reader.NodeType) {
510 case XmlNodeType.Element:
511 WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
513 WriteAttributes (reader, defattr);
514 reader.MoveToElement ();
516 // Well, I found that MS.NET took this way, since
517 // there was a error-prone SgmlReader that fails
518 // MoveToNextAttribute().
519 if (reader.HasAttributes) {
520 for (int i = 0; i < reader.AttributeCount; i++) {
521 reader.MoveToAttribute (i);
522 WriteAttribute (reader, defattr);
524 reader.MoveToElement ();
527 if (reader.IsEmptyElement)
530 int depth = reader.Depth;
532 if (reader.NodeType != XmlNodeType.EndElement) {
534 WriteNode (reader, defattr);
535 } while (depth < reader.Depth);
537 WriteFullEndElement ();
540 // In case of XmlAttribute, don't proceed reader, and it will never be written.
541 case XmlNodeType.Attribute:
543 case XmlNodeType.Text:
544 WriteString (reader.Value);
546 case XmlNodeType.CDATA:
547 WriteCData (reader.Value);
549 case XmlNodeType.EntityReference:
550 WriteEntityRef (reader.Name);
552 case XmlNodeType.XmlDeclaration:
553 // LAMESPEC: It means that XmlWriter implementation _must not_ check
554 // whether PI name is "xml" (it is XML error) or not.
555 case XmlNodeType.ProcessingInstruction:
556 WriteProcessingInstruction (reader.Name, reader.Value);
558 case XmlNodeType.Comment:
559 WriteComment (reader.Value);
561 case XmlNodeType.DocumentType:
562 WriteDocType (reader.Name,
563 reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
565 case XmlNodeType.SignificantWhitespace:
566 goto case XmlNodeType.Whitespace;
567 case XmlNodeType.Whitespace:
568 WriteWhitespace (reader.Value);
570 case XmlNodeType.EndElement:
571 WriteFullEndElement ();
573 case XmlNodeType.EndEntity:
575 case XmlNodeType.None:
576 break; // Do nothing, nor reporting errors.
578 throw new XmlException ("Unexpected node " + reader.Name + " of type " + reader.NodeType);
583 public abstract void WriteProcessingInstruction (string name, string text);
585 public abstract void WriteRaw (string data);
587 public abstract void WriteRaw (char[] buffer, int index, int count);
590 public void WriteStartAttribute (string localName)
592 WriteStartAttribute (null, localName, null);
596 public void WriteStartAttribute (string localName, string ns)
598 WriteStartAttribute (null, localName, ns);
601 public abstract void WriteStartAttribute (string prefix, string localName, string ns);
603 public abstract void WriteStartDocument ();
605 public abstract void WriteStartDocument (bool standalone);
607 public void WriteStartElement (string localName)
609 WriteStartElement (null, localName, null);
612 public void WriteStartElement (string localName, string ns)
614 WriteStartElement (null, localName, ns);
617 public abstract void WriteStartElement (string prefix, string localName, string ns);
619 public abstract void WriteString (string text);
621 public abstract void WriteSurrogateCharEntity (char lowChar, char highChar);
623 public abstract void WriteWhitespace (string ws);
626 public virtual void WriteValue (bool value)
628 WriteString (XQueryConvert.BooleanToString (value));
631 public virtual void WriteValue (DateTime value)
633 WriteString (XmlConvert.ToString (value));
636 public virtual void WriteValue (decimal value)
638 WriteString (XQueryConvert.DecimalToString (value));
641 public virtual void WriteValue (double value)
643 WriteString (XQueryConvert.DoubleToString (value));
646 public virtual void WriteValue (int value)
648 WriteString (XQueryConvert.IntToString (value));
651 public virtual void WriteValue (long value)
653 WriteString (XQueryConvert.IntegerToString (value));
656 public virtual void WriteValue (object value)
659 throw new ArgumentNullException ("value");
662 WriteString ((string) value);
663 else if (value is bool)
664 WriteValue ((bool) value);
665 else if (value is byte)
666 WriteValue ((int) value);
667 else if (value is byte [])
668 WriteBase64 ((byte []) value, 0, ((byte []) value).Length);
669 else if (value is char [])
670 WriteChars ((char []) value, 0, ((char []) value).Length);
671 else if (value is DateTime)
672 WriteValue ((DateTime) value);
673 else if (value is decimal)
674 WriteValue ((decimal) value);
675 else if (value is double)
676 WriteValue ((double) value);
677 else if (value is short)
678 WriteValue ((int) value);
679 else if (value is int)
680 WriteValue ((int) value);
681 else if (value is long)
682 WriteValue ((long) value);
683 else if (value is float)
684 WriteValue ((float) value);
685 else if (value is TimeSpan) // undocumented
686 WriteString (XmlConvert.ToString ((TimeSpan) value));
687 else if (value is Uri)
688 WriteString (((Uri) value).ToString ());
689 else if (value is XmlQualifiedName) {
690 XmlQualifiedName qname = (XmlQualifiedName) value;
691 if (!qname.Equals (XmlQualifiedName.Empty)) {
692 if (qname.Namespace.Length > 0 && LookupPrefix (qname.Namespace) == null)
693 throw new InvalidCastException (String.Format ("The QName '{0}' cannot be written. No corresponding prefix is declared", qname));
694 WriteQualifiedName (qname.Name, qname.Namespace);
697 WriteString (String.Empty);
699 else if (value is IEnumerable) {
701 foreach (object obj in (IEnumerable) value) {
710 throw new InvalidCastException (String.Format ("Type '{0}' cannot be cast to string", value.GetType ()));
713 public virtual void WriteValue (float value)
715 WriteString (XQueryConvert.FloatToString (value));
718 public virtual void WriteValue (string value)