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