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 {
62 settings = new XmlWriterSettings ();
68 public abstract WriteState WriteState { get; }
72 public virtual string XmlLang {
76 public virtual XmlSpace XmlSpace {
77 get { return XmlSpace.None; }
80 public abstract string XmlLang { get; }
82 public abstract XmlSpace XmlSpace { get; }
89 public abstract void Close ();
92 public static XmlWriter Create (Stream stream)
94 return Create (stream, null);
97 public static XmlWriter Create (string file)
99 return Create (file, null);
102 public static XmlWriter Create (TextWriter writer)
104 return Create (writer, null);
107 public static XmlWriter Create (XmlWriter writer)
109 return Create (writer, null);
112 public static XmlWriter Create (StringBuilder builder)
114 return Create (builder, null);
117 public static XmlWriter Create (Stream stream, XmlWriterSettings settings)
119 Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
120 return Create (new StreamWriter (stream, enc), settings);
123 public static XmlWriter Create (string file, XmlWriterSettings settings)
125 Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
126 return CreateTextWriter (new StreamWriter (file, false, enc), settings, true);
129 public static XmlWriter Create (StringBuilder builder, XmlWriterSettings settings)
131 return Create (new StringWriter (builder), settings);
134 public static XmlWriter Create (TextWriter writer, XmlWriterSettings settings)
136 if (settings == null)
137 settings = new XmlWriterSettings ();
138 return CreateTextWriter (writer, settings, settings.CloseOutput);
141 public static XmlWriter Create (XmlWriter writer, XmlWriterSettings settings)
143 if (settings == null)
144 settings = new XmlWriterSettings ();
145 writer.settings = settings;
149 private static XmlWriter CreateTextWriter (TextWriter writer, XmlWriterSettings settings, bool closeOutput)
151 if (settings == null)
152 settings = new XmlWriterSettings ();
153 XmlTextWriter xtw = new XmlTextWriter (writer, settings, closeOutput);
154 return Create (xtw, settings);
157 protected virtual void Dispose (bool disposing)
162 void IDisposable.Dispose ()
168 public abstract void Flush ();
170 public abstract string LookupPrefix (string ns);
172 private void WriteAttribute (XmlReader reader, bool defattr)
174 if (!defattr && reader.IsDefault)
177 WriteStartAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI);
179 // no ReadAttributeValue() in 2.1 profile.
180 WriteString (reader.Value);
182 while (reader.ReadAttributeValue ()) {
183 switch (reader.NodeType) {
184 case XmlNodeType.Text:
185 WriteString (reader.Value);
187 case XmlNodeType.EntityReference:
188 WriteEntityRef (reader.Name);
193 WriteEndAttribute ();
196 public virtual void WriteAttributes (XmlReader reader, bool defattr)
199 throw new ArgumentException("null XmlReader specified.", "reader");
201 switch (reader.NodeType) {
202 case XmlNodeType.XmlDeclaration:
203 WriteAttributeString ("version", reader ["version"]);
204 if (reader ["encoding"] != null)
205 WriteAttributeString ("encoding", reader ["encoding"]);
206 if (reader ["standalone"] != null)
207 WriteAttributeString ("standalone", reader ["standalone"]);
209 case XmlNodeType.Element:
210 if (reader.MoveToFirstAttribute ())
211 goto case XmlNodeType.Attribute;
213 case XmlNodeType.Attribute:
215 WriteAttribute (reader, defattr);
216 } while (reader.MoveToNextAttribute ());
217 reader.MoveToElement ();
220 throw new XmlException("NodeType is not one of Element, Attribute, nor XmlDeclaration.");
224 public void WriteAttributeString (string localName, string value)
226 WriteAttributeString ("", localName, null, value);
229 public void WriteAttributeString (string localName, string ns, string value)
231 WriteAttributeString ("", localName, ns, value);
234 public void WriteAttributeString (string prefix, string localName, string ns, string value)
236 // In MS.NET (1.0), this check is done *here*, not at WriteStartAttribute.
237 // (XmlTextWriter.WriteStartAttribute("xmlns", "anyname", null) throws an exception.
239 WriteStartAttribute (prefix, localName, ns);
240 if (value != null && value.Length > 0)
242 WriteEndAttribute ();
245 public abstract void WriteBase64 (byte[] buffer, int index, int count);
248 public virtual void WriteBinHex (byte [] buffer, int index, int count)
250 StringWriter sw = new StringWriter ();
251 XmlConvert.WriteBinHex (buffer, index, count, sw);
252 WriteString (sw.ToString ());
255 public abstract void WriteBinHex (byte[] buffer, int index, int count);
258 public abstract void WriteCData (string text);
260 public abstract void WriteCharEntity (char ch);
262 public abstract void WriteChars (char[] buffer, int index, int count);
264 public abstract void WriteComment (string text);
266 public abstract void WriteDocType (string name, string pubid, string sysid, string subset);
268 public void WriteElementString (string localName, string value)
270 WriteStartElement(localName);
271 if (value != null && value.Length > 0)
276 public void WriteElementString (string localName, string ns, string value)
278 WriteStartElement(localName, ns);
279 if (value != null && value.Length > 0)
285 public void WriteElementString (string prefix, string localName, string ns, string value)
287 WriteStartElement(prefix, localName, ns);
288 if (value != null && value.Length > 0)
294 public abstract void WriteEndAttribute ();
296 public abstract void WriteEndDocument ();
298 public abstract void WriteEndElement ();
300 public abstract void WriteEntityRef (string name);
302 public abstract void WriteFullEndElement ();
305 public virtual void WriteName (string name)
307 WriteNameInternal (name);
310 public virtual void WriteNmToken (string name)
312 WriteNmTokenInternal (name);
315 public virtual void WriteQualifiedName (string localName, string ns)
317 WriteQualifiedNameInternal (localName, ns);
320 public abstract void WriteName (string name);
322 public abstract void WriteNmToken (string name);
324 public abstract void WriteQualifiedName (string localName, string ns);
327 internal void WriteNameInternal (string name)
330 switch (Settings.ConformanceLevel) {
331 case ConformanceLevel.Document:
332 case ConformanceLevel.Fragment:
333 XmlConvert.VerifyName (name);
337 XmlConvert.VerifyName (name);
342 internal virtual void WriteNmTokenInternal (string name)
346 switch (Settings.ConformanceLevel) {
347 case ConformanceLevel.Document:
348 case ConformanceLevel.Fragment:
349 valid = XmlChar.IsNmToken (name);
353 valid = XmlChar.IsNmToken (name);
356 throw new ArgumentException ("Argument name is not a valid NMTOKEN.");
360 internal void WriteQualifiedNameInternal (string localName, string ns)
362 if (localName == null || localName == String.Empty)
363 throw new ArgumentException ();
368 switch (Settings.ConformanceLevel) {
369 case ConformanceLevel.Document:
370 case ConformanceLevel.Fragment:
371 XmlConvert.VerifyNCName (localName);
375 XmlConvert.VerifyNCName (localName);
378 string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
380 throw new ArgumentException (String.Format ("Namespace '{0}' is not declared.", ns));
382 if (prefix != String.Empty) {
383 WriteString (prefix);
385 WriteString (localName);
388 WriteString (localName);
392 public virtual void WriteNode (XPathNavigator navigator, bool defattr)
394 if (navigator == null)
395 throw new ArgumentNullException ("navigator");
396 switch (navigator.NodeType) {
397 case XPathNodeType.Attribute:
400 case XPathNodeType.Namespace:
403 case XPathNodeType.Text:
404 WriteString (navigator.Value);
406 case XPathNodeType.SignificantWhitespace:
407 WriteWhitespace (navigator.Value);
409 case XPathNodeType.Whitespace:
410 WriteWhitespace (navigator.Value);
412 case XPathNodeType.Comment:
413 WriteComment (navigator.Value);
415 case XPathNodeType.ProcessingInstruction:
416 WriteProcessingInstruction (navigator.Name, navigator.Value);
418 case XPathNodeType.Root:
419 if (navigator.MoveToFirstChild ()) {
421 WriteNode (navigator, defattr);
422 } while (navigator.MoveToNext ());
423 navigator.MoveToParent ();
426 case XPathNodeType.Element:
427 WriteStartElement (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
428 if (navigator.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
430 if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault)
431 WriteAttributeString (navigator.Prefix,
432 navigator.LocalName == String.Empty ? "xmlns" : navigator.LocalName,
433 "http://www.w3.org/2000/xmlns/",
435 } while (navigator.MoveToNextNamespace (XPathNamespaceScope.Local));
436 navigator.MoveToParent ();
438 if (navigator.MoveToFirstAttribute ()) {
440 if (defattr || navigator.SchemaInfo == null || navigator.SchemaInfo.IsDefault)
441 WriteAttributeString (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI, navigator.Value);
443 } while (navigator.MoveToNextAttribute ());
444 navigator.MoveToParent ();
446 if (navigator.MoveToFirstChild ()) {
448 WriteNode (navigator, defattr);
449 } while (navigator.MoveToNext ());
450 navigator.MoveToParent ();
452 if (navigator.IsEmptyElement)
455 WriteFullEndElement ();
458 throw new NotSupportedException ();
463 public virtual void WriteNode (XmlReader reader, bool defattr)
466 throw new ArgumentException ();
468 if (reader.ReadState == ReadState.Initial) {
471 WriteNode (reader, defattr);
472 } while (!reader.EOF);
476 switch (reader.NodeType) {
477 case XmlNodeType.Element:
478 WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
480 WriteAttributes (reader, defattr);
481 reader.MoveToElement ();
483 // Well, I found that MS.NET took this way, since
484 // there was a error-prone SgmlReader that fails
485 // MoveToNextAttribute().
486 if (reader.HasAttributes) {
487 for (int i = 0; i < reader.AttributeCount; i++) {
488 reader.MoveToAttribute (i);
489 WriteAttribute (reader, defattr);
491 reader.MoveToElement ();
494 if (reader.IsEmptyElement)
497 int depth = reader.Depth;
499 if (reader.NodeType != XmlNodeType.EndElement) {
501 WriteNode (reader, defattr);
502 } while (depth < reader.Depth);
504 WriteFullEndElement ();
507 // In case of XmlAttribute, don't proceed reader, and it will never be written.
508 case XmlNodeType.Attribute:
510 case XmlNodeType.Text:
511 WriteString (reader.Value);
513 case XmlNodeType.CDATA:
514 WriteCData (reader.Value);
516 case XmlNodeType.EntityReference:
517 WriteEntityRef (reader.Name);
519 case XmlNodeType.XmlDeclaration:
520 // LAMESPEC: It means that XmlWriter implementation _must not_ check
521 // whether PI name is "xml" (it is XML error) or not.
522 case XmlNodeType.ProcessingInstruction:
523 WriteProcessingInstruction (reader.Name, reader.Value);
525 case XmlNodeType.Comment:
526 WriteComment (reader.Value);
528 case XmlNodeType.DocumentType:
529 WriteDocType (reader.Name,
530 reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
532 case XmlNodeType.SignificantWhitespace:
533 goto case XmlNodeType.Whitespace;
534 case XmlNodeType.Whitespace:
535 WriteWhitespace (reader.Value);
537 case XmlNodeType.EndElement:
538 WriteFullEndElement ();
540 case XmlNodeType.EndEntity:
542 case XmlNodeType.None:
543 break; // Do nothing, nor reporting errors.
545 throw new XmlException ("Unexpected node " + reader.Name + " of type " + reader.NodeType);
550 public abstract void WriteProcessingInstruction (string name, string text);
552 public abstract void WriteRaw (string data);
554 public abstract void WriteRaw (char[] buffer, int index, int count);
557 public void WriteStartAttribute (string localName)
559 WriteStartAttribute (null, localName, null);
563 public void WriteStartAttribute (string localName, string ns)
565 WriteStartAttribute (null, localName, ns);
568 public abstract void WriteStartAttribute (string prefix, string localName, string ns);
570 public abstract void WriteStartDocument ();
572 public abstract void WriteStartDocument (bool standalone);
574 public void WriteStartElement (string localName)
576 WriteStartElement (null, localName, null);
579 public void WriteStartElement (string localName, string ns)
581 WriteStartElement (null, localName, ns);
584 public abstract void WriteStartElement (string prefix, string localName, string ns);
586 public abstract void WriteString (string text);
588 public abstract void WriteSurrogateCharEntity (char lowChar, char highChar);
590 public abstract void WriteWhitespace (string ws);
593 public virtual void WriteValue (bool value)
595 WriteString (XQueryConvert.BooleanToString (value));
598 public virtual void WriteValue (DateTime value)
600 WriteString (XmlConvert.ToString (value));
603 public virtual void WriteValue (decimal value)
605 WriteString (XQueryConvert.DecimalToString (value));
608 public virtual void WriteValue (double value)
610 WriteString (XQueryConvert.DoubleToString (value));
613 public virtual void WriteValue (int value)
615 WriteString (XQueryConvert.IntToString (value));
618 public virtual void WriteValue (long value)
620 WriteString (XQueryConvert.IntegerToString (value));
623 public virtual void WriteValue (object value)
626 throw new ArgumentNullException ("value");
629 WriteString ((string) value);
630 else if (value is bool)
631 WriteValue ((bool) value);
632 else if (value is byte)
633 WriteValue ((int) value);
634 else if (value is byte [])
635 WriteBase64 ((byte []) value, 0, ((byte []) value).Length);
636 else if (value is char [])
637 WriteChars ((char []) value, 0, ((char []) value).Length);
638 else if (value is DateTime)
639 WriteValue ((DateTime) value);
640 else if (value is decimal)
641 WriteValue ((decimal) value);
642 else if (value is double)
643 WriteValue ((double) value);
644 else if (value is short)
645 WriteValue ((int) value);
646 else if (value is int)
647 WriteValue ((int) value);
648 else if (value is long)
649 WriteValue ((long) value);
650 else if (value is float)
651 WriteValue ((float) value);
652 else if (value is TimeSpan) // undocumented
653 WriteString (XmlConvert.ToString ((TimeSpan) value));
654 else if (value is XmlQualifiedName) {
655 XmlQualifiedName qname = (XmlQualifiedName) value;
656 if (!qname.Equals (XmlQualifiedName.Empty)) {
657 if (qname.Namespace.Length > 0 && LookupPrefix (qname.Namespace) == null)
658 throw new InvalidCastException (String.Format ("The QName '{0}' cannot be written. No corresponding prefix is declared", qname));
659 WriteQualifiedName (qname.Name, qname.Namespace);
662 WriteString (String.Empty);
664 else if (value is IEnumerable) {
666 foreach (object obj in (IEnumerable) value) {
675 throw new InvalidCastException (String.Format ("Type '{0}' cannot be cast to string", value.GetType ()));
678 public virtual void WriteValue (float value)
680 WriteString (XQueryConvert.FloatToString (value));
683 public virtual void WriteValue (string value)