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;
34 using System.Xml.Schema;
35 using System.Xml.Serialization;
37 namespace System.Xml.Linq
39 [XmlSchemaProvider (null, IsAny = true)]
40 public class XElement : XContainer, IXmlSerializable
42 static IEnumerable <XElement> emptySequence =
43 new List <XElement> ();
45 public static IEnumerable <XElement> EmptySequence {
46 get { return emptySequence; }
50 XAttribute attr_first, attr_last;
51 bool explicit_is_empty = true;
53 public XElement (XName name, object content)
56 throw new ArgumentNullException ("name");
61 public XElement (XElement other)
64 throw new ArgumentNullException ("other");
66 Add (other.Attributes ());
70 public XElement (XName name)
73 throw new ArgumentNullException ("name");
77 public XElement (XName name, params object [] content)
80 throw new ArgumentNullException ("name");
85 public XElement (XStreamingElement other)
88 throw new ArgumentNullException ("other");
89 this.name = other.Name;
93 [CLSCompliant (false)]
94 public static explicit operator bool (XElement element)
97 throw new ArgumentNullException ("element");
98 return XUtil.ConvertToBoolean (element.Value);
101 [CLSCompliant (false)]
102 public static explicit operator bool? (XElement element)
107 return element.Value == null ? (bool?) null : XUtil.ConvertToBoolean (element.Value);
110 [CLSCompliant (false)]
111 public static explicit operator DateTime (XElement element)
114 throw new ArgumentNullException ("element");
115 return XUtil.ToDateTime (element.Value);
118 [CLSCompliant (false)]
119 public static explicit operator DateTime? (XElement element)
124 return element.Value == null ? (DateTime?) null : XUtil.ToDateTime (element.Value);
127 #if !TARGET_JVM // Same as for System.Xml.XmlConvert.ToDateTimeOffset
129 [CLSCompliant (false)]
130 public static explicit operator DateTimeOffset (XElement element)
133 throw new ArgumentNullException ("element");
134 return XmlConvert.ToDateTimeOffset (element.Value);
137 [CLSCompliant (false)]
138 public static explicit operator DateTimeOffset? (XElement element)
143 return element.Value == null ? (DateTimeOffset?) null : XmlConvert.ToDateTimeOffset (element.Value);
148 [CLSCompliant (false)]
149 public static explicit operator decimal (XElement element)
152 throw new ArgumentNullException ("element");
153 return XmlConvert.ToDecimal (element.Value);
156 [CLSCompliant (false)]
157 public static explicit operator decimal? (XElement element)
162 return element.Value == null ? (decimal?) null : XmlConvert.ToDecimal (element.Value);
165 [CLSCompliant (false)]
166 public static explicit operator double (XElement element)
169 throw new ArgumentNullException ("element");
170 return XmlConvert.ToDouble (element.Value);
173 [CLSCompliant (false)]
174 public static explicit operator double? (XElement element)
179 return element.Value == null ? (double?) null : XmlConvert.ToDouble (element.Value);
182 [CLSCompliant (false)]
183 public static explicit operator float (XElement element)
186 throw new ArgumentNullException ("element");
187 return XmlConvert.ToSingle (element.Value);
190 [CLSCompliant (false)]
191 public static explicit operator float? (XElement element)
196 return element.Value == null ? (float?) null : XmlConvert.ToSingle (element.Value);
199 [CLSCompliant (false)]
200 public static explicit operator Guid (XElement element)
203 throw new ArgumentNullException ("element");
204 return XmlConvert.ToGuid (element.Value);
207 [CLSCompliant (false)]
208 public static explicit operator Guid? (XElement element)
213 return element.Value == null ? (Guid?) null : XmlConvert.ToGuid (element.Value);
216 [CLSCompliant (false)]
217 public static explicit operator int (XElement element)
220 throw new ArgumentNullException ("element");
221 return XmlConvert.ToInt32 (element.Value);
224 [CLSCompliant (false)]
225 public static explicit operator int? (XElement element)
230 return element.Value == null ? (int?) null : XmlConvert.ToInt32 (element.Value);
233 [CLSCompliant (false)]
234 public static explicit operator long (XElement element)
237 throw new ArgumentNullException ("element");
238 return XmlConvert.ToInt64 (element.Value);
241 [CLSCompliant (false)]
242 public static explicit operator long? (XElement element)
247 return element.Value == null ? (long?) null : XmlConvert.ToInt64 (element.Value);
250 [CLSCompliant (false)]
251 public static explicit operator uint (XElement element)
254 throw new ArgumentNullException ("element");
255 return XmlConvert.ToUInt32 (element.Value);
258 [CLSCompliant (false)]
259 public static explicit operator uint? (XElement element)
264 return element.Value == null ? (uint?) null : XmlConvert.ToUInt32 (element.Value);
267 [CLSCompliant (false)]
268 public static explicit operator ulong (XElement element)
271 throw new ArgumentNullException ("element");
272 return XmlConvert.ToUInt64 (element.Value);
275 [CLSCompliant (false)]
276 public static explicit operator ulong? (XElement element)
281 return element.Value == null ? (ulong?) null : XmlConvert.ToUInt64 (element.Value);
284 [CLSCompliant (false)]
285 public static explicit operator TimeSpan (XElement element)
288 throw new ArgumentNullException ("element");
289 return XmlConvert.ToTimeSpan (element.Value);
292 [CLSCompliant (false)]
293 public static explicit operator TimeSpan? (XElement element)
298 return element.Value == null ? (TimeSpan?) null : XmlConvert.ToTimeSpan (element.Value);
301 [CLSCompliant (false)]
302 public static explicit operator string (XElement element)
307 return element.Value;
310 public XAttribute FirstAttribute {
311 get { return attr_first; }
312 internal set { attr_first = value; }
315 public XAttribute LastAttribute {
316 get { return attr_last; }
317 internal set { attr_last = value; }
320 public bool HasAttributes {
321 get { return attr_first != null; }
324 public bool HasElements {
326 foreach (object o in Nodes ())
333 public bool IsEmpty {
334 get { return !Nodes ().GetEnumerator ().MoveNext () && explicit_is_empty; }
335 internal set { explicit_is_empty = value; }
342 throw new ArgumentNullException ("Name");
343 OnNameChanging (this);
345 OnNameChanged (this);
349 public override XmlNodeType NodeType {
350 get { return XmlNodeType.Element; }
353 public string Value {
355 StringBuilder sb = null;
356 foreach (XNode n in Nodes ()) {
358 sb = new StringBuilder ();
360 sb.Append (((XText) n).Value);
361 else if (n is XElement)
362 sb.Append (((XElement) n).Value);
364 return sb == null ? String.Empty : sb.ToString ();
372 IEnumerable <XElement> GetAncestorList (XName name, bool getMeIn)
374 List <XElement> list = new List <XElement> ();
377 for (XElement el = Parent as XElement; el != null; el = el.Parent as XElement)
378 if (name == null || el.Name == name)
383 public XAttribute Attribute (XName name)
385 foreach (XAttribute a in Attributes ())
391 public IEnumerable <XAttribute> Attributes ()
394 for (XAttribute a = attr_first; a != null; a = next) {
395 next = a.NextAttribute;
401 public IEnumerable <XAttribute> Attributes (XName name)
403 foreach (XAttribute a in Attributes ())
408 static void DefineDefaultSettings (XmlReaderSettings settings, LoadOptions options)
411 // 2.1 has a DtdProcessing property which defaults to DtdProcessing.Prohibit
412 settings.DtdProcessing = DtdProcessing.Parse;
414 settings.ProhibitDtd = false;
417 settings.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
420 static XmlReaderSettings CreateDefaultSettings (LoadOptions options)
422 var settings = new XmlReaderSettings ();
423 DefineDefaultSettings (settings, options);
427 public static XElement Load (string uri)
429 return Load (uri, LoadOptions.None);
432 public static XElement Load (string uri, LoadOptions options)
434 XmlReaderSettings s = CreateDefaultSettings (options);
436 using (XmlReader r = XmlReader.Create (uri, s)) {
437 return LoadCore (r, options);
441 public static XElement Load (TextReader textReader)
443 return Load (textReader, LoadOptions.None);
446 public static XElement Load (TextReader textReader, LoadOptions options)
448 XmlReaderSettings s = CreateDefaultSettings (options);
450 using (XmlReader r = XmlReader.Create (textReader, s)) {
451 return LoadCore (r, options);
455 public static XElement Load (XmlReader reader)
457 return Load (reader, LoadOptions.None);
460 public static XElement Load (XmlReader reader, LoadOptions options)
462 XmlReaderSettings s = reader.Settings != null ? reader.Settings.Clone () : new XmlReaderSettings ();
463 DefineDefaultSettings (s, options);
465 using (XmlReader r = XmlReader.Create (reader, s)) {
466 return LoadCore (r, options);
470 #if MOONLIGHT || MOBILE || NET_4_0
471 public static XElement Load (Stream stream)
473 return Load (stream, LoadOptions.None);
476 public static XElement Load (Stream stream, LoadOptions options)
478 XmlReaderSettings s = new XmlReaderSettings ();
479 DefineDefaultSettings (s, options);
481 using (XmlReader r = XmlReader.Create (stream, s)) {
482 return LoadCore (r, options);
487 internal static XElement LoadCore (XmlReader r, LoadOptions options)
490 if (r.NodeType != XmlNodeType.Element)
491 throw new InvalidOperationException ("The XmlReader must be positioned at an element");
492 XName name = XName.Get (r.LocalName, r.NamespaceURI);
493 XElement e = new XElement (name);
494 e.FillLineInfoAndBaseUri (r, options);
496 if (r.MoveToFirstAttribute ()) {
498 // not sure how current Orcas behavior makes sense here though ...
499 if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
500 e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
502 e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
503 e.LastAttribute.FillLineInfoAndBaseUri (r, options);
504 } while (r.MoveToNextAttribute ());
507 if (!r.IsEmptyElement) {
509 e.ReadContentFrom (r, options);
511 e.explicit_is_empty = false;
513 e.explicit_is_empty = true;
519 public static XElement Parse (string text)
521 return Parse (text, LoadOptions.None);
524 public static XElement Parse (string text, LoadOptions options)
526 return Load (new StringReader (text), options);
529 public void RemoveAll ()
535 public void RemoveAttributes ()
537 while (attr_first != null)
541 public void Save (string fileName)
543 Save (fileName, SaveOptions.None);
546 public void Save (string fileName, SaveOptions options)
548 XmlWriterSettings s = new XmlWriterSettings ();
550 if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
552 #if NET_4_0 || MOONLIGHT || MOBILE
553 if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
554 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
556 using (XmlWriter w = XmlWriter.Create (fileName, s)) {
561 public void Save (TextWriter textWriter)
563 Save (textWriter, SaveOptions.None);
566 public void Save (TextWriter textWriter, SaveOptions options)
568 XmlWriterSettings s = new XmlWriterSettings ();
570 if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
572 #if NET_4_0 || MOONLIGHT || MOBILE
573 if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
574 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
576 using (XmlWriter w = XmlWriter.Create (textWriter, s)) {
581 public void Save (XmlWriter writer)
586 #if NET_4_0 || MOONLIGHT || MOBILE
587 public void Save (Stream stream)
589 Save (stream, SaveOptions.None);
592 public void Save (Stream stream, SaveOptions options)
594 XmlWriterSettings s = new XmlWriterSettings ();
595 if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
597 if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
598 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
600 using (var writer = XmlWriter.Create (stream, s)){
605 public IEnumerable <XElement> AncestorsAndSelf ()
607 return GetAncestorList (null, true);
610 public IEnumerable <XElement> AncestorsAndSelf (XName name)
612 return GetAncestorList (name, true);
615 public IEnumerable <XElement> DescendantsAndSelf ()
617 List <XElement> list = new List <XElement> ();
619 list.AddRange (Descendants ());
623 public IEnumerable <XElement> DescendantsAndSelf (XName name)
625 List <XElement> list = new List <XElement> ();
626 if (name == this.name)
628 list.AddRange (Descendants (name));
632 public IEnumerable <XNode> DescendantNodesAndSelf ()
635 foreach (XNode node in DescendantNodes ())
639 public void SetAttributeValue (XName name, object value)
641 XAttribute a = Attribute (name);
647 SetAttributeObject (new XAttribute (name, value));
650 a.Value = XUtil.ToString (value);
654 void SetAttributeObject (XAttribute a)
657 a = (XAttribute) XUtil.GetDetachedObject (a);
659 if (attr_first == null) {
663 attr_last.NextAttribute = a;
664 a.PreviousAttribute = attr_last;
670 string LookupPrefix (string ns, XmlWriter w)
672 string prefix = ns.Length > 0 ? GetPrefixOfNamespace (ns) ?? w.LookupPrefix (ns) : String.Empty;
673 foreach (XAttribute a in Attributes ()) {
674 if (a.IsNamespaceDeclaration && a.Value == ns) {
675 if (a.Name.Namespace == XNamespace.Xmlns)
676 prefix = a.Name.LocalName;
677 // otherwise xmlns="..."
684 static string CreateDummyNamespace (ref int createdNS, IEnumerable<XAttribute> atts, bool isAttr)
686 if (!isAttr && atts.All (a => a.Name.LocalName != "xmlns" || a.Name.NamespaceName == XNamespace.Xmlns.NamespaceName))
690 p = "p" + (++createdNS);
692 if (atts.All (a => a.Name.LocalName != p || a.Name.NamespaceName == XNamespace.Xmlns.NamespaceName))
698 public override void WriteTo (XmlWriter writer)
700 // some people expect the same prefix output as in input,
701 // in the loss of performance... see bug #466423.
702 string prefix = LookupPrefix (name.NamespaceName, writer);
705 prefix = CreateDummyNamespace (ref createdNS, Attributes (), false);
707 writer.WriteStartElement (prefix, name.LocalName, name.Namespace.NamespaceName);
709 foreach (XAttribute a in Attributes ()) {
710 if (a.IsNamespaceDeclaration) {
711 if (a.Name.Namespace == XNamespace.Xmlns)
712 writer.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
714 writer.WriteAttributeString ("xmlns", a.Value);
716 string apfix = LookupPrefix (a.Name.NamespaceName, writer);
718 apfix = CreateDummyNamespace (ref createdNS, Attributes (), true);
719 writer.WriteAttributeString (apfix, a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
723 foreach (XNode node in Nodes ())
724 node.WriteTo (writer);
726 if (explicit_is_empty)
727 writer.WriteEndElement ();
729 writer.WriteFullEndElement ();
732 public XNamespace GetDefaultNamespace ()
734 for (XElement el = this; el != null; el = el.Parent)
735 foreach (XAttribute a in el.Attributes ())
736 if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
737 return XNamespace.Get (a.Value);
738 return XNamespace.None; // nothing is declared.
741 public XNamespace GetNamespaceOfPrefix (string prefix)
743 for (XElement el = this; el != null; el = el.Parent)
744 foreach (XAttribute a in el.Attributes ())
745 if (a.IsNamespaceDeclaration && (prefix.Length == 0 && a.Name.LocalName == "xmlns" || a.Name.LocalName == prefix))
746 return XNamespace.Get (a.Value);
747 return XNamespace.None; // nothing is declared.
750 public string GetPrefixOfNamespace (XNamespace ns)
752 foreach (string prefix in GetPrefixOfNamespaceCore (ns))
753 if (GetNamespaceOfPrefix (prefix) == ns)
755 return null; // nothing is declared
758 IEnumerable<string> GetPrefixOfNamespaceCore (XNamespace ns)
760 for (XElement el = this; el != null; el = el.Parent)
761 foreach (XAttribute a in el.Attributes ())
762 if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
763 yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
766 public void ReplaceAll (object content)
772 public void ReplaceAll (params object [] content)
778 public void ReplaceAttributes (object content)
784 public void ReplaceAttributes (params object [] content)
790 public void SetElementValue (XName name, object value)
792 var element = Element (name);
793 if (element == null && value != null) {
794 Add (new XElement (name, value));
795 } else if (element != null && value == null) {
798 element.SetValue (value);
801 public void SetValue (object value)
804 throw new ArgumentNullException ("value");
805 if (value is XAttribute || value is XDocument || value is XDeclaration || value is XDocumentType)
806 throw new ArgumentException (String.Format ("Node type {0} is not allowed as element value", value.GetType ()));
808 foreach (object o in XUtil.ExpandArray (value))
812 internal override bool OnAddingObject (object o, bool rejectAttribute, XNode refNode, bool addFirst)
814 if (o is XDocument || o is XDocumentType || o is XDeclaration || (rejectAttribute && o is XAttribute))
815 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", o.GetType ()));
817 XAttribute a = o as XAttribute;
819 foreach (XAttribute ia in Attributes ())
820 if (a.Name == ia.Name)
821 throw new InvalidOperationException (String.Format ("Duplicate attribute: {0}", a.Name));
822 SetAttributeObject (a);
825 else if (o is string && refNode is XText) {
826 ((XText) refNode).Value += o as string;
833 void IXmlSerializable.WriteXml (XmlWriter writer)
838 void IXmlSerializable.ReadXml (XmlReader reader)
840 ReadContentFrom (reader, LoadOptions.None);
843 XmlSchema IXmlSerializable.GetSchema ()