5 // Copyright 2007 Novell (http://www.novell.com)
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System.Collections;
29 using System.Collections.Generic;
33 using System.Xml.Schema;
34 using System.Xml.Serialization;
36 namespace System.Xml.Linq
38 [XmlSchemaProvider (null, IsAny = true)]
39 public class XElement : XContainer, IXmlSerializable
41 static IEnumerable <XElement> emptySequence =
42 new List <XElement> ();
44 public static IEnumerable <XElement> EmptySequence {
45 get { return emptySequence; }
49 XAttribute attr_first, attr_last;
50 bool explicit_is_empty = true;
52 public XElement (XName name, object value)
58 public XElement (XElement source)
61 Add (source.Attributes ());
62 Add (source.Nodes ());
65 public XElement (XName name)
70 public XElement (XName name, params object [] contents)
76 public XElement (XStreamingElement source)
78 this.name = source.Name;
79 Add (source.Contents);
82 [CLSCompliant (false)]
83 public static explicit operator bool (XElement element)
86 throw new ArgumentNullException ("element");
87 return XUtil.ConvertToBoolean (element.Value);
90 [CLSCompliant (false)]
91 public static explicit operator bool? (XElement element)
96 return element.Value == null ? (bool?) null : XUtil.ConvertToBoolean (element.Value);
99 [CLSCompliant (false)]
100 public static explicit operator DateTime (XElement element)
103 throw new ArgumentNullException ("element");
104 return XUtil.ToDateTime (element.Value);
107 [CLSCompliant (false)]
108 public static explicit operator DateTime? (XElement element)
113 return element.Value == null ? (DateTime?) null : XUtil.ToDateTime (element.Value);
116 #if !TARGET_JVM // Same as for System.Xml.XmlConvert.ToDateTimeOffset
118 [CLSCompliant (false)]
119 public static explicit operator DateTimeOffset (XElement element)
122 throw new ArgumentNullException ("element");
123 return XmlConvert.ToDateTimeOffset (element.Value);
126 [CLSCompliant (false)]
127 public static explicit operator DateTimeOffset? (XElement element)
132 return element.Value == null ? (DateTimeOffset?) null : XmlConvert.ToDateTimeOffset (element.Value);
137 [CLSCompliant (false)]
138 public static explicit operator decimal (XElement element)
141 throw new ArgumentNullException ("element");
142 return XmlConvert.ToDecimal (element.Value);
145 [CLSCompliant (false)]
146 public static explicit operator decimal? (XElement element)
151 return element.Value == null ? (decimal?) null : XmlConvert.ToDecimal (element.Value);
154 [CLSCompliant (false)]
155 public static explicit operator double (XElement element)
158 throw new ArgumentNullException ("element");
159 return XmlConvert.ToDouble (element.Value);
162 [CLSCompliant (false)]
163 public static explicit operator double? (XElement element)
168 return element.Value == null ? (double?) null : XmlConvert.ToDouble (element.Value);
171 [CLSCompliant (false)]
172 public static explicit operator float (XElement element)
175 throw new ArgumentNullException ("element");
176 return XmlConvert.ToSingle (element.Value);
179 [CLSCompliant (false)]
180 public static explicit operator float? (XElement element)
185 return element.Value == null ? (float?) null : XmlConvert.ToSingle (element.Value);
188 [CLSCompliant (false)]
189 public static explicit operator Guid (XElement element)
192 throw new ArgumentNullException ("element");
193 return XmlConvert.ToGuid (element.Value);
196 [CLSCompliant (false)]
197 public static explicit operator Guid? (XElement element)
202 return element.Value == null ? (Guid?) null : XmlConvert.ToGuid (element.Value);
205 [CLSCompliant (false)]
206 public static explicit operator int (XElement element)
209 throw new ArgumentNullException ("element");
210 return XmlConvert.ToInt32 (element.Value);
213 [CLSCompliant (false)]
214 public static explicit operator int? (XElement element)
219 return element.Value == null ? (int?) null : XmlConvert.ToInt32 (element.Value);
222 [CLSCompliant (false)]
223 public static explicit operator long (XElement element)
226 throw new ArgumentNullException ("element");
227 return XmlConvert.ToInt64 (element.Value);
230 [CLSCompliant (false)]
231 public static explicit operator long? (XElement element)
236 return element.Value == null ? (long?) null : XmlConvert.ToInt64 (element.Value);
239 [CLSCompliant (false)]
240 public static explicit operator uint (XElement element)
243 throw new ArgumentNullException ("element");
244 return XmlConvert.ToUInt32 (element.Value);
247 [CLSCompliant (false)]
248 public static explicit operator uint? (XElement element)
253 return element.Value == null ? (uint?) null : XmlConvert.ToUInt32 (element.Value);
256 [CLSCompliant (false)]
257 public static explicit operator ulong (XElement element)
260 throw new ArgumentNullException ("element");
261 return XmlConvert.ToUInt64 (element.Value);
264 [CLSCompliant (false)]
265 public static explicit operator ulong? (XElement element)
270 return element.Value == null ? (ulong?) null : XmlConvert.ToUInt64 (element.Value);
273 [CLSCompliant (false)]
274 public static explicit operator TimeSpan (XElement element)
277 throw new ArgumentNullException ("element");
278 return XmlConvert.ToTimeSpan (element.Value);
281 [CLSCompliant (false)]
282 public static explicit operator TimeSpan? (XElement element)
287 return element.Value == null ? (TimeSpan?) null : XmlConvert.ToTimeSpan (element.Value);
290 [CLSCompliant (false)]
291 public static explicit operator string (XElement element)
296 return element.Value;
299 public XAttribute FirstAttribute {
300 get { return attr_first; }
301 internal set { attr_first = value; }
304 public XAttribute LastAttribute {
305 get { return attr_last; }
306 internal set { attr_last = value; }
309 public bool HasAttributes {
310 get { return attr_first != null; }
313 public bool HasElements {
315 foreach (object o in Nodes ())
322 public bool IsEmpty {
323 get { return !Nodes ().GetEnumerator ().MoveNext () && explicit_is_empty; }
324 internal set { explicit_is_empty = value; }
331 throw new ArgumentNullException ("value");
336 public override XmlNodeType NodeType {
337 get { return XmlNodeType.Element; }
340 public string Value {
342 StringBuilder sb = null;
343 foreach (XNode n in Nodes ()) {
345 sb = new StringBuilder ();
347 sb.Append (((XText) n).Value);
348 else if (n is XElement)
349 sb.Append (((XElement) n).Value);
351 return sb == null ? String.Empty : sb.ToString ();
359 IEnumerable <XElement> GetAncestorList (XName name, bool getMeIn)
361 List <XElement> list = new List <XElement> ();
364 for (XElement el = Parent as XElement; el != null; el = el.Parent as XElement)
365 if (name == null || el.Name == name)
370 public XAttribute Attribute (XName name)
372 foreach (XAttribute a in Attributes ())
378 public IEnumerable <XAttribute> Attributes ()
381 for (XAttribute a = attr_first; a != null; a = next) {
382 next = a.NextAttribute;
388 public IEnumerable <XAttribute> Attributes (XName name)
390 foreach (XAttribute a in Attributes ())
395 static void DefineDefaultSettings (XmlReaderSettings settings, LoadOptions options)
398 // 2.1 has a DtdProcessing property which defaults to DtdProcessing.Prohibit
399 settings.DtdProcessing = DtdProcessing.Parse;
401 settings.ProhibitDtd = false;
404 settings.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
407 static XmlReaderSettings CreateDefaultSettings (LoadOptions options)
409 var settings = new XmlReaderSettings ();
410 DefineDefaultSettings (settings, options);
414 public static XElement Load (string uri)
416 return Load (uri, LoadOptions.None);
419 public static XElement Load (string uri, LoadOptions options)
421 XmlReaderSettings s = CreateDefaultSettings (options);
423 using (XmlReader r = XmlReader.Create (uri, s)) {
424 return LoadCore (r, options);
428 public static XElement Load (TextReader tr)
430 return Load (tr, LoadOptions.None);
433 public static XElement Load (TextReader tr, LoadOptions options)
435 XmlReaderSettings s = CreateDefaultSettings (options);
437 using (XmlReader r = XmlReader.Create (tr, s)) {
438 return LoadCore (r, options);
442 public static XElement Load (XmlReader reader)
444 return Load (reader, LoadOptions.None);
447 public static XElement Load (XmlReader reader, LoadOptions options)
449 XmlReaderSettings s = reader.Settings != null ? reader.Settings.Clone () : new XmlReaderSettings ();
450 DefineDefaultSettings (s, options);
452 using (XmlReader r = XmlReader.Create (reader, s)) {
453 return LoadCore (r, options);
457 #if MOONLIGHT || MOBILE || NET_4_0
458 public static XElement Load (Stream stream)
460 return Load (stream, LoadOptions.None);
463 public static XElement Load (Stream stream, LoadOptions options)
465 XmlReaderSettings s = new XmlReaderSettings ();
466 DefineDefaultSettings (s, options);
468 using (XmlReader r = XmlReader.Create (stream, s)) {
469 return LoadCore (r, options);
474 internal static XElement LoadCore (XmlReader r, LoadOptions options)
477 if (r.NodeType != XmlNodeType.Element)
478 throw new InvalidOperationException ("The XmlReader must be positioned at an element");
479 XName name = XName.Get (r.LocalName, r.NamespaceURI);
480 XElement e = new XElement (name);
481 e.FillLineInfoAndBaseUri (r, options);
483 if (r.MoveToFirstAttribute ()) {
485 // not sure how current Orcas behavior makes sense here though ...
486 if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
487 e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
489 e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
490 e.LastAttribute.FillLineInfoAndBaseUri (r, options);
491 } while (r.MoveToNextAttribute ());
494 if (!r.IsEmptyElement) {
496 e.ReadContentFrom (r, options);
498 e.explicit_is_empty = false;
500 e.explicit_is_empty = true;
506 public static XElement Parse (string s)
508 return Parse (s, LoadOptions.None);
511 public static XElement Parse (string s, LoadOptions options)
513 return Load (new StringReader (s), options);
516 public void RemoveAll ()
522 public void RemoveAttributes ()
524 while (attr_first != null)
528 public void Save (string filename)
530 Save (filename, SaveOptions.None);
533 public void Save (string filename, SaveOptions options)
535 XmlWriterSettings s = new XmlWriterSettings ();
537 if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
539 #if NET_4_0 || MOONLIGHT || MOBILE
540 if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
541 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
543 using (XmlWriter w = XmlWriter.Create (filename, s)) {
548 public void Save (TextWriter tw)
550 Save (tw, SaveOptions.None);
553 public void Save (TextWriter tw, SaveOptions options)
555 XmlWriterSettings s = new XmlWriterSettings ();
557 if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
559 #if NET_4_0 || MOONLIGHT || MOBILE
560 if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
561 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
563 using (XmlWriter w = XmlWriter.Create (tw, s)) {
568 public void Save (XmlWriter w)
573 #if NET_4_0 || MOONLIGHT || MOBILE
574 public void Save (Stream stream)
576 Save (stream, SaveOptions.None);
579 public void Save (Stream stream, SaveOptions options)
581 XmlWriterSettings s = new XmlWriterSettings ();
582 if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
584 if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
585 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
587 using (var writer = XmlWriter.Create (stream, s)){
592 public IEnumerable <XElement> AncestorsAndSelf ()
594 return GetAncestorList (null, true);
597 public IEnumerable <XElement> AncestorsAndSelf (XName name)
599 return GetAncestorList (name, true);
602 public IEnumerable <XElement> DescendantsAndSelf ()
604 List <XElement> list = new List <XElement> ();
606 list.AddRange (Descendants ());
610 public IEnumerable <XElement> DescendantsAndSelf (XName name)
612 List <XElement> list = new List <XElement> ();
613 if (name == this.name)
615 list.AddRange (Descendants (name));
619 public IEnumerable <XNode> DescendantNodesAndSelf ()
622 foreach (XNode node in DescendantNodes ())
626 public void SetAttributeValue (XName name, object value)
628 XAttribute a = Attribute (name);
634 SetAttributeObject (new XAttribute (name, value));
637 a.Value = XUtil.ToString (value);
641 void SetAttributeObject (XAttribute a)
643 a = (XAttribute) XUtil.GetDetachedObject (a);
645 if (attr_first == null) {
649 attr_last.NextAttribute = a;
650 a.PreviousAttribute = attr_last;
655 public override void WriteTo (XmlWriter w)
657 // some people expect the same prefix output as in input,
658 // in the loss of performance... see bug #466423.
659 string prefix = name.NamespaceName.Length > 0 ? w.LookupPrefix (name.Namespace.NamespaceName) : String.Empty;
660 foreach (XAttribute a in Attributes ()) {
661 if (a.IsNamespaceDeclaration && a.Value == name.Namespace.NamespaceName) {
662 if (a.Name.Namespace == XNamespace.Xmlns)
663 prefix = a.Name.LocalName;
664 // otherwise xmlns="..."
669 w.WriteStartElement (prefix, name.LocalName, name.Namespace.NamespaceName);
671 foreach (XAttribute a in Attributes ()) {
672 if (a.IsNamespaceDeclaration) {
673 if (a.Name.Namespace == XNamespace.Xmlns)
674 w.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
676 w.WriteAttributeString ("xmlns", a.Value);
679 w.WriteAttributeString (a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
682 foreach (XNode node in Nodes ())
685 if (explicit_is_empty)
686 w.WriteEndElement ();
688 w.WriteFullEndElement ();
691 public XNamespace GetDefaultNamespace ()
693 for (XElement el = this; el != null; el = el.Parent)
694 foreach (XAttribute a in el.Attributes ())
695 if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
696 return XNamespace.Get (a.Value);
697 return XNamespace.None; // nothing is declared.
700 public XNamespace GetNamespaceOfPrefix (string prefix)
702 for (XElement el = this; el != null; el = el.Parent)
703 foreach (XAttribute a in el.Attributes ())
704 if (a.IsNamespaceDeclaration && (prefix.Length == 0 && a.Name.LocalName == "xmlns" || a.Name.LocalName == prefix))
705 return XNamespace.Get (a.Value);
706 return XNamespace.None; // nothing is declared.
709 public string GetPrefixOfNamespace (XNamespace ns)
711 foreach (string prefix in GetPrefixOfNamespaceCore (ns))
712 if (GetNamespaceOfPrefix (prefix) == ns)
714 return null; // nothing is declared
717 IEnumerable<string> GetPrefixOfNamespaceCore (XNamespace ns)
719 for (XElement el = this; el != null; el = el.Parent)
720 foreach (XAttribute a in el.Attributes ())
721 if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
722 yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
725 public void ReplaceAll (object item)
731 public void ReplaceAll (params object [] items)
737 public void ReplaceAttributes (object item)
743 public void ReplaceAttributes (params object [] items)
749 public void SetElementValue (XName name, object value)
751 XElement el = new XElement (name, value);
756 public void SetValue (object value)
759 throw new ArgumentNullException ("value");
760 if (value is XAttribute || value is XDocument || value is XDeclaration || value is XDocumentType)
761 throw new ArgumentException (String.Format ("Node type {0} is not allowed as element value", value.GetType ()));
763 foreach (object o in XUtil.ExpandArray (value))
767 internal override bool OnAddingObject (object o, bool rejectAttribute, XNode refNode, bool addFirst)
769 if (o is XDocument || o is XDocumentType || o is XDeclaration || (rejectAttribute && o is XAttribute))
770 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", o.GetType ()));
772 XAttribute a = o as XAttribute;
774 foreach (XAttribute ia in Attributes ())
775 if (a.Name == ia.Name)
776 throw new InvalidOperationException (String.Format ("Duplicate attribute: {0}", a.Name));
777 SetAttributeObject (a);
780 else if (o is string && refNode is XText) {
781 ((XText) refNode).Value += o as string;
788 void IXmlSerializable.WriteXml (XmlWriter writer)
793 void IXmlSerializable.ReadXml (XmlReader reader)
795 ReadContentFrom (reader, LoadOptions.None);
798 XmlSchema IXmlSerializable.GetSchema ()