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 value)
59 public XElement (XElement source)
62 Add (source.Attributes ());
63 Add (source.Nodes ());
66 public XElement (XName name)
71 public XElement (XName name, params object [] contents)
77 public XElement (XStreamingElement source)
79 this.name = source.Name;
80 Add (source.Contents);
83 [CLSCompliant (false)]
84 public static explicit operator bool (XElement element)
87 throw new ArgumentNullException ("element");
88 return XUtil.ConvertToBoolean (element.Value);
91 [CLSCompliant (false)]
92 public static explicit operator bool? (XElement element)
97 return element.Value == null ? (bool?) null : XUtil.ConvertToBoolean (element.Value);
100 [CLSCompliant (false)]
101 public static explicit operator DateTime (XElement element)
104 throw new ArgumentNullException ("element");
105 return XUtil.ToDateTime (element.Value);
108 [CLSCompliant (false)]
109 public static explicit operator DateTime? (XElement element)
114 return element.Value == null ? (DateTime?) null : XUtil.ToDateTime (element.Value);
117 #if !TARGET_JVM // Same as for System.Xml.XmlConvert.ToDateTimeOffset
119 [CLSCompliant (false)]
120 public static explicit operator DateTimeOffset (XElement element)
123 throw new ArgumentNullException ("element");
124 return XmlConvert.ToDateTimeOffset (element.Value);
127 [CLSCompliant (false)]
128 public static explicit operator DateTimeOffset? (XElement element)
133 return element.Value == null ? (DateTimeOffset?) null : XmlConvert.ToDateTimeOffset (element.Value);
138 [CLSCompliant (false)]
139 public static explicit operator decimal (XElement element)
142 throw new ArgumentNullException ("element");
143 return XmlConvert.ToDecimal (element.Value);
146 [CLSCompliant (false)]
147 public static explicit operator decimal? (XElement element)
152 return element.Value == null ? (decimal?) null : XmlConvert.ToDecimal (element.Value);
155 [CLSCompliant (false)]
156 public static explicit operator double (XElement element)
159 throw new ArgumentNullException ("element");
160 return XmlConvert.ToDouble (element.Value);
163 [CLSCompliant (false)]
164 public static explicit operator double? (XElement element)
169 return element.Value == null ? (double?) null : XmlConvert.ToDouble (element.Value);
172 [CLSCompliant (false)]
173 public static explicit operator float (XElement element)
176 throw new ArgumentNullException ("element");
177 return XmlConvert.ToSingle (element.Value);
180 [CLSCompliant (false)]
181 public static explicit operator float? (XElement element)
186 return element.Value == null ? (float?) null : XmlConvert.ToSingle (element.Value);
189 [CLSCompliant (false)]
190 public static explicit operator Guid (XElement element)
193 throw new ArgumentNullException ("element");
194 return XmlConvert.ToGuid (element.Value);
197 [CLSCompliant (false)]
198 public static explicit operator Guid? (XElement element)
203 return element.Value == null ? (Guid?) null : XmlConvert.ToGuid (element.Value);
206 [CLSCompliant (false)]
207 public static explicit operator int (XElement element)
210 throw new ArgumentNullException ("element");
211 return XmlConvert.ToInt32 (element.Value);
214 [CLSCompliant (false)]
215 public static explicit operator int? (XElement element)
220 return element.Value == null ? (int?) null : XmlConvert.ToInt32 (element.Value);
223 [CLSCompliant (false)]
224 public static explicit operator long (XElement element)
227 throw new ArgumentNullException ("element");
228 return XmlConvert.ToInt64 (element.Value);
231 [CLSCompliant (false)]
232 public static explicit operator long? (XElement element)
237 return element.Value == null ? (long?) null : XmlConvert.ToInt64 (element.Value);
240 [CLSCompliant (false)]
241 public static explicit operator uint (XElement element)
244 throw new ArgumentNullException ("element");
245 return XmlConvert.ToUInt32 (element.Value);
248 [CLSCompliant (false)]
249 public static explicit operator uint? (XElement element)
254 return element.Value == null ? (uint?) null : XmlConvert.ToUInt32 (element.Value);
257 [CLSCompliant (false)]
258 public static explicit operator ulong (XElement element)
261 throw new ArgumentNullException ("element");
262 return XmlConvert.ToUInt64 (element.Value);
265 [CLSCompliant (false)]
266 public static explicit operator ulong? (XElement element)
271 return element.Value == null ? (ulong?) null : XmlConvert.ToUInt64 (element.Value);
274 [CLSCompliant (false)]
275 public static explicit operator TimeSpan (XElement element)
278 throw new ArgumentNullException ("element");
279 return XmlConvert.ToTimeSpan (element.Value);
282 [CLSCompliant (false)]
283 public static explicit operator TimeSpan? (XElement element)
288 return element.Value == null ? (TimeSpan?) null : XmlConvert.ToTimeSpan (element.Value);
291 [CLSCompliant (false)]
292 public static explicit operator string (XElement element)
297 return element.Value;
300 public XAttribute FirstAttribute {
301 get { return attr_first; }
302 internal set { attr_first = value; }
305 public XAttribute LastAttribute {
306 get { return attr_last; }
307 internal set { attr_last = value; }
310 public bool HasAttributes {
311 get { return attr_first != null; }
314 public bool HasElements {
316 foreach (object o in Nodes ())
323 public bool IsEmpty {
324 get { return !Nodes ().GetEnumerator ().MoveNext () && explicit_is_empty; }
325 internal set { explicit_is_empty = value; }
332 throw new ArgumentNullException ("Name");
337 public override XmlNodeType NodeType {
338 get { return XmlNodeType.Element; }
341 public string Value {
343 StringBuilder sb = null;
344 foreach (XNode n in Nodes ()) {
346 sb = new StringBuilder ();
348 sb.Append (((XText) n).Value);
349 else if (n is XElement)
350 sb.Append (((XElement) n).Value);
352 return sb == null ? String.Empty : sb.ToString ();
360 IEnumerable <XElement> GetAncestorList (XName name, bool getMeIn)
362 List <XElement> list = new List <XElement> ();
365 for (XElement el = Parent as XElement; el != null; el = el.Parent as XElement)
366 if (name == null || el.Name == name)
371 public XAttribute Attribute (XName name)
373 foreach (XAttribute a in Attributes ())
379 public IEnumerable <XAttribute> Attributes ()
382 for (XAttribute a = attr_first; a != null; a = next) {
383 next = a.NextAttribute;
389 public IEnumerable <XAttribute> Attributes (XName name)
391 foreach (XAttribute a in Attributes ())
396 static void DefineDefaultSettings (XmlReaderSettings settings, LoadOptions options)
399 // 2.1 has a DtdProcessing property which defaults to DtdProcessing.Prohibit
400 settings.DtdProcessing = DtdProcessing.Parse;
402 settings.ProhibitDtd = false;
405 settings.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
408 static XmlReaderSettings CreateDefaultSettings (LoadOptions options)
410 var settings = new XmlReaderSettings ();
411 DefineDefaultSettings (settings, options);
415 public static XElement Load (string uri)
417 return Load (uri, LoadOptions.None);
420 public static XElement Load (string uri, LoadOptions options)
422 XmlReaderSettings s = CreateDefaultSettings (options);
424 using (XmlReader r = XmlReader.Create (uri, s)) {
425 return LoadCore (r, options);
429 public static XElement Load (TextReader tr)
431 return Load (tr, LoadOptions.None);
434 public static XElement Load (TextReader tr, LoadOptions options)
436 XmlReaderSettings s = CreateDefaultSettings (options);
438 using (XmlReader r = XmlReader.Create (tr, s)) {
439 return LoadCore (r, options);
443 public static XElement Load (XmlReader reader)
445 return Load (reader, LoadOptions.None);
448 public static XElement Load (XmlReader reader, LoadOptions options)
450 XmlReaderSettings s = reader.Settings != null ? reader.Settings.Clone () : new XmlReaderSettings ();
451 DefineDefaultSettings (s, options);
453 using (XmlReader r = XmlReader.Create (reader, s)) {
454 return LoadCore (r, options);
458 #if MOONLIGHT || MOBILE || NET_4_0
459 public static XElement Load (Stream stream)
461 return Load (stream, LoadOptions.None);
464 public static XElement Load (Stream stream, LoadOptions options)
466 XmlReaderSettings s = new XmlReaderSettings ();
467 DefineDefaultSettings (s, options);
469 using (XmlReader r = XmlReader.Create (stream, s)) {
470 return LoadCore (r, options);
475 internal static XElement LoadCore (XmlReader r, LoadOptions options)
478 if (r.NodeType != XmlNodeType.Element)
479 throw new InvalidOperationException ("The XmlReader must be positioned at an element");
480 XName name = XName.Get (r.LocalName, r.NamespaceURI);
481 XElement e = new XElement (name);
482 e.FillLineInfoAndBaseUri (r, options);
484 if (r.MoveToFirstAttribute ()) {
486 // not sure how current Orcas behavior makes sense here though ...
487 if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
488 e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
490 e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
491 e.LastAttribute.FillLineInfoAndBaseUri (r, options);
492 } while (r.MoveToNextAttribute ());
495 if (!r.IsEmptyElement) {
497 e.ReadContentFrom (r, options);
499 e.explicit_is_empty = false;
501 e.explicit_is_empty = true;
507 public static XElement Parse (string s)
509 return Parse (s, LoadOptions.None);
512 public static XElement Parse (string s, LoadOptions options)
514 return Load (new StringReader (s), options);
517 public void RemoveAll ()
523 public void RemoveAttributes ()
525 while (attr_first != null)
529 public void Save (string filename)
531 Save (filename, SaveOptions.None);
534 public void Save (string filename, SaveOptions options)
536 XmlWriterSettings s = new XmlWriterSettings ();
538 if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
540 #if NET_4_0 || MOONLIGHT || MOBILE
541 if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
542 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
544 using (XmlWriter w = XmlWriter.Create (filename, s)) {
549 public void Save (TextWriter tw)
551 Save (tw, SaveOptions.None);
554 public void Save (TextWriter tw, SaveOptions options)
556 XmlWriterSettings s = new XmlWriterSettings ();
558 if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
560 #if NET_4_0 || MOONLIGHT || MOBILE
561 if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
562 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
564 using (XmlWriter w = XmlWriter.Create (tw, s)) {
569 public void Save (XmlWriter w)
574 #if NET_4_0 || MOONLIGHT || MOBILE
575 public void Save (Stream stream)
577 Save (stream, SaveOptions.None);
580 public void Save (Stream stream, SaveOptions options)
582 XmlWriterSettings s = new XmlWriterSettings ();
583 if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
585 if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
586 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
588 using (var writer = XmlWriter.Create (stream, s)){
593 public IEnumerable <XElement> AncestorsAndSelf ()
595 return GetAncestorList (null, true);
598 public IEnumerable <XElement> AncestorsAndSelf (XName name)
600 return GetAncestorList (name, true);
603 public IEnumerable <XElement> DescendantsAndSelf ()
605 List <XElement> list = new List <XElement> ();
607 list.AddRange (Descendants ());
611 public IEnumerable <XElement> DescendantsAndSelf (XName name)
613 List <XElement> list = new List <XElement> ();
614 if (name == this.name)
616 list.AddRange (Descendants (name));
620 public IEnumerable <XNode> DescendantNodesAndSelf ()
623 foreach (XNode node in DescendantNodes ())
627 public void SetAttributeValue (XName name, object value)
629 XAttribute a = Attribute (name);
635 SetAttributeObject (new XAttribute (name, value));
638 a.Value = XUtil.ToString (value);
642 void SetAttributeObject (XAttribute a)
644 a = (XAttribute) XUtil.GetDetachedObject (a);
646 if (attr_first == null) {
650 attr_last.NextAttribute = a;
651 a.PreviousAttribute = attr_last;
656 string LookupPrefix (string ns, XmlWriter w)
658 string prefix = ns.Length > 0 ? w.LookupPrefix (ns) : String.Empty;
659 foreach (XAttribute a in Attributes ()) {
660 if (a.IsNamespaceDeclaration && a.Value == ns) {
661 if (a.Name.Namespace == XNamespace.Xmlns)
662 prefix = a.Name.LocalName;
663 // otherwise xmlns="..."
670 public override void WriteTo (XmlWriter w)
672 // some people expect the same prefix output as in input,
673 // in the loss of performance... see bug #466423.
675 string prefix = LookupPrefix (name.NamespaceName, w);
676 Func<string> nsCreator = () => {
679 p = "p" + (++createdNS);
681 if (Attributes ().All (a => a.Name.LocalName != p))
687 prefix = nsCreator ();
689 w.WriteStartElement (prefix, name.LocalName, name.Namespace.NamespaceName);
691 foreach (XAttribute a in Attributes ()) {
692 if (a.IsNamespaceDeclaration) {
693 if (a.Name.Namespace == XNamespace.Xmlns)
694 w.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
696 w.WriteAttributeString ("xmlns", a.Value);
698 string apfix = LookupPrefix (a.Name.NamespaceName, w);
700 apfix = nsCreator ();
701 w.WriteAttributeString (apfix, a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
705 foreach (XNode node in Nodes ())
708 if (explicit_is_empty)
709 w.WriteEndElement ();
711 w.WriteFullEndElement ();
714 public XNamespace GetDefaultNamespace ()
716 for (XElement el = this; el != null; el = el.Parent)
717 foreach (XAttribute a in el.Attributes ())
718 if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
719 return XNamespace.Get (a.Value);
720 return XNamespace.None; // nothing is declared.
723 public XNamespace GetNamespaceOfPrefix (string prefix)
725 for (XElement el = this; el != null; el = el.Parent)
726 foreach (XAttribute a in el.Attributes ())
727 if (a.IsNamespaceDeclaration && (prefix.Length == 0 && a.Name.LocalName == "xmlns" || a.Name.LocalName == prefix))
728 return XNamespace.Get (a.Value);
729 return XNamespace.None; // nothing is declared.
732 public string GetPrefixOfNamespace (XNamespace ns)
734 foreach (string prefix in GetPrefixOfNamespaceCore (ns))
735 if (GetNamespaceOfPrefix (prefix) == ns)
737 return null; // nothing is declared
740 IEnumerable<string> GetPrefixOfNamespaceCore (XNamespace ns)
742 for (XElement el = this; el != null; el = el.Parent)
743 foreach (XAttribute a in el.Attributes ())
744 if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
745 yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
748 public void ReplaceAll (object item)
754 public void ReplaceAll (params object [] items)
760 public void ReplaceAttributes (object item)
766 public void ReplaceAttributes (params object [] items)
772 public void SetElementValue (XName name, object value)
774 var element = Element (name);
775 if (element == null && value != null) {
776 Add (new XElement (name, value));
777 } else if (element != null && value == null) {
780 element.SetValue (value);
783 public void SetValue (object value)
786 throw new ArgumentNullException ("value");
787 if (value is XAttribute || value is XDocument || value is XDeclaration || value is XDocumentType)
788 throw new ArgumentException (String.Format ("Node type {0} is not allowed as element value", value.GetType ()));
790 foreach (object o in XUtil.ExpandArray (value))
794 internal override bool OnAddingObject (object o, bool rejectAttribute, XNode refNode, bool addFirst)
796 if (o is XDocument || o is XDocumentType || o is XDeclaration || (rejectAttribute && o is XAttribute))
797 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", o.GetType ()));
799 XAttribute a = o as XAttribute;
801 foreach (XAttribute ia in Attributes ())
802 if (a.Name == ia.Name)
803 throw new InvalidOperationException (String.Format ("Duplicate attribute: {0}", a.Name));
804 SetAttributeObject (a);
807 else if (o is string && refNode is XText) {
808 ((XText) refNode).Value += o as string;
815 void IXmlSerializable.WriteXml (XmlWriter writer)
820 void IXmlSerializable.ReadXml (XmlReader reader)
822 ReadContentFrom (reader, LoadOptions.None);
825 XmlSchema IXmlSerializable.GetSchema ()