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 #if NET_2_0 && !NET_2_1
39 using System.Xml.XPath;
45 public abstract class XmlWriter : IDisposable
47 public abstract class XmlWriter
51 XmlWriterSettings settings;
56 protected XmlWriter () { }
63 public virtual XmlWriterSettings Settings {
66 settings = new XmlWriterSettings ();
72 public abstract WriteState WriteState { get; }
76 public virtual string XmlLang {
80 public virtual XmlSpace XmlSpace {
81 get { return XmlSpace.None; }
84 public abstract string XmlLang { get; }
86 public abstract XmlSpace XmlSpace { get; }
93 public abstract void Close ();
96 public static XmlWriter Create (Stream stream)
98 return Create (stream, null);
101 public static XmlWriter Create (string file)
103 return Create (file, null);
106 public static XmlWriter Create (TextWriter writer)
108 return Create (writer, null);
111 public static XmlWriter Create (XmlWriter writer)
113 return Create (writer, null);
116 public static XmlWriter Create (StringBuilder builder)
118 return Create (builder, null);
121 public static XmlWriter Create (Stream stream, XmlWriterSettings settings)
123 Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
124 return Create (new StreamWriter (stream, enc), settings);
127 public static XmlWriter Create (string file, XmlWriterSettings settings)
129 Encoding enc = settings != null ? settings.Encoding : Encoding.UTF8;
130 return CreateTextWriter (new StreamWriter (file, false, enc), settings, true);
133 public static XmlWriter Create (StringBuilder builder, XmlWriterSettings settings)
135 return Create (new StringWriter (builder), settings);
138 public static XmlWriter Create (TextWriter writer, XmlWriterSettings settings)
140 if (settings == null)
141 settings = new XmlWriterSettings ();
142 return CreateTextWriter (writer, settings, settings.CloseOutput);
145 public static XmlWriter Create (XmlWriter writer, XmlWriterSettings settings)
147 if (settings == null)
148 settings = new XmlWriterSettings ();
149 writer.settings = settings;
153 private static XmlWriter CreateTextWriter (TextWriter writer, XmlWriterSettings settings, bool closeOutput)
155 if (settings == null)
156 settings = new XmlWriterSettings ();
157 XmlTextWriter xtw = new XmlTextWriter (writer, settings, closeOutput);
158 return Create (xtw, settings);
161 protected virtual void Dispose (bool disposing)
166 void IDisposable.Dispose ()
172 public abstract void Flush ();
174 public abstract string LookupPrefix (string ns);
176 private void WriteAttribute (XmlReader reader, bool defattr)
178 if (!defattr && reader.IsDefault)
181 WriteStartAttribute (reader.Prefix, reader.LocalName, reader.NamespaceURI);
183 // no ReadAttributeValue() in 2.1 profile.
184 WriteString (reader.Value);
186 while (reader.ReadAttributeValue ()) {
187 switch (reader.NodeType) {
188 case XmlNodeType.Text:
189 WriteString (reader.Value);
191 case XmlNodeType.EntityReference:
192 WriteEntityRef (reader.Name);
197 WriteEndAttribute ();
200 public virtual void WriteAttributes (XmlReader reader, bool defattr)
203 throw new ArgumentException("null XmlReader specified.", "reader");
205 switch (reader.NodeType) {
206 case XmlNodeType.XmlDeclaration:
207 WriteAttributeString ("version", reader ["version"]);
208 if (reader ["encoding"] != null)
209 WriteAttributeString ("encoding", reader ["encoding"]);
210 if (reader ["standalone"] != null)
211 WriteAttributeString ("standalone", reader ["standalone"]);
213 case XmlNodeType.Element:
214 if (reader.MoveToFirstAttribute ())
215 goto case XmlNodeType.Attribute;
217 case XmlNodeType.Attribute:
219 WriteAttribute (reader, defattr);
220 } while (reader.MoveToNextAttribute ());
221 reader.MoveToElement ();
224 throw new XmlException("NodeType is not one of Element, Attribute, nor XmlDeclaration.");
228 public void WriteAttributeString (string localName, string value)
230 WriteAttributeString ("", localName, null, value);
233 public void WriteAttributeString (string localName, string ns, string value)
235 WriteAttributeString ("", localName, ns, value);
238 public void WriteAttributeString (string prefix, string localName, string ns, string value)
240 // In MS.NET (1.0), this check is done *here*, not at WriteStartAttribute.
241 // (XmlTextWriter.WriteStartAttribute("xmlns", "anyname", null) throws an exception.
243 WriteStartAttribute (prefix, localName, ns);
244 if (value != null && value.Length > 0)
246 WriteEndAttribute ();
249 public abstract void WriteBase64 (byte[] buffer, int index, int count);
252 public virtual void WriteBinHex (byte [] buffer, int index, int count)
254 StringWriter sw = new StringWriter ();
255 XmlConvert.WriteBinHex (buffer, index, count, sw);
256 WriteString (sw.ToString ());
259 public abstract void WriteBinHex (byte[] buffer, int index, int count);
262 public abstract void WriteCData (string text);
264 public abstract void WriteCharEntity (char ch);
266 public abstract void WriteChars (char[] buffer, int index, int count);
268 public abstract void WriteComment (string text);
270 public abstract void WriteDocType (string name, string pubid, string sysid, string subset);
272 public void WriteElementString (string localName, string value)
274 WriteStartElement(localName);
275 if (value != null && value.Length > 0)
280 public void WriteElementString (string localName, string ns, string value)
282 WriteStartElement(localName, ns);
283 if (value != null && value.Length > 0)
289 public void WriteElementString (string prefix, string localName, string ns, string value)
291 WriteStartElement(prefix, localName, ns);
292 if (value != null && value.Length > 0)
298 public abstract void WriteEndAttribute ();
300 public abstract void WriteEndDocument ();
302 public abstract void WriteEndElement ();
304 public abstract void WriteEntityRef (string name);
306 public abstract void WriteFullEndElement ();
309 public virtual void WriteName (string name)
311 WriteNameInternal (name);
314 public virtual void WriteNmToken (string name)
316 WriteNmTokenInternal (name);
319 public virtual void WriteQualifiedName (string localName, string ns)
321 WriteQualifiedNameInternal (localName, ns);
324 public abstract void WriteName (string name);
326 public abstract void WriteNmToken (string name);
328 public abstract void WriteQualifiedName (string localName, string ns);
331 internal void WriteNameInternal (string name)
334 switch (Settings.ConformanceLevel) {
335 case ConformanceLevel.Document:
336 case ConformanceLevel.Fragment:
337 XmlConvert.VerifyName (name);
341 XmlConvert.VerifyName (name);
346 internal virtual void WriteNmTokenInternal (string name)
350 switch (Settings.ConformanceLevel) {
351 case ConformanceLevel.Document:
352 case ConformanceLevel.Fragment:
353 valid = XmlChar.IsNmToken (name);
357 valid = XmlChar.IsNmToken (name);
360 throw new ArgumentException ("Argument name is not a valid NMTOKEN.");
364 internal void WriteQualifiedNameInternal (string localName, string ns)
366 if (localName == null || localName == String.Empty)
367 throw new ArgumentException ();
372 switch (Settings.ConformanceLevel) {
373 case ConformanceLevel.Document:
374 case ConformanceLevel.Fragment:
375 XmlConvert.VerifyNCName (localName);
379 XmlConvert.VerifyNCName (localName);
382 string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
384 throw new ArgumentException (String.Format ("Namespace '{0}' is not declared.", ns));
386 if (prefix != String.Empty) {
387 WriteString (prefix);
389 WriteString (localName);
392 WriteString (localName);
395 #if NET_2_0 && !NET_2_1
396 [MonoTODO] // FIXME: test defattr handling
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:
403 if (defattr || navigator.SchemaInfo == null ||
404 !navigator.SchemaInfo.IsDefault)
405 WriteAttributeString (navigator.Prefix,
407 navigator.NamespaceURI,
410 case XPathNodeType.Namespace:
411 if (defattr || navigator.SchemaInfo == null ||
412 !navigator.SchemaInfo.IsDefault)
413 WriteAttributeString (navigator.Prefix,
414 navigator.LocalName == String.Empty ? "xmlns" : navigator.LocalName,
415 "http://www.w3.org/2000/xmlns/",
418 case XPathNodeType.Text:
419 WriteString (navigator.Value);
421 case XPathNodeType.SignificantWhitespace:
422 WriteWhitespace (navigator.Value);
424 case XPathNodeType.Whitespace:
425 WriteWhitespace (navigator.Value);
427 case XPathNodeType.Comment:
428 WriteComment (navigator.Value);
430 case XPathNodeType.ProcessingInstruction:
431 WriteProcessingInstruction (navigator.Name, navigator.Value);
433 case XPathNodeType.Root:
434 if (navigator.MoveToFirstChild ()) {
436 WriteNode (navigator, defattr);
437 } while (navigator.MoveToNext ());
438 navigator.MoveToParent ();
441 case XPathNodeType.Element:
442 WriteStartElement (navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
443 if (navigator.MoveToFirstNamespace (XPathNamespaceScope.Local)) {
445 WriteNode (navigator, defattr);
446 } while (navigator.MoveToNextNamespace (XPathNamespaceScope.Local));
447 navigator.MoveToParent ();
449 if (navigator.MoveToFirstAttribute ()) {
451 WriteNode (navigator, defattr);
452 } while (navigator.MoveToNextAttribute ());
453 navigator.MoveToParent ();
455 if (navigator.MoveToFirstChild ()) {
457 WriteNode (navigator, defattr);
458 } while (navigator.MoveToNext ());
459 navigator.MoveToParent ();
461 if (navigator.IsEmptyElement)
464 WriteFullEndElement ();
467 throw new NotSupportedException ();
472 public virtual void WriteNode (XmlReader reader, bool defattr)
475 throw new ArgumentException ();
477 if (reader.ReadState == ReadState.Initial) {
480 WriteNode (reader, defattr);
481 } while (!reader.EOF);
485 switch (reader.NodeType) {
486 case XmlNodeType.Element:
487 WriteStartElement (reader.Prefix, reader.LocalName, reader.NamespaceURI);
489 WriteAttributes (reader, defattr);
490 reader.MoveToElement ();
492 // Well, I found that MS.NET took this way, since
493 // there was a error-prone SgmlReader that fails
494 // MoveToNextAttribute().
495 if (reader.HasAttributes) {
496 for (int i = 0; i < reader.AttributeCount; i++) {
497 reader.MoveToAttribute (i);
498 WriteAttribute (reader, defattr);
500 reader.MoveToElement ();
503 if (reader.IsEmptyElement)
506 int depth = reader.Depth;
508 if (reader.NodeType != XmlNodeType.EndElement) {
510 WriteNode (reader, defattr);
511 } while (depth < reader.Depth);
513 WriteFullEndElement ();
516 // In case of XmlAttribute, don't proceed reader, and it will never be written.
517 case XmlNodeType.Attribute:
519 case XmlNodeType.Text:
520 WriteString (reader.Value);
522 case XmlNodeType.CDATA:
523 WriteCData (reader.Value);
525 case XmlNodeType.EntityReference:
526 WriteEntityRef (reader.Name);
528 case XmlNodeType.XmlDeclaration:
529 // LAMESPEC: It means that XmlWriter implementation _must not_ check
530 // whether PI name is "xml" (it is XML error) or not.
531 case XmlNodeType.ProcessingInstruction:
532 WriteProcessingInstruction (reader.Name, reader.Value);
534 case XmlNodeType.Comment:
535 WriteComment (reader.Value);
537 case XmlNodeType.DocumentType:
538 WriteDocType (reader.Name,
539 reader ["PUBLIC"], reader ["SYSTEM"], reader.Value);
541 case XmlNodeType.SignificantWhitespace:
542 goto case XmlNodeType.Whitespace;
543 case XmlNodeType.Whitespace:
544 WriteWhitespace (reader.Value);
546 case XmlNodeType.EndElement:
547 WriteFullEndElement ();
549 case XmlNodeType.EndEntity:
551 case XmlNodeType.None:
552 break; // Do nothing, nor reporting errors.
554 throw new XmlException ("Unexpected node " + reader.Name + " of type " + reader.NodeType);
559 public abstract void WriteProcessingInstruction (string name, string text);
561 public abstract void WriteRaw (string data);
563 public abstract void WriteRaw (char[] buffer, int index, int count);
566 public void WriteStartAttribute (string localName)
568 WriteStartAttribute (null, localName, null);
572 public void WriteStartAttribute (string localName, string ns)
574 WriteStartAttribute (null, localName, ns);
577 public abstract void WriteStartAttribute (string prefix, string localName, string ns);
579 public abstract void WriteStartDocument ();
581 public abstract void WriteStartDocument (bool standalone);
583 public void WriteStartElement (string localName)
585 WriteStartElement (null, localName, null);
588 public void WriteStartElement (string localName, string ns)
590 WriteStartElement (null, localName, ns);
593 public abstract void WriteStartElement (string prefix, string localName, string ns);
595 public abstract void WriteString (string text);
597 public abstract void WriteSurrogateCharEntity (char lowChar, char highChar);
599 public abstract void WriteWhitespace (string ws);
602 public virtual void WriteValue (bool value)
604 WriteString (XQueryConvert.BooleanToString (value));
607 public virtual void WriteValue (DateTime value)
609 WriteString (XmlConvert.ToString (value));
612 public virtual void WriteValue (decimal value)
614 WriteString (XQueryConvert.DecimalToString (value));
617 public virtual void WriteValue (double value)
619 WriteString (XQueryConvert.DoubleToString (value));
622 public virtual void WriteValue (int value)
624 WriteString (XQueryConvert.IntToString (value));
627 public virtual void WriteValue (long value)
629 WriteString (XQueryConvert.IntegerToString (value));
632 public virtual void WriteValue (object value)
635 throw new ArgumentNullException ("value");
638 WriteString ((string) value);
639 else if (value is bool)
640 WriteValue ((bool) value);
641 else if (value is byte)
642 WriteValue ((int) value);
643 else if (value is byte [])
644 WriteBase64 ((byte []) value, 0, ((byte []) value).Length);
645 else if (value is char [])
646 WriteChars ((char []) value, 0, ((char []) value).Length);
647 else if (value is DateTime)
648 WriteValue ((DateTime) value);
649 else if (value is decimal)
650 WriteValue ((decimal) value);
651 else if (value is double)
652 WriteValue ((double) value);
653 else if (value is short)
654 WriteValue ((int) value);
655 else if (value is int)
656 WriteValue ((int) value);
657 else if (value is long)
658 WriteValue ((long) value);
659 else if (value is float)
660 WriteValue ((float) value);
661 else if (value is TimeSpan) // undocumented
662 WriteString (XmlConvert.ToString ((TimeSpan) value));
663 else if (value is XmlQualifiedName) {
664 XmlQualifiedName qname = (XmlQualifiedName) value;
665 if (!qname.Equals (XmlQualifiedName.Empty)) {
666 if (qname.Namespace.Length > 0 && LookupPrefix (qname.Namespace) == null)
667 throw new InvalidCastException (String.Format ("The QName '{0}' cannot be written. No corresponding prefix is declared", qname));
668 WriteQualifiedName (qname.Name, qname.Namespace);
671 WriteString (String.Empty);
673 else if (value is IEnumerable) {
675 foreach (object obj in (IEnumerable) value) {
684 throw new InvalidCastException (String.Format ("Type '{0}' cannot be cast to string", value.GetType ()));
687 public virtual void WriteValue (float value)
689 WriteString (XQueryConvert.FloatToString (value));
692 public virtual void WriteValue (string value)