1 
2 //------------------------------------------------------------------------------
3 // <copyright file="XmlWriter.cs" company="Microsoft">
4 //     Copyright (c) Microsoft Corporation.  All rights reserved.
5 // </copyright>
6 // <owner current="true" primary="true">Microsoft</owner>
7 //------------------------------------------------------------------------------
8 
9 using System;
10 using System.IO;
11 using System.Text;
12 #if !SILVERLIGHT
13 using System.Xml.XPath;
14 #endif
15 using System.Xml.Schema;
16 using System.Diagnostics;
17 using System.Collections.Generic;
18 using System.Globalization;
19 using System.Runtime.Versioning;
20 
21 namespace System.Xml {
22 
23     // Specifies the state of the XmlWriter.
24     public enum WriteState {
25         // Nothing has been written yet.
26         Start,
27 
28         // Writing the prolog.
29         Prolog,
30 
31         // Writing a the start tag for an element.
32         Element,
33 
34         // Writing an attribute value.
35         Attribute,
36 
37         // Writing element content.
38         Content,
39 
40         // XmlWriter is closed; Close has been called.
41         Closed,
42 
43         // Writer is in error state.
44         Error,
45     };
46 
47     // Represents a writer that provides fast non-cached forward-only way of generating XML streams containing XML documents
48     // that conform to the W3C Extensible Markup Language (XML) 1.0 specification and the Namespaces in XML specification.
49     public abstract partial class XmlWriter : IDisposable {
50 
51         // Helper buffer for WriteNode(XmlReader, bool)
52         char[] writeNodeBuffer;
53 
54         // Constants
55         const int WriteNodeBufferSize = 1024;
56 
57         // Returns the settings describing the features of the the writer. Returns null for V1 XmlWriters (XmlTextWriter).
58         public virtual XmlWriterSettings Settings {
59             get {
60                 return null;
61             }
62         }
63 
64         // Write methods
65         // Writes out the XML declaration with the version "1.0".
66 
WriteStartDocument()67         public abstract void WriteStartDocument();
68 
69         //Writes out the XML declaration with the version "1.0" and the speficied standalone attribute.
70 
WriteStartDocument(bool standalone)71         public abstract void WriteStartDocument(bool standalone);
72 
73         //Closes any open elements or attributes and puts the writer back in the Start state.
74 
WriteEndDocument()75         public abstract void WriteEndDocument();
76 
77         // Writes out the DOCTYPE declaration with the specified name and optional attributes.
78 
WriteDocType(string name, string pubid, string sysid, string subset)79         public abstract void WriteDocType(string name, string pubid, string sysid, string subset);
80 
81         // Writes out the specified start tag and associates it with the given namespace.
WriteStartElement(string localName, string ns)82         public void WriteStartElement(string localName, string ns) {
83             WriteStartElement(null, localName, ns);
84         }
85 
86         // Writes out the specified start tag and associates it with the given namespace and prefix.
87 
WriteStartElement(string prefix, string localName, string ns)88         public abstract void WriteStartElement(string prefix, string localName, string ns);
89 
90         // Writes out a start tag with the specified local name with no namespace.
WriteStartElement(string localName)91         public void WriteStartElement(string localName) {
92             WriteStartElement(null, localName, (string)null);
93         }
94 
95         // Closes one element and pops the corresponding namespace scope.
96 
WriteEndElement()97         public abstract void WriteEndElement();
98 
99         // Closes one element and pops the corresponding namespace scope. Writes out a full end element tag, e.g. </element>.
100 
WriteFullEndElement()101         public abstract void WriteFullEndElement();
102 
103         // Writes out the attribute with the specified LocalName, value, and NamespaceURI.
104 #if !SILVERLIGHT
105 #endif
WriteAttributeString(string localName, string ns, string value)106         public void WriteAttributeString(string localName, string ns, string value) {
107             WriteStartAttribute(null, localName, ns);
108             WriteString(value);
109             WriteEndAttribute();
110         }
111 
112         // Writes out the attribute with the specified LocalName and value.
WriteAttributeString(string localName, string value)113         public void WriteAttributeString(string localName, string value) {
114             WriteStartAttribute(null, localName, (string)null);
115             WriteString(value);
116             WriteEndAttribute();
117         }
118 
119         // Writes out the attribute with the specified prefix, LocalName, NamespaceURI and value.
WriteAttributeString(string prefix, string localName, string ns, string value)120         public void WriteAttributeString(string prefix, string localName, string ns, string value) {
121             WriteStartAttribute(prefix, localName, ns);
122             WriteString(value);
123             WriteEndAttribute();
124         }
125 
126         // Writes the start of an attribute.
WriteStartAttribute(string localName, string ns)127         public void WriteStartAttribute(string localName, string ns) {
128             WriteStartAttribute(null, localName, ns);
129         }
130 
131         // Writes the start of an attribute.
132 
WriteStartAttribute(string prefix, string localName, string ns)133         public abstract void WriteStartAttribute(string prefix, string localName, string ns);
134 
135         // Writes the start of an attribute.
WriteStartAttribute(string localName)136         public void WriteStartAttribute(string localName) {
137             WriteStartAttribute(null, localName, (string)null);
138         }
139 
140         // Closes the attribute opened by WriteStartAttribute call.
141 
WriteEndAttribute()142         public abstract void WriteEndAttribute();
143 
144         // Writes out a <![CDATA[...]]>; block containing the specified text.
145 
WriteCData(string text)146         public abstract void WriteCData(string text);
147 
148         // Writes out a comment <!--...-->; containing the specified text.
149 
WriteComment(string text)150         public abstract void WriteComment(string text);
151 
152         // Writes out a processing instruction with a space between the name and text as follows: <?name text?>
153 
WriteProcessingInstruction(string name, string text)154         public abstract void WriteProcessingInstruction(string name, string text);
155 
156         // Writes out an entity reference as follows: "&"+name+";".
157 
WriteEntityRef(string name)158         public abstract void WriteEntityRef(string name);
159 
160         // Forces the generation of a character entity for the specified Unicode character value.
161 
WriteCharEntity(char ch)162         public abstract void WriteCharEntity(char ch);
163 
164         // Writes out the given whitespace.
165 
WriteWhitespace(string ws)166         public abstract void WriteWhitespace(string ws);
167 
168         // Writes out the specified text content.
169 
WriteString(string text)170         public abstract void WriteString(string text);
171 
172         // Write out the given surrogate pair as an entity reference.
173 
WriteSurrogateCharEntity(char lowChar, char highChar)174         public abstract void WriteSurrogateCharEntity(char lowChar, char highChar);
175 
176         // Writes out the specified text content.
177 
WriteChars(char[] buffer, int index, int count)178         public abstract void WriteChars(char[] buffer, int index, int count);
179 
180         // Writes raw markup from the given character buffer.
181 
WriteRaw(char[] buffer, int index, int count)182         public abstract void WriteRaw(char[] buffer, int index, int count);
183 
184         // Writes raw markup from the given string.
185 
WriteRaw(string data)186         public abstract void WriteRaw(string data);
187 
188         // Encodes the specified binary bytes as base64 and writes out the resulting text.
189 
WriteBase64(byte[] buffer, int index, int count)190         public abstract void WriteBase64(byte[] buffer, int index, int count);
191 
192         // Encodes the specified binary bytes as binhex and writes out the resulting text.
WriteBinHex(byte[] buffer, int index, int count)193         public virtual void WriteBinHex(byte[] buffer, int index, int count) {
194             BinHexEncoder.Encode(buffer, index, count, this);
195         }
196 
197         // Returns the state of the XmlWriter.
198         public abstract WriteState WriteState { get; }
199 
200         // Closes the XmlWriter and the underlying stream/TextReader (if Settings.CloseOutput is true).
Close()201         public virtual void Close() { }
202 
203         // Flushes data that is in the internal buffers into the underlying streams/TextReader and flushes the stream/TextReader.
204 
Flush()205         public abstract void Flush();
206 
207         // Returns the closest prefix defined in the current namespace scope for the specified namespace URI.
LookupPrefix(string ns)208         public abstract string LookupPrefix(string ns);
209 
210         // Gets an XmlSpace representing the current xml:space scope.
211         public virtual XmlSpace XmlSpace {
212             get {
213                 return XmlSpace.Default;
214             }
215         }
216 
217         // Gets the current xml:lang scope.
218         public virtual string XmlLang {
219             get {
220                 return string.Empty;
221             }
222         }
223 
224         // Scalar Value Methods
225 
226         // Writes out the specified name, ensuring it is a valid NmToken according to the XML specification
227         // (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name).
WriteNmToken(string name)228         public virtual void WriteNmToken(string name) {
229             if (name == null || name.Length == 0) {
230                 throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
231             }
232             WriteString(XmlConvert.VerifyNMTOKEN(name, ExceptionType.ArgumentException));
233         }
234 
235         // Writes out the specified name, ensuring it is a valid Name according to the XML specification
236         // (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name).
WriteName(string name)237         public virtual void WriteName(string name) {
238             WriteString(XmlConvert.VerifyQName(name, ExceptionType.ArgumentException));
239         }
240 
241         // Writes out the specified namespace-qualified name by looking up the prefix that is in scope for the given namespace.
WriteQualifiedName(string localName, string ns)242         public virtual void WriteQualifiedName(string localName, string ns) {
243             if (ns != null && ns.Length > 0) {
244                 string prefix = LookupPrefix(ns);
245                 if (prefix == null) {
246                     throw new ArgumentException(Res.GetString(Res.Xml_UndefNamespace, ns));
247                 }
248                 WriteString(prefix);
249                 WriteString(":");
250             }
251             WriteString(localName);
252         }
253 
254         // Writes out the specified value.
WriteValue(object value)255         public virtual void WriteValue(object value) {
256             if (value == null) {
257                 throw new ArgumentNullException("value");
258             }
259 #if SILVERLIGHT
260             WriteString(XmlUntypedStringConverter.Instance.ToString(value, null));
261 #else
262             WriteString(XmlUntypedConverter.Untyped.ToString(value, null));
263 #endif
264         }
265 
266         // Writes out the specified value.
WriteValue(string value)267         public virtual void WriteValue(string value) {
268             if (value == null) {
269 
270                 return;
271 
272             }
273             WriteString(value);
274         }
275 
276         // Writes out the specified value.
WriteValue(bool value)277         public virtual void WriteValue(bool value) {
278             WriteString(XmlConvert.ToString(value));
279         }
280 
281         // Writes out the specified value.
WriteValue(DateTime value)282         public virtual void WriteValue(DateTime value) {
283             WriteString(XmlConvert.ToString(value, XmlDateTimeSerializationMode.RoundtripKind));
284         }
285 
286         // Writes out the specified value.
WriteValue(DateTimeOffset value)287         public virtual void WriteValue(DateTimeOffset value) {
288             // Under Win8P, WriteValue(DateTime) will invoke this overload, but custom writers
289             // might not have implemented it. This base implementation should call WriteValue(DateTime).
290             // The following conversion results in the same string as calling ToString with DateTimeOffset.
291             if (value.Offset != TimeSpan.Zero) {
292                 WriteValue(value.LocalDateTime);
293             }
294             else {
295                 WriteValue(value.UtcDateTime);
296             }
297         }
298 
299         // Writes out the specified value.
WriteValue(double value)300         public virtual void WriteValue(double value) {
301             WriteString(XmlConvert.ToString(value));
302         }
303 
304         // Writes out the specified value.
WriteValue(float value)305         public virtual void WriteValue(float value) {
306             WriteString(XmlConvert.ToString(value));
307         }
308 
309         // Writes out the specified value.
WriteValue(decimal value)310         public virtual void WriteValue(decimal value) {
311             WriteString(XmlConvert.ToString(value));
312         }
313 
314         // Writes out the specified value.
WriteValue(int value)315         public virtual void WriteValue(int value) {
316             WriteString(XmlConvert.ToString(value));
317         }
318 
319         // Writes out the specified value.
WriteValue(long value)320         public virtual void WriteValue(long value) {
321             WriteString(XmlConvert.ToString(value));
322         }
323 
324         // XmlReader Helper Methods
325 
326         // Writes out all the attributes found at the current position in the specified XmlReader.
WriteAttributes(XmlReader reader, bool defattr)327         public virtual void WriteAttributes(XmlReader reader, bool defattr) {
328             if (null == reader) {
329                 throw new ArgumentNullException("reader");
330             }
331 
332             if (reader.NodeType == XmlNodeType.Element || reader.NodeType == XmlNodeType.XmlDeclaration) {
333                 if (reader.MoveToFirstAttribute()) {
334                     WriteAttributes(reader, defattr);
335                     reader.MoveToElement();
336                 }
337             }
338             else if (reader.NodeType != XmlNodeType.Attribute) {
339                 throw new XmlException(Res.Xml_InvalidPosition, string.Empty);
340             }
341             else {
342                 do {
343                     // we need to check both XmlReader.IsDefault and XmlReader.SchemaInfo.IsDefault.
344                     // If either of these is true and defattr=false, we should not write the attribute out
345                     if (defattr || !reader.IsDefaultInternal) {
346                         WriteStartAttribute(reader.Prefix, reader.LocalName, reader.NamespaceURI);
347                         while (reader.ReadAttributeValue()) {
348                             if (reader.NodeType == XmlNodeType.EntityReference) {
349                                 WriteEntityRef(reader.Name);
350                             }
351                             else {
352                                 WriteString(reader.Value);
353                             }
354                         }
355                         WriteEndAttribute();
356                     }
357                 }
358                 while (reader.MoveToNextAttribute());
359             }
360         }
361 
362         // Copies the current node from the given reader to the writer (including child nodes), and if called on an element moves the XmlReader
363         // to the corresponding end element.
WriteNode(XmlReader reader, bool defattr)364         public virtual void WriteNode(XmlReader reader, bool defattr) {
365             if (null == reader) {
366                 throw new ArgumentNullException("reader");
367             }
368 
369             bool canReadChunk = reader.CanReadValueChunk;
370             int d = reader.NodeType == XmlNodeType.None ? -1 : reader.Depth;
371             do {
372                 switch (reader.NodeType) {
373                     case XmlNodeType.Element:
374                         WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
375                         WriteAttributes(reader, defattr);
376                         if (reader.IsEmptyElement) {
377                             WriteEndElement();
378                             break;
379                         }
380                         break;
381                     case XmlNodeType.Text:
382                         if (canReadChunk) {
383                             if (writeNodeBuffer == null) {
384                                 writeNodeBuffer = new char[WriteNodeBufferSize];
385                             }
386                             int read;
387                             while ((read = reader.ReadValueChunk(writeNodeBuffer, 0, WriteNodeBufferSize)) > 0) {
388                                 this.WriteChars(writeNodeBuffer, 0, read);
389                             }
390                         }
391                         else {
392 
393                             WriteString(reader.Value);
394 
395                         }
396                         break;
397                     case XmlNodeType.Whitespace:
398                     case XmlNodeType.SignificantWhitespace:
399 
400                         WriteWhitespace(reader.Value);
401 
402                         break;
403                     case XmlNodeType.CDATA:
404                         WriteCData(reader.Value);
405                         break;
406                     case XmlNodeType.EntityReference:
407                         WriteEntityRef(reader.Name);
408                         break;
409                     case XmlNodeType.XmlDeclaration:
410                     case XmlNodeType.ProcessingInstruction:
411                         WriteProcessingInstruction(reader.Name, reader.Value);
412                         break;
413                     case XmlNodeType.DocumentType:
414                         WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);
415                         break;
416 
417                     case XmlNodeType.Comment:
418                         WriteComment(reader.Value);
419                         break;
420                     case XmlNodeType.EndElement:
421                         WriteFullEndElement();
422                         break;
423                 }
424             } while (reader.Read() && (d < reader.Depth || (d == reader.Depth && reader.NodeType == XmlNodeType.EndElement)));
425         }
426 
427 #if !SILVERLIGHT // Removing dependency on XPathNavigator
428         // Copies the current node from the given XPathNavigator to the writer (including child nodes).
WriteNode(XPathNavigator navigator, bool defattr)429         public virtual void WriteNode(XPathNavigator navigator, bool defattr) {
430             if (navigator == null) {
431                 throw new ArgumentNullException("navigator");
432             }
433             int iLevel = 0;
434 
435             navigator = navigator.Clone();
436 
437             while (true) {
438                 bool mayHaveChildren = false;
439                 XPathNodeType nodeType = navigator.NodeType;
440 
441                 switch (nodeType) {
442                     case XPathNodeType.Element:
443                         WriteStartElement(navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
444 
445                         // Copy attributes
446                         if (navigator.MoveToFirstAttribute()) {
447                             do {
448                                 IXmlSchemaInfo schemaInfo = navigator.SchemaInfo;
449                                 if (defattr || (schemaInfo == null || !schemaInfo.IsDefault)) {
450                                     WriteStartAttribute(navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
451                                     // copy string value to writer
452                                     WriteString(navigator.Value);
453                                     WriteEndAttribute();
454                                 }
455                             } while (navigator.MoveToNextAttribute());
456                             navigator.MoveToParent();
457                         }
458 
459                         // Copy namespaces
460                         if (navigator.MoveToFirstNamespace(XPathNamespaceScope.Local)) {
461                             WriteLocalNamespaces(navigator);
462                             navigator.MoveToParent();
463                         }
464                         mayHaveChildren = true;
465                         break;
466                     case XPathNodeType.Attribute:
467                         // do nothing on root level attribute
468                         break;
469                     case XPathNodeType.Text:
470                         WriteString(navigator.Value);
471                         break;
472                     case XPathNodeType.SignificantWhitespace:
473                     case XPathNodeType.Whitespace:
474                         WriteWhitespace(navigator.Value);
475                         break;
476                     case XPathNodeType.Root:
477                         mayHaveChildren = true;
478                         break;
479                     case XPathNodeType.Comment:
480                         WriteComment(navigator.Value);
481                         break;
482                     case XPathNodeType.ProcessingInstruction:
483                         WriteProcessingInstruction(navigator.LocalName, navigator.Value);
484                         break;
485                     case XPathNodeType.Namespace:
486                         // do nothing on root level namespace
487                         break;
488                     default:
489                         Debug.Assert(false);
490                         break;
491                 }
492 
493                 if (mayHaveChildren) {
494                     // If children exist, move down to next level
495                     if (navigator.MoveToFirstChild()) {
496                         iLevel++;
497                         continue;
498                     }
499                     else {
500                         // EndElement
501                         if (navigator.NodeType == XPathNodeType.Element) {
502                             if (navigator.IsEmptyElement) {
503                                 WriteEndElement();
504                             }
505                             else {
506                                 WriteFullEndElement();
507                             }
508                         }
509                     }
510                 }
511 
512                 // No children
513                 while (true) {
514                     if (iLevel == 0) {
515                         // The entire subtree has been copied
516                         return;
517                     }
518 
519                     if (navigator.MoveToNext()) {
520                         // Found a sibling, so break to outer loop
521                         break;
522                     }
523 
524                     // No siblings, so move up to previous level
525                     iLevel--;
526                     navigator.MoveToParent();
527 
528                     // EndElement
529                     if (navigator.NodeType == XPathNodeType.Element)
530                         WriteFullEndElement();
531                 }
532             }
533         }
534 #endif
535 
536         // Element Helper Methods
537 
538         // Writes out an element with the specified name containing the specified string value.
WriteElementString(string localName, String value)539         public void WriteElementString(string localName, String value) {
540             WriteElementString(localName, null, value);
541         }
542 
543         // Writes out an attribute with the specified name, namespace URI and string value.
WriteElementString(string localName, String ns, String value)544         public void WriteElementString(string localName, String ns, String value) {
545             WriteStartElement(localName, ns);
546             if (null != value && 0 != value.Length) {
547                 WriteString(value);
548             }
549             WriteEndElement();
550         }
551 
552         // Writes out an attribute with the specified name, namespace URI, and string value.
WriteElementString(string prefix, String localName, String ns, String value)553         public void WriteElementString(string prefix, String localName, String ns, String value) {
554             WriteStartElement(prefix, localName, ns);
555             if (null != value && 0 != value.Length) {
556                 WriteString(value);
557             }
558             WriteEndElement();
559         }
560 
Dispose()561         public void Dispose() {
562             Dispose(true);
563         }
564 
565         // Dispose the underline stream objects (calls Close on the XmlWriter)
Dispose(bool disposing)566         protected virtual void Dispose(bool disposing) {
567             if (disposing && WriteState != WriteState.Closed) {
568                 Close();
569             }
570         }
571 
572 #if !SILVERLIGHT // Removing dependency on XPathNavigator
573         // Copy local namespaces on the navigator's current node to the raw writer. The namespaces are returned by the navigator in reversed order.
574         // The recursive call reverses them back.
WriteLocalNamespaces(XPathNavigator nsNav)575         private void WriteLocalNamespaces(XPathNavigator nsNav) {
576             string prefix = nsNav.LocalName;
577             string ns = nsNav.Value;
578 
579             if (nsNav.MoveToNextNamespace(XPathNamespaceScope.Local)) {
580                 WriteLocalNamespaces(nsNav);
581             }
582 
583             if (prefix.Length == 0) {
584                 WriteAttributeString(string.Empty, "xmlns", XmlReservedNs.NsXmlNs, ns);
585             }
586             else {
587                 WriteAttributeString("xmlns", prefix, XmlReservedNs.NsXmlNs, ns);
588             }
589         }
590 #endif
591 
592         //
593         // Static methods for creating writers
594         //
595 #if !SILVERLIGHT
596         // Creates an XmlWriter for writing into the provided file.
597         [ResourceConsumption(ResourceScope.Machine)]
598         [ResourceExposure(ResourceScope.Machine)]
Create(string outputFileName)599         public static XmlWriter Create(string outputFileName) {
600             return Create(outputFileName, null);
601         }
602 
603         // Creates an XmlWriter for writing into the provided file with the specified settings.
604         [ResourceConsumption(ResourceScope.Machine)]
605         [ResourceExposure(ResourceScope.Machine)]
Create(string outputFileName, XmlWriterSettings settings)606         public static XmlWriter Create(string outputFileName, XmlWriterSettings settings) {
607             if (settings == null) {
608                 settings = new XmlWriterSettings();
609             }
610             return settings.CreateWriter(outputFileName);
611         }
612 #endif
613 
614         // Creates an XmlWriter for writing into the provided stream.
Create(Stream output)615         public static XmlWriter Create(Stream output) {
616             return Create(output, null);
617         }
618 
619         // Creates an XmlWriter for writing into the provided stream with the specified settings.
Create(Stream output, XmlWriterSettings settings)620         public static XmlWriter Create(Stream output, XmlWriterSettings settings) {
621             if (settings == null) {
622                 settings = new XmlWriterSettings();
623             }
624             return settings.CreateWriter(output);
625         }
626 
627         // Creates an XmlWriter for writing into the provided TextWriter.
Create(TextWriter output)628         public static XmlWriter Create(TextWriter output) {
629             return Create(output, null);
630         }
631 
632         // Creates an XmlWriter for writing into the provided TextWriter with the specified settings.
Create(TextWriter output, XmlWriterSettings settings)633         public static XmlWriter Create(TextWriter output, XmlWriterSettings settings) {
634             if (settings == null) {
635                 settings = new XmlWriterSettings();
636             }
637             return settings.CreateWriter(output);
638         }
639 
640         // Creates an XmlWriter for writing into the provided StringBuilder.
Create(StringBuilder output)641         public static XmlWriter Create(StringBuilder output) {
642             return Create(output, null);
643         }
644 
645         // Creates an XmlWriter for writing into the provided StringBuilder with the specified settings.
Create(StringBuilder output, XmlWriterSettings settings)646         public static XmlWriter Create(StringBuilder output, XmlWriterSettings settings) {
647             if (settings == null) {
648                 settings = new XmlWriterSettings();
649             }
650             if (output == null) {
651                 throw new ArgumentNullException("output");
652             }
653             return settings.CreateWriter(new StringWriter(output, CultureInfo.InvariantCulture));
654         }
655 
656         // Creates an XmlWriter wrapped around the provided XmlWriter with the default settings.
Create(XmlWriter output)657         public static XmlWriter Create(XmlWriter output) {
658             return Create(output, null);
659         }
660 
661         // Creates an XmlWriter wrapped around the provided XmlWriter with the specified settings.
Create(XmlWriter output, XmlWriterSettings settings)662         public static XmlWriter Create(XmlWriter output, XmlWriterSettings settings) {
663             if (settings == null) {
664                 settings = new XmlWriterSettings();
665             }
666             return settings.CreateWriter(output);
667         }
668 
669     }
670 }
671 
672