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;
38 using System.Xml.XPath;
40 using System.Threading;
41 using System.Threading.Tasks;
46 public abstract class XmlWriter : IDisposable
48 XmlWriterSettings settings;
52 protected XmlWriter () { }
58 public virtual XmlWriterSettings Settings {
59 get { return settings; }
62 public abstract WriteState WriteState { get; }
64 public virtual string XmlLang {
68 public virtual XmlSpace XmlSpace {
69 get { return XmlSpace.None; }
77 public virtual void Close ()
80 throw new InvalidOperationException ("An asynchronous operation is already in progress.");
83 public abstract void Close ();
86 public static XmlWriter Create (Stream output)
88 return Create (output, null);
91 public static XmlWriter Create (string outputFileName)
93 return Create (outputFileName, null);
96 public static XmlWriter Create (TextWriter output)
98 return Create (output, null);
101 public static XmlWriter Create (XmlWriter output)
103 return Create (output, null);
106 public static XmlWriter Create (StringBuilder output)
108 return Create (output, null);
111 public static XmlWriter Create (Stream output, XmlWriterSettings settings)
113 Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
114 return Create (new StreamWriter (output, enc), settings);
117 public static XmlWriter Create (string outputFileName, XmlWriterSettings settings)
119 Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
120 return CreateTextWriter (new StreamWriter (outputFileName, false, enc), settings, true);
123 public static XmlWriter Create (StringBuilder output, XmlWriterSettings settings)
125 return Create (new StringWriter (output), settings);
128 public static XmlWriter Create (TextWriter output, XmlWriterSettings settings)
130 if (settings == null)
131 settings = new XmlWriterSettings ();
132 return CreateTextWriter (output, settings, settings.CloseOutput);
135 public static XmlWriter Create (XmlWriter output, XmlWriterSettings settings)
137 if (settings == null)
138 settings = new XmlWriterSettings ();
140 settings = settings.Clone ();
142 var src = output.Settings;
144 settings.ConformanceLevel = ConformanceLevel.Document; // Huh? Why??
145 output = new DefaultXmlWriter (output);
147 settings.SetReadOnly ();
149 output.settings = settings;
151 ConformanceLevel dst = src.ConformanceLevel;
152 switch (src.ConformanceLevel) {
153 case ConformanceLevel.Auto:
154 dst = settings.ConformanceLevel;
156 case ConformanceLevel.Document:
157 case ConformanceLevel.Fragment:
158 if (settings.ConformanceLevel != ConformanceLevel.Auto)
159 dst = settings.ConformanceLevel;
163 settings.MergeFrom (src);
166 settings.SetReadOnly ();
169 // 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.
170 if (src.ConformanceLevel != dst) {
171 output = new DefaultXmlWriter (output, false);
172 output.settings = settings;
179 private static XmlWriter CreateTextWriter (TextWriter writer, XmlWriterSettings settings, bool closeOutput)
181 if (settings == null)
182 settings = new XmlWriterSettings ();
183 XmlTextWriter xtw = new XmlTextWriter (writer, settings, closeOutput);
184 return Create (xtw, settings);
187 protected virtual void Dispose (bool disposing)
192 #if NET_4_0 || MOBILE
193 public void Dispose ()
195 void IDisposable.Dispose()
201 public abstract void Flush ();
203 public abstract string LookupPrefix (string ns);
205 private void WriteAttribute (XmlReader reader, bool defattr)
207 if (!defattr && reader.IsDefault)
210 WriteStartAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI);
211 while (reader.ReadAttributeValue ()) {
212 switch (reader.NodeType) {
213 case XmlNodeType.Text:
214 WriteString (reader.Value);
216 case XmlNodeType.EntityReference:
217 WriteEntityRef (reader.Name);
221 WriteEndAttribute ();
224 public virtual void WriteAttributes (XmlReader reader, bool defattr)
227 throw new ArgumentException("null XmlReader specified.", "reader");
229 switch (reader.NodeType) {
230 case XmlNodeType.XmlDeclaration:
231 WriteAttributeString ("version", reader ["version"]);
232 if (reader ["encoding"] != null)
233 WriteAttributeString ("encoding", reader ["encoding"]);
234 if (reader ["standalone"] != null)
235 WriteAttributeString ("standalone", reader ["standalone"]);
237 case XmlNodeType.Element:
238 if (reader.MoveToFirstAttribute ())
239 goto case XmlNodeType.Attribute;
241 case XmlNodeType.Attribute:
243 WriteAttribute (reader, defattr);
244 } while (reader.MoveToNextAttribute ());
245 reader.MoveToElement ();
248 throw new XmlException("NodeType is not one of Element, Attribute, nor XmlDeclaration.");
252 public void WriteAttributeString (string localName, string value)
254 WriteAttributeString ("", localName, null, value);
257 public void WriteAttributeString (string localName, string ns, string value)
259 WriteAttributeString ("", localName, ns, value);
262 public void WriteAttributeString (string prefix, string localName, string ns, string value)
264 // In MS.NET (1.0), this check is done *here*, not at WriteStartAttribute.
265 // (XmlTextWriter.WriteStartAttribute("xmlns", "anyname", null) throws an exception.
267 WriteStartAttribute (prefix, localName, ns);
268 if (value != null && value.Length > 0)
270 WriteEndAttribute ();
273 public abstract void WriteBase64 (byte[] buffer, int index, int count);
275 public virtual void WriteBinHex (byte [] buffer, int index, int count)
277 StringWriter sw = new StringWriter ();
278 XmlConvert.WriteBinHex (buffer, index, count, sw);
279 WriteString (sw.ToString ());
282 public abstract void WriteCData (string text);
284 public abstract void WriteCharEntity (char ch);
286 public abstract void WriteChars (char[] buffer, int index, int count);
288 public abstract void WriteComment (string text);
290 public abstract void WriteDocType (string name, string pubid, string sysid, string subset);
292 public void WriteElementString (string localName, string value)
294 WriteStartElement(localName);
295 if (value != null && value.Length > 0)
300 public void WriteElementString (string localName, string ns, string value)
302 WriteStartElement(localName, ns);
303 if (value != null && value.Length > 0)
308 public void WriteElementString (string prefix, string localName, string ns, string value)
310 WriteStartElement(prefix, localName, ns);
311 if (value != null && value.Length > 0)
316 public abstract void WriteEndAttribute ();
318 public abstract void WriteEndDocument ();
320 public abstract void WriteEndElement ();
322 public abstract void WriteEntityRef (string name);
324 public abstract void WriteFullEndElement ();
326 public virtual void WriteName (string name)
328 WriteNameInternal (name);
331 public virtual void WriteNmToken (string name)
333 WriteNmTokenInternal (name);
336 public virtual void WriteQualifiedName (string localName, string ns)
338 WriteQualifiedNameInternal (localName, ns);
341 internal void WriteNameInternal (string name)
343 switch (Settings.ConformanceLevel) {
344 case ConformanceLevel.Document:
345 case ConformanceLevel.Fragment:
346 XmlConvert.VerifyName (name);
352 internal virtual void WriteNmTokenInternal (string name)
355 switch (Settings.ConformanceLevel) {
356 case ConformanceLevel.Document:
357 case ConformanceLevel.Fragment:
358 valid = XmlChar.IsNmToken (name);
362 throw new ArgumentException ("Argument name is not a valid NMTOKEN.");
366 internal void WriteQualifiedNameInternal (string localName, string ns)
368 if (localName == null || localName == String.Empty)
369 throw new ArgumentException ();
373 if (Settings != null) {
374 switch (Settings.ConformanceLevel) {
375 case ConformanceLevel.Document:
376 case ConformanceLevel.Fragment:
377 XmlConvert.VerifyNCName (localName);
382 XmlConvert.VerifyNCName (localName);
384 string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
386 throw new ArgumentException (String.Format ("Namespace '{0}' is not declared.", ns));
388 if (prefix != String.Empty) {
389 WriteString (prefix);
391 WriteString (localName);
394 WriteString (localName);
397 public virtual void WriteNode (XPathNavigator navigator, bool defattr)
399 if (navigator == null)
400 throw new ArgumentNullException ("navigator");
401 switch (navigator.NodeType) {
402 case XPathNodeType.Attribute:
405 case XPathNodeType.Namespace:
408 case XPathNodeType.Text:
409 WriteString (navigator.Value);
411 case XPathNodeType.SignificantWhitespace:
412 WriteWhitespace (navigator.Value);
414 case XPathNodeType.Whitespace:
415 WriteWhitespace (navigator.Value);
417 case XPathNodeType.Comment:
418 WriteComment (navigator.Value);
420 case XPathNodeType.ProcessingInstruction:
421 WriteProcessingInstruction (navigator.Name, navigator.Value);
423 case XPathNodeType.Root:
424 if (navigator.MoveToFirstChild ()) {
426 WriteNode (navigator, defattr);
427 } while (navigator.MoveToNext ());
428 navigator.MoveToParent ();
431 case XPathNodeType.Element:
432 WriteStartElement (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
433 if (navigator.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
435 if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault)
436 WriteAttributeString (navigator.Prefix,
437 navigator.LocalName == String.Empty ? "xmlns" : navigator.LocalName,
438 "http://www.w3.org/2000/xmlns/",
440 } while (navigator.MoveToNextNamespace (XPathNamespaceScope.Local));
441 navigator.MoveToParent ();
443 if (navigator.MoveToFirstAttribute ()) {
445 if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault)
446 WriteAttributeString (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI, navigator.Value);
448 } while (navigator.MoveToNextAttribute ());
449 navigator.MoveToParent ();
451 if (navigator.MoveToFirstChild ()) {
453 WriteNode (navigator, defattr);
454 } while (navigator.MoveToNext ());
455 navigator.MoveToParent ();
457 if (navigator.IsEmptyElement)
460 WriteFullEndElement ();
463 throw new NotSupportedException ();
467 public virtual void WriteNode (XmlReader reader, bool defattr)
470 throw new ArgumentException ();
472 if (reader.ReadState == ReadState.Initial) {
475 WriteNode (reader, defattr);
476 } while (!reader.EOF);
480 switch (reader.NodeType) {
481 case XmlNodeType.Element:
482 WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
484 WriteAttributes (reader, defattr);
485 reader.MoveToElement ();
487 // Well, I found that MS.NET took this way, since
488 // there was a error-prone SgmlReader that fails
489 // MoveToNextAttribute().
490 if (reader.HasAttributes) {
491 for (int i = 0; i < reader.AttributeCount; i++) {
492 reader.MoveToAttribute (i);
493 WriteAttribute (reader, defattr);
495 reader.MoveToElement ();
498 if (reader.IsEmptyElement)
501 int depth = reader.Depth;
503 if (reader.NodeType != XmlNodeType.EndElement) {
505 WriteNode (reader, defattr);
506 } while (depth < reader.Depth);
508 WriteFullEndElement ();
511 // In case of XmlAttribute, don't proceed reader, and it will never be written.
512 case XmlNodeType.Attribute:
514 case XmlNodeType.Text:
515 WriteString (reader.Value);
517 case XmlNodeType.CDATA:
518 WriteCData (reader.Value);
520 case XmlNodeType.EntityReference:
521 WriteEntityRef (reader.Name);
523 case XmlNodeType.XmlDeclaration:
524 // LAMESPEC: It means that XmlWriter implementation _must not_ check
525 // whether PI name is "xml" (it is XML error) or not.
526 case XmlNodeType.ProcessingInstruction:
527 WriteProcessingInstruction (reader.Name, reader.Value);
529 case XmlNodeType.Comment:
530 WriteComment (reader.Value);
532 case XmlNodeType.DocumentType:
533 WriteDocType (reader.Name,
534 reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
536 case XmlNodeType.SignificantWhitespace:
537 goto case XmlNodeType.Whitespace;
538 case XmlNodeType.Whitespace:
539 WriteWhitespace (reader.Value);
541 case XmlNodeType.EndElement:
542 WriteFullEndElement ();
544 case XmlNodeType.EndEntity:
546 case XmlNodeType.None:
547 break; // Do nothing, nor reporting errors.
549 throw new XmlException ("Unexpected node " + reader.Name + " of type " + reader.NodeType);
554 public abstract void WriteProcessingInstruction (string name, string text);
556 public abstract void WriteRaw (string data);
558 public abstract void WriteRaw (char[] buffer, int index, int count);
560 public void WriteStartAttribute (string localName)
562 WriteStartAttribute (null, localName, null);
565 public void WriteStartAttribute (string localName, string ns)
567 WriteStartAttribute (null, localName, ns);
570 public abstract void WriteStartAttribute (string prefix, string localName, string ns);
572 public abstract void WriteStartDocument ();
574 public abstract void WriteStartDocument (bool standalone);
576 public void WriteStartElement (string localName)
578 WriteStartElement (null, localName, null);
581 public void WriteStartElement (string localName, string ns)
583 WriteStartElement (null, localName, ns);
586 public abstract void WriteStartElement (string prefix, string localName, string ns);
588 public abstract void WriteString (string text);
590 public abstract void WriteSurrogateCharEntity (char lowChar, char highChar);
592 public abstract void WriteWhitespace (string ws);
594 public virtual void WriteValue (bool value)
596 WriteString (XQueryConvert.BooleanToString (value));
599 public virtual void WriteValue (DateTime value)
601 WriteString (XmlConvert.ToString (value));
604 public virtual void WriteValue (decimal value)
606 WriteString (XQueryConvert.DecimalToString (value));
609 public virtual void WriteValue (double value)
611 WriteString (XQueryConvert.DoubleToString (value));
614 public virtual void WriteValue (int value)
616 WriteString (XQueryConvert.IntToString (value));
619 public virtual void WriteValue (long value)
621 WriteString (XQueryConvert.IntegerToString (value));
624 public virtual void WriteValue (object value)
627 throw new ArgumentNullException ("value");
630 WriteString ((string) value);
631 else if (value is bool)
632 WriteValue ((bool) value);
633 else if (value is byte)
634 WriteValue ((int) value);
635 else if (value is byte [])
636 WriteBase64 ((byte []) value, 0, ((byte []) value).Length);
637 else if (value is char [])
638 WriteChars ((char []) value, 0, ((char []) value).Length);
639 else if (value is DateTime)
640 WriteValue ((DateTime) value);
641 else if (value is decimal)
642 WriteValue ((decimal) value);
643 else if (value is double)
644 WriteValue ((double) value);
645 else if (value is short)
646 WriteValue ((int) value);
647 else if (value is int)
648 WriteValue ((int) value);
649 else if (value is long)
650 WriteValue ((long) value);
651 else if (value is float)
652 WriteValue ((float) value);
653 else if (value is TimeSpan) // undocumented
654 WriteString (XmlConvert.ToString ((TimeSpan) value));
655 else if (value is Uri)
656 WriteString (((Uri) value).ToString ());
657 else if (value is XmlQualifiedName) {
658 XmlQualifiedName qname = (XmlQualifiedName) value;
659 if (!qname.Equals (XmlQualifiedName.Empty)) {
660 if (qname.Namespace.Length > 0 && LookupPrefix (qname.Namespace) == null)
661 throw new InvalidCastException (String.Format ("The QName '{0}' cannot be written. No corresponding prefix is declared", qname));
662 WriteQualifiedName (qname.Name, qname.Namespace);
665 WriteString (String.Empty);
667 else if (value is IEnumerable) {
669 foreach (object obj in (IEnumerable) value) {
678 throw new InvalidCastException (String.Format ("Type '{0}' cannot be cast to string", value.GetType ()));
681 public virtual void WriteValue (float value)
683 WriteString (XQueryConvert.FloatToString (value));
686 public virtual void WriteValue (string value)
692 public virtual void WriteValue (DateTimeOffset value)
694 WriteString (XmlConvert.ToString (value));
701 #region .NET 4.5 Async Methods
708 throw new InvalidOperationException ("Set XmlWriterSettings.Async to true if you want to use Async Methods.");
711 throw new InvalidOperationException ("An asynchronous operation is already in progress.");
716 public virtual Task FlushAsync ()
719 return Task.Run (() => {
723 asyncRunning = false;
728 public virtual Task WriteAttributesAsync (XmlReader reader, bool defattr)
731 return Task.Run (() => {
733 WriteAttributes (reader, defattr);
735 asyncRunning = false;
740 public Task WriteAttributeStringAsync (string prefix, string localName,
741 string ns, string value)
744 return Task.Run (() => {
746 WriteAttributeString (prefix, localName, ns, value);
748 asyncRunning = false;
753 public virtual Task WriteBase64Async (byte[] buffer, int index, int count)
756 return Task.Run (() => {
758 WriteBase64 (buffer, index, count);
760 asyncRunning = false;
765 public virtual Task WriteBinHexAsync (byte[] buffer, int index, int count)
768 return Task.Run (() => {
770 WriteBinHex (buffer, index, count);
772 asyncRunning = false;
777 public virtual Task WriteCDataAsync (string text)
780 return Task.Run (() => {
784 asyncRunning = false;
789 public virtual Task WriteCharEntityAsync (char ch)
792 return Task.Run (() => {
794 WriteCharEntity (ch);
796 asyncRunning = false;
801 public virtual Task WriteCharsAsync (char[] buffer, int index, int count)
804 return Task.Run (() => {
806 WriteChars (buffer, index, count);
808 asyncRunning = false;
813 public virtual Task WriteCommentAsync (string text)
816 return Task.Run (() => {
820 asyncRunning = false;
825 public virtual Task WriteDocTypeAsync (string name, string pubid, string sysid, string subset)
828 return Task.Run (() => {
830 WriteDocType (name, pubid, sysid, subset);
832 asyncRunning = false;
837 public Task WriteElementStringAsync (string prefix, string localName, string ns, string value)
840 return Task.Run (() => {
842 WriteElementString (prefix, localName, ns, value);
844 asyncRunning = false;
849 protected internal virtual Task WriteEndAttributeAsync ()
852 return Task.Run (() => {
854 WriteEndAttribute ();
856 asyncRunning = false;
861 public virtual Task WriteEndDocumentAsync ()
864 return Task.Run (() => {
868 asyncRunning = false;
873 public virtual Task WriteEndElementAsync ()
876 return Task.Run (() => {
880 asyncRunning = false;
885 public virtual Task WriteEntityRefAsync (string name)
888 return Task.Run (() => {
890 WriteEntityRef (name);
892 asyncRunning = false;
897 public virtual Task WriteFullEndElementAsync ()
900 return Task.Run (() => {
902 WriteFullEndElement ();
904 asyncRunning = false;
909 public virtual Task WriteNameAsync (string name)
912 return Task.Run (() => {
916 asyncRunning = false;
921 public virtual Task WriteNmTokenAsync (string name)
924 return Task.Run (() => {
928 asyncRunning = false;
933 public virtual Task WriteNodeAsync (XmlReader reader, bool defattr)
936 return Task.Run (() => {
938 WriteNode (reader, defattr);
940 asyncRunning = false;
945 public virtual Task WriteNodeAsync (XPathNavigator navigator, bool defattr)
948 return Task.Run (() => {
950 WriteNode (navigator, defattr);
952 asyncRunning = false;
957 public virtual Task WriteProcessingInstructionAsync (string name, string text)
960 return Task.Run (() => {
962 WriteProcessingInstruction (name, text);
964 asyncRunning = false;
969 public virtual Task WriteQualifiedNameAsync (string localName, string ns)
972 return Task.Run (() => {
974 WriteQualifiedName (localName, ns);
976 asyncRunning = false;
981 public virtual Task WriteRawAsync (string data)
984 return Task.Run (() => {
988 asyncRunning = false;
994 public virtual Task WriteRawAsync (char[] buffer, int index, int count)
997 return Task.Run (() => {
999 WriteRaw (buffer, index, count);
1001 asyncRunning = false;
1006 protected internal virtual Task WriteStartAttributeAsync (
1007 string prefix, string localName, string ns)
1010 return Task.Run (() => {
1012 WriteStartAttribute (prefix, localName, ns);
1014 asyncRunning = false;
1019 public virtual Task WriteStartDocumentAsync ()
1022 return Task.Run (() => {
1024 WriteStartDocument ();
1026 asyncRunning = false;
1031 public virtual Task WriteStartDocumentAsync (bool standalone)
1034 return Task.Run (() => {
1036 WriteStartDocument (standalone);
1038 asyncRunning = false;
1043 public virtual Task WriteStartElementAsync (string prefix, string localName, string ns)
1046 return Task.Run (() => {
1048 WriteStartElement (prefix, localName, ns);
1050 asyncRunning = false;
1055 public virtual Task WriteStringAsync (string text)
1058 return Task.Run (() => {
1062 asyncRunning = false;
1067 public virtual Task WriteSurrogateCharEntityAsync (char lowChar, char highChar)
1070 return Task.Run (() => {
1072 WriteSurrogateCharEntity (lowChar, highChar);
1074 asyncRunning = false;
1079 public virtual Task WriteWhitespaceAsync (string ws)
1082 return Task.Run (() => {
1084 WriteWhitespace (ws);
1086 asyncRunning = false;