File: System\Xml\Linq\XElement.cs
Web Access
Project: src\src\libraries\System.Private.Xml.Linq\src\System.Private.Xml.Linq.csproj (System.Private.Xml.Linq)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Schema;
using System.Xml.Serialization;
using CultureInfo = System.Globalization.CultureInfo;
using IEnumerable = System.Collections.IEnumerable;
using StringBuilder = System.Text.StringBuilder;
using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;
 
namespace System.Xml.Linq
{
    /// <summary>
    /// Represents an XML element.
    /// </summary>
    /// <remarks>
    /// An element has an <see cref="XName"/>, optionally one or more attributes,
    /// and can optionally contain content (see <see cref="XContainer.Nodes"/>.
    /// An <see cref="XElement"/> can contain the following types of content:
    ///   <list>
    ///     <item>string (Text content)</item>
    ///     <item><see cref="XElement"/></item>
    ///     <item><see cref="XComment"/></item>
    ///     <item><see cref="XProcessingInstruction"/></item>
    ///   </list>
    /// </remarks>
    [XmlSchemaProvider(null, IsAny = true)]
    [System.ComponentModel.TypeDescriptionProvider("MS.Internal.Xml.Linq.ComponentModel.XTypeDescriptionProvider`1[[System.Xml.Linq.XElement, System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],System.ComponentModel.TypeConverter")]
    public class XElement : XContainer, IXmlSerializable
    {
        /// <summary>
        /// Gets an empty collection of elements.
        /// </summary>
        public static IEnumerable<XElement> EmptySequence
        {
            get
            {
                return Array.Empty<XElement>();
            }
        }
 
        internal XName name = null!;
        internal XAttribute? lastAttr;
 
        /// <summary>
        /// Initializes a new instance of the XElement class with the specified name.
        /// </summary>
        /// <param name="name">
        /// The name of the element.
        /// </param>
        public XElement(XName name)
        {
            ArgumentNullException.ThrowIfNull(name);
 
            this.name = name;
        }
 
        /// <summary>
        /// Initializes a new instance of the XElement class with the specified name and content.
        /// </summary>
        /// <param name="name">
        /// The element name.
        /// </param>
        /// <param name="content">The initial contents of the element.</param>
        /// <remarks>
        /// See XContainer.Add(object content) for details about the content that can be added
        /// using this method.
        /// </remarks>
        public XElement(XName name, object? content)
            : this(name)
        {
            AddContentSkipNotify(content);
        }
 
        /// <summary>
        /// Initializes a new instance of the XElement class with the specified name and content.
        /// </summary>
        /// <param name="name">
        /// The element name.
        /// </param>
        /// <param name="content">
        /// The initial content of the element.
        /// </param>
        /// <remarks>
        /// See XContainer.Add(object content) for details about the content that can be added
        /// using this method.
        /// </remarks>
        public XElement(XName name, params object?[] content) : this(name, (object)content) { }
 
        /// <summary>
        /// Initializes a new instance of the XElement class from another XElement object.
        /// </summary>
        /// <param name="other">
        /// Another element that will be copied to this element.
        /// </param>
        /// <remarks>
        /// This constructor makes a deep copy from one element to another.
        /// </remarks>
        public XElement(XElement other)
            : base(other)
        {
            this.name = other.name;
            XAttribute? a = other.lastAttr;
            if (a != null)
            {
                do
                {
                    a = a.next!;
                    AppendAttributeSkipNotify(new XAttribute(a));
                } while (a != other.lastAttr);
            }
        }
 
        /// <summary>
        /// Initializes an XElement object from an <see cref="XStreamingElement"/> object.
        /// </summary>
        /// <param name="other">
        /// The <see cref="XStreamingElement"/> object whose value will be used
        /// to initialize the new element.
        /// </param>
        public XElement(XStreamingElement other)
        {
            ArgumentNullException.ThrowIfNull(other);
 
            name = other.name;
            AddContentSkipNotify(other.content);
        }
 
        internal XElement()
            : this("default"!)
        {
        }
 
        internal XElement(XmlReader r)
            : this(r, LoadOptions.None)
        {
        }
 
        private XElement(AsyncConstructionSentry _)
        {
            // Dummy ctor used to avoid public default ctor.  This is used
            // by async methods meant to perform the same operations as
            // the XElement constructors that do synchronous processing;
            // the async methods instead construct an XElement using this
            // constructor (which doesn't do any processing) and then themselves
            // do the async processing.  This is because ctors can't be 'async'.
        }
        private struct AsyncConstructionSentry { }
 
        internal XElement(XmlReader r, LoadOptions o)
        {
            ReadElementFrom(r, o);
        }
 
        internal static async Task<XElement> CreateAsync(XmlReader r, CancellationToken cancellationToken)
        {
            XElement xe = new XElement(default(AsyncConstructionSentry));
            await xe.ReadElementFromAsync(r, LoadOptions.None, cancellationToken).ConfigureAwait(false);
            return xe;
        }
 
        ///<overloads>
        /// Outputs this <see cref="XElement"/>'s underlying XML tree.  The output can
        /// be saved to a file, a <see cref="Stream"/>, a <see cref="TextWriter"/>,
        /// or an <see cref="XmlWriter"/>.  Optionally whitespace can be preserved.
        /// </overloads>
        /// <summary>
        /// Output this <see cref="XElement"/> to a file.
        /// </summary>
        /// <remarks>
        /// The format will be indented by default.  If you want
        /// no indenting then use the SaveOptions version of Save (see
        /// <see cref="XElement.Save(string, SaveOptions)"/>) enabling
        /// SaveOptions.DisableFormatting.
        /// There is also an option SaveOptions.OmitDuplicateNamespaces for removing duplicate namespace declarations.
        /// Or instead use the SaveOptions as an annotation on this node or its ancestors, then this method will use those options.
        /// </remarks>
        /// <param name="fileName">
        /// The name of the file to output the XML to.
        /// </param>
        public void Save(string fileName)
        {
            Save(fileName, GetSaveOptionsFromAnnotations());
        }
 
        /// <summary>
        /// Output this <see cref="XElement"/> to a file.
        /// </summary>
        /// <param name="fileName">
        /// The name of the file to output the XML to.
        /// </param>
        /// <param name="options">
        /// If SaveOptions.DisableFormatting is enabled the output is not indented.
        /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed.
        /// </param>
        public void Save(string fileName, SaveOptions options)
        {
            XmlWriterSettings ws = GetXmlWriterSettings(options);
            using (XmlWriter w = XmlWriter.Create(fileName, ws))
            {
                Save(w);
            }
        }
 
        /// <summary>
        /// Gets the first attribute of an element.
        /// </summary>
        public XAttribute? FirstAttribute
        {
            get { return lastAttr?.next; }
        }
 
        /// <summary>
        /// Gets a value indicating whether the element has at least one attribute.
        /// </summary>
        public bool HasAttributes
        {
            get { return lastAttr != null; }
        }
 
        /// <summary>
        /// Gets a value indicating whether the element has at least one child element.
        /// </summary>
        public bool HasElements
        {
            get
            {
                XNode? n = content as XNode;
                if (n != null)
                {
                    do
                    {
                        if (n is XElement) return true;
                        n = n.next!;
                    } while (n != content);
                }
                return false;
            }
        }
 
        /// <summary>
        /// Gets a value indicating whether the element contains no content.
        /// </summary>
        public bool IsEmpty
        {
            get { return content == null; }
        }
 
        /// <summary>
        /// Gets the last attribute of an element.
        /// </summary>
        public XAttribute? LastAttribute
        {
            get { return lastAttr; }
        }
 
        /// <summary>
        /// Gets the name of this element.
        /// </summary>
        public XName Name
        {
            get
            {
                return name;
            }
            set
            {
                ArgumentNullException.ThrowIfNull(value);
                bool notify = NotifyChanging(this, XObjectChangeEventArgs.Name);
                name = value;
                if (notify) NotifyChanged(this, XObjectChangeEventArgs.Name);
            }
        }
 
        /// <summary>
        /// Gets the node type for this node.
        /// </summary>
        /// <remarks>
        /// This property will always return XmlNodeType.Text.
        /// </remarks>
        public override XmlNodeType NodeType
        {
            get
            {
                return XmlNodeType.Element;
            }
        }
 
        /// <summary>
        /// Gets the text contents of this element.
        /// </summary>
        /// <remarks>
        /// If there is text content interspersed with nodes (mixed content) then the text content
        /// will be concatenated and returned.
        /// </remarks>
        public string Value
        {
            get
            {
                if (content == null) return string.Empty;
                string? s = content as string;
                if (s != null) return s;
                StringBuilder sb = StringBuilderCache.Acquire();
                AppendText(sb);
                return StringBuilderCache.GetStringAndRelease(sb);
            }
            set
            {
                ArgumentNullException.ThrowIfNull(value);
                RemoveNodes();
                Add(value);
            }
        }
 
        /// <overloads>
        /// Returns this <see cref="XElement"/> and all of it's ancestors up
        /// to the root node.  Optionally an <see cref="XName"/> can be passed
        /// in to target a specific ancestor(s).
        /// <seealso cref="XNode.Ancestors()"/>
        /// </overloads>
        /// <summary>
        /// Returns this <see cref="XElement"/> and all of it's ancestors up to
        /// the root node.
        /// <seealso cref="XNode.Ancestors()"/>
        /// </summary>
        /// <returns>
        /// An <see cref="IEnumerable"/> of <see cref="XElement"/> containing all of
        /// this <see cref="XElement"/>'s ancestors up to the root node (including
        /// this <see cref="XElement"/>.
        /// </returns>
        public IEnumerable<XElement> AncestorsAndSelf()
        {
            return GetAncestors(null, true);
        }
 
        /// <summary>
        /// Returns the ancestor(s) of this <see cref="XElement"/> with the matching
        /// <see cref="XName"/>. If this <see cref="XElement"/>'s <see cref="XName"/>
        /// matches the <see cref="XName"/> passed in then it will be included in the
        /// resulting <see cref="IEnumerable"/> or <see cref="XElement"/>.
        /// <seealso cref="XNode.Ancestors()"/>
        /// </summary>
        /// <param name="name">
        /// The <see cref="XName"/> of the target ancestor.
        /// </param>
        /// <returns>
        /// An <see cref="IEnumerable"/> of <see cref="XElement"/> containing the
        /// ancestors of this <see cref="XElement"/> with a matching <see cref="XName"/>.
        /// </returns>
        public IEnumerable<XElement> AncestorsAndSelf(XName? name)
        {
            return name != null ? GetAncestors(name, true) : XElement.EmptySequence;
        }
 
        /// <summary>
        /// Returns the <see cref="XAttribute"/> associated with this <see cref="XElement"/> that has this
        /// <see cref="XName"/>.
        /// </summary>
        /// <param name="name">
        /// The <see cref="XName"/> of the <see cref="XAttribute"/> to get.
        /// </param>
        /// <returns>
        /// The <see cref="XAttribute"/> with the <see cref="XName"/> passed in.  If there is no <see cref="XAttribute"/>
        /// with this <see cref="XName"/> then null is returned.
        /// </returns>
        public XAttribute? Attribute(XName name)
        {
            XAttribute? a = lastAttr;
            if (a != null)
            {
                do
                {
                    a = a.next!;
                    if (a.name == name) return a;
                } while (a != lastAttr);
            }
            return null;
        }
 
        /// <overloads>
        /// Returns the <see cref="XAttribute"/> associated with this <see cref="XElement"/>.  Optionally
        /// an <see cref="XName"/> can be given to target a specific <see cref="XAttribute"/>(s).
        /// </overloads>
        /// <summary>
        /// Returns all of the <see cref="XAttribute"/>s associated with this <see cref="XElement"/>.
        /// <seealso cref="XContainer.Elements()"/>
        /// </summary>
        /// <returns>
        /// An <see cref="IEnumerable"/> of <see cref="XAttribute"/> containing all of the <see cref="XAttribute"/>s
        /// associated with this <see cref="XElement"/>.
        /// </returns>
        public IEnumerable<XAttribute> Attributes()
        {
            return GetAttributes(null);
        }
 
        /// <summary>
        /// Returns the <see cref="XAttribute"/>(s) associated with this <see cref="XElement"/> that has the passed
        /// in <see cref="XName"/>.
        /// <seealso cref="XElement.Attributes()"/>
        /// </summary>
        /// <param name="name">
        /// The <see cref="XName"/> of the targeted <see cref="XAttribute"/>.
        /// </param>
        /// <returns>
        /// The <see cref="XAttribute"/>(s) with the matching
        /// </returns>
        public IEnumerable<XAttribute> Attributes(XName? name)
        {
            return name != null ? GetAttributes(name) : XAttribute.EmptySequence;
        }
 
        /// <summary>
        /// Get the self and descendant nodes for an <see cref="XElement"/>
        /// </summary>
        /// <returns></returns>
        public IEnumerable<XNode> DescendantNodesAndSelf()
        {
            return GetDescendantNodes(true);
        }
 
        /// <overloads>
        /// Returns this <see cref="XElement"/> and all of it's descendants.  Overloads allow
        /// specification of a type of descendant to return, or a specific <see cref="XName"/>
        /// of a descendant <see cref="XElement"/> to match.
        /// </overloads>
        /// <summary>
        /// Returns this <see cref="XElement"/> and all of it's descendant <see cref="XElement"/>s
        /// as an <see cref="IEnumerable"/> of <see cref="XElement"/>.
        /// <seealso cref="XElement.DescendantsAndSelf()"/>
        /// </summary>
        /// <returns>
        /// An <see cref="IEnumerable"/> of <see cref="XElement"/> containing this <see cref="XElement"/>
        /// and all of it's descendants.
        /// </returns>
        public IEnumerable<XElement> DescendantsAndSelf()
        {
            return GetDescendants(null, true);
        }
 
        /// <summary>
        /// Returns the descendants of this <see cref="XElement"/> that have a matching <see cref="XName"/>
        /// to the one passed in, including, potentially, this <see cref="XElement"/>.
        /// <seealso cref="XElement.DescendantsAndSelf(XName)"/>
        /// </summary>
        /// <param name="name">
        /// The <see cref="XName"/> of the descendant <see cref="XElement"/> that is being targeted.
        /// </param>
        /// <returns>
        /// An <see cref="IEnumerable"/> of <see cref="XElement"/> containing all of the descendant
        /// <see cref="XElement"/>s that have this <see cref="XName"/>.
        /// </returns>
        public IEnumerable<XElement> DescendantsAndSelf(XName? name)
        {
            return name != null ? GetDescendants(name, true) : XElement.EmptySequence;
        }
 
        /// <summary>
        /// Returns the default <see cref="XNamespace"/> of an <see cref="XElement"/>
        /// </summary>
        public XNamespace GetDefaultNamespace()
        {
            string? namespaceName = GetNamespaceOfPrefixInScope("xmlns", null);
            return namespaceName != null ? XNamespace.Get(namespaceName) : XNamespace.None;
        }
 
        /// <summary>
        /// Get the namespace associated with a particular prefix for this <see cref="XElement"/>
        /// in its document context.
        /// </summary>
        /// <param name="prefix">The namespace prefix to look up</param>
        /// <returns>An <see cref="XNamespace"/> for the namespace bound to the prefix</returns>
        public XNamespace? GetNamespaceOfPrefix(string prefix)
        {
            ArgumentException.ThrowIfNullOrEmpty(prefix);
            if (prefix == "xmlns") return XNamespace.Xmlns;
            string? namespaceName = GetNamespaceOfPrefixInScope(prefix, null);
            if (namespaceName != null) return XNamespace.Get(namespaceName);
            if (prefix == "xml") return XNamespace.Xml;
            return null;
        }
 
        /// <summary>
        /// Get the prefix associated with a namespace for an element in its context.
        /// </summary>
        /// <param name="ns">The <see cref="XNamespace"/> for which to get a prefix</param>
        /// <returns>The namespace prefix string</returns>
        public string? GetPrefixOfNamespace(XNamespace ns)
        {
            ArgumentNullException.ThrowIfNull(ns);
 
            string namespaceName = ns.NamespaceName;
            bool hasInScopeNamespace = false;
            XElement? e = this;
            do
            {
                XAttribute? a = e.lastAttr;
                if (a != null)
                {
                    bool hasLocalNamespace = false;
                    do
                    {
                        a = a.next!;
                        if (a.IsNamespaceDeclaration)
                        {
                            if (a.Value == namespaceName)
                            {
                                if (a.Name.NamespaceName.Length != 0 &&
                                    (!hasInScopeNamespace ||
                                     GetNamespaceOfPrefixInScope(a.Name.LocalName, e) == null))
                                {
                                    return a.Name.LocalName;
                                }
                            }
                            hasLocalNamespace = true;
                        }
                    }
                    while (a != e.lastAttr);
                    hasInScopeNamespace |= hasLocalNamespace;
                }
                e = e.parent as XElement;
            }
            while (e != null);
            if ((object)namespaceName == (object)XNamespace.xmlPrefixNamespace)
            {
                if (!hasInScopeNamespace || GetNamespaceOfPrefixInScope("xml", null) == null) return "xml";
            }
            else if ((object)namespaceName == (object)XNamespace.xmlnsPrefixNamespace)
            {
                return "xmlns";
            }
            return null;
        }
 
        /// <overloads>
        /// The Load method provides multiple strategies for creating a new
        /// <see cref="XElement"/> and initializing it from a data source containing
        /// raw XML.  Load from a file (passing in a URI to the file), an
        /// <see cref="Stream"/>, a <see cref="TextReader"/>, or an
        /// <see cref="XmlReader"/>.  Note:  Use <see cref="XDocument.Parse(string)"/>
        /// to create an <see cref="XDocument"/> from a string containing XML.
        /// <seealso cref="XDocument.Load(string)" />
        /// <seealso cref="XElement.Parse(string)"/>
        /// </overloads>
        /// <summary>
        /// Create a new <see cref="XElement"/> based on the contents of the file
        /// referenced by the URI parameter passed in.  Note: Use
        /// <see cref="XElement.Parse(string)"/> to create an <see cref="XElement"/> from
        /// a string containing XML.
        /// <seealso cref="XmlReader.Create(string)"/>
        /// <seealso cref="XElement.Parse(string)"/>
        /// <seealso cref="XDocument.Parse(string)"/>
        /// </summary>
        /// <remarks>
        /// This method uses the <see cref="XmlReader.Create(string)"/> method to create
        /// an <see cref="XmlReader"/> to read the raw XML into the underlying
        /// XML tree.
        /// </remarks>
        /// <param name="uri">
        /// A URI string referencing the file to load into a new <see cref="XElement"/>.
        /// </param>
        /// <returns>
        /// An <see cref="XElement"/> initialized with the contents of the file referenced
        /// in the passed in uri parameter.
        /// </returns>
        public static XElement Load([StringSyntax(StringSyntaxAttribute.Uri)] string uri)
        {
            return Load(uri, LoadOptions.None);
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> based on the contents of the file
        /// referenced by the URI parameter passed in.  Optionally, whitespace can be preserved.
        /// <see cref="XmlReader.Create(string)"/>
        /// <seealso cref="XDocument.Load(string, LoadOptions)"/>
        /// </summary>
        /// <remarks>
        /// This method uses the <see cref="XmlReader.Create(string)"/> method to create
        /// an <see cref="XmlReader"/> to read the raw XML into an underlying
        /// XML tree. If LoadOptions.PreserveWhitespace is enabled then
        /// the <see cref="XmlReaderSettings"/> property <see cref="XmlReaderSettings.IgnoreWhitespace"/>
        /// is set to false.
        /// </remarks>
        /// <param name="uri">
        /// A string representing the URI of the file to be loaded into a new <see cref="XElement"/>.
        /// </param>
        /// <param name="options">
        /// A set of <see cref="LoadOptions"/>.
        /// </param>
        /// <returns>
        /// An <see cref="XElement"/> initialized with the contents of the file referenced
        /// in the passed uri parameter.  If LoadOptions.PreserveWhitespace is enabled then
        /// significant whitespace will be preserved.
        /// </returns>
        public static XElement Load([StringSyntax(StringSyntaxAttribute.Uri)] string uri, LoadOptions options)
        {
            XmlReaderSettings rs = GetXmlReaderSettings(options);
            using (XmlReader r = XmlReader.Create(uri, rs))
            {
                return Load(r, options);
            }
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> and initialize its underlying XML tree using
        /// the passed <see cref="Stream"/> parameter.
        /// </summary>
        /// <param name="stream">
        /// A <see cref="Stream"/> containing the raw XML to read into the newly
        /// created <see cref="XElement"/>.
        /// </param>
        /// <returns>
        /// A new <see cref="XElement"/> containing the contents of the passed in
        /// <see cref="Stream"/>.
        /// </returns>
        public static XElement Load(Stream stream)
        {
            return Load(stream, LoadOptions.None);
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> and initialize its underlying XML tree using
        /// the passed <see cref="Stream"/> parameter.  Optionally whitespace handling
        /// can be preserved.
        /// </summary>
        /// <remarks>
        /// If LoadOptions.PreserveWhitespace is enabled then
        /// the <see cref="XmlReaderSettings"/> property <see cref="XmlReaderSettings.IgnoreWhitespace"/>
        /// is set to false.
        /// </remarks>
        /// <param name="stream">
        /// A <see cref="Stream"/> containing the raw XML to read into the newly
        /// created <see cref="XElement"/>.
        /// </param>
        /// <param name="options">
        /// A set of <see cref="LoadOptions"/>.
        /// </param>
        /// <returns>
        /// A new <see cref="XElement"/> containing the contents of the passed in
        /// <see cref="Stream"/>.
        /// </returns>
        public static XElement Load(Stream stream, LoadOptions options)
        {
            XmlReaderSettings rs = GetXmlReaderSettings(options);
            using (XmlReader r = XmlReader.Create(stream, rs))
            {
                return Load(r, options);
            }
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> and initialize its underlying XML tree using
        /// the passed <see cref="Stream"/> parameter.  Optionally whitespace handling
        /// can be preserved.
        /// </summary>
        /// <remarks>
        /// If LoadOptions.PreserveWhitespace is enabled then
        /// the <see cref="XmlReaderSettings"/> property <see cref="XmlReaderSettings.IgnoreWhitespace"/>
        /// is set to false.
        /// </remarks>
        /// <param name="stream">
        /// A <see cref="Stream"/> containing the raw XML to read into the newly
        /// created <see cref="XElement"/>.
        /// </param>
        /// <param name="options">
        /// A set of <see cref="LoadOptions"/>.
        /// </param>
        /// <param name="cancellationToken">
        /// A cancellation token.</param>
        /// <returns>
        /// A new <see cref="XElement"/> containing the contents of the passed in
        /// <see cref="Stream"/>.
        /// </returns>
        public static async Task<XElement> LoadAsync(Stream stream, LoadOptions options, CancellationToken cancellationToken)
        {
            XmlReaderSettings rs = GetXmlReaderSettings(options);
 
            rs.Async = true;
 
            using (XmlReader r = XmlReader.Create(stream, rs))
            {
                return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false);
            }
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> and initialize its underlying XML tree using
        /// the passed <see cref="TextReader"/> parameter.
        /// </summary>
        /// <param name="textReader">
        /// A <see cref="TextReader"/> containing the raw XML to read into the newly
        /// created <see cref="XElement"/>.
        /// </param>
        /// <returns>
        /// A new <see cref="XElement"/> containing the contents of the passed in
        /// <see cref="TextReader"/>.
        /// </returns>
        public static XElement Load(TextReader textReader)
        {
            return Load(textReader, LoadOptions.None);
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> and initialize its underlying XML tree using
        /// the passed <see cref="TextReader"/> parameter.  Optionally whitespace handling
        /// can be preserved.
        /// </summary>
        /// <remarks>
        /// If LoadOptions.PreserveWhitespace is enabled then
        /// the <see cref="XmlReaderSettings"/> property <see cref="XmlReaderSettings.IgnoreWhitespace"/>
        /// is set to false.
        /// </remarks>
        /// <param name="textReader">
        /// A <see cref="TextReader"/> containing the raw XML to read into the newly
        /// created <see cref="XElement"/>.
        /// </param>
        /// <param name="options">
        /// A set of <see cref="LoadOptions"/>.
        /// </param>
        /// <returns>
        /// A new <see cref="XElement"/> containing the contents of the passed in
        /// <see cref="TextReader"/>.
        /// </returns>
        public static XElement Load(TextReader textReader, LoadOptions options)
        {
            XmlReaderSettings rs = GetXmlReaderSettings(options);
            using (XmlReader r = XmlReader.Create(textReader, rs))
            {
                return Load(r, options);
            }
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> and initialize its underlying XML tree using
        /// the passed <see cref="TextReader"/> parameter.  Optionally whitespace handling
        /// can be preserved.
        /// </summary>
        /// <remarks>
        /// If LoadOptions.PreserveWhitespace is enabled then
        /// the <see cref="XmlReaderSettings"/> property <see cref="XmlReaderSettings.IgnoreWhitespace"/>
        /// is set to false.
        /// </remarks>
        /// <param name="textReader">
        /// A <see cref="TextReader"/> containing the raw XML to read into the newly
        /// created <see cref="XElement"/>.
        /// </param>
        /// <param name="options">
        /// A set of <see cref="LoadOptions"/>.
        /// </param>
        /// <param name="cancellationToken">
        /// A cancellation token.</param>
        /// <returns>
        /// A new <see cref="XElement"/> containing the contents of the passed in
        /// <see cref="TextReader"/>.
        /// </returns>
        public static async Task<XElement> LoadAsync(TextReader textReader, LoadOptions options, CancellationToken cancellationToken)
        {
            XmlReaderSettings rs = GetXmlReaderSettings(options);
 
            rs.Async = true;
 
            using (XmlReader r = XmlReader.Create(textReader, rs))
            {
                return await LoadAsync(r, options, cancellationToken).ConfigureAwait(false);
            }
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> containing the contents of the
        /// passed in <see cref="XmlReader"/>.
        /// </summary>
        /// <param name="reader">
        /// An <see cref="XmlReader"/> containing the XML to be read into the new
        /// <see cref="XElement"/>.
        /// </param>
        /// <returns>
        /// A new <see cref="XElement"/> containing the contents of the passed
        /// in <see cref="XmlReader"/>.
        /// </returns>
        public static XElement Load(XmlReader reader)
        {
            return Load(reader, LoadOptions.None);
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> containing the contents of the
        /// passed in <see cref="XmlReader"/>.
        /// </summary>
        /// <param name="reader">
        /// An <see cref="XmlReader"/> containing the XML to be read into the new
        /// <see cref="XElement"/>.
        /// </param>
        /// <param name="options">
        /// A set of <see cref="LoadOptions"/>.
        /// </param>
        /// <returns>
        /// A new <see cref="XElement"/> containing the contents of the passed
        /// in <see cref="XmlReader"/>.
        /// </returns>
        public static XElement Load(XmlReader reader, LoadOptions options)
        {
            ArgumentNullException.ThrowIfNull(reader);
 
            if (reader.MoveToContent() != XmlNodeType.Element) throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ExpectedNodeType, XmlNodeType.Element, reader.NodeType));
            XElement e = new XElement(reader, options);
            reader.MoveToContent();
            if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile);
            return e;
        }
 
        /// <summary>
        /// Create a new <see cref="XElement"/> containing the contents of the
        /// passed in <see cref="XmlReader"/>.
        /// </summary>
        /// <param name="reader">
        /// An <see cref="XmlReader"/> containing the XML to be read into the new
        /// <see cref="XElement"/>.
        /// </param>
        /// <param name="options">
        /// A set of <see cref="LoadOptions"/>.
        /// </param>
        /// <param name="cancellationToken">
        /// A cancellation token.</param>
        /// <returns>
        /// A new <see cref="XElement"/> containing the contents of the passed
        /// in <see cref="XmlReader"/>.
        /// </returns>
        public static Task<XElement> LoadAsync(XmlReader reader, LoadOptions options, CancellationToken cancellationToken)
        {
            ArgumentNullException.ThrowIfNull(reader);
 
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<XElement>(cancellationToken);
            return LoadAsyncInternal(reader, options, cancellationToken);
        }
 
        private static async Task<XElement> LoadAsyncInternal(XmlReader reader, LoadOptions options, CancellationToken cancellationToken)
        {
            if (await reader.MoveToContentAsync().ConfigureAwait(false) != XmlNodeType.Element) throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ExpectedNodeType, XmlNodeType.Element, reader.NodeType));
 
            XElement e = new XElement(default(AsyncConstructionSentry));
            await e.ReadElementFromAsync(reader, options, cancellationToken).ConfigureAwait(false);
 
            cancellationToken.ThrowIfCancellationRequested();
            await reader.MoveToContentAsync().ConfigureAwait(false);
 
            if (!reader.EOF) throw new InvalidOperationException(SR.InvalidOperation_ExpectedEndOfFile);
            return e;
        }
 
        /// <overloads>
        /// Parses a string containing XML into an <see cref="XElement"/>.  Optionally
        /// whitespace can be preserved.
        /// </overloads>
        /// <summary>
        /// Parses a string containing XML into an <see cref="XElement"/>.
        /// </summary>
        /// <remarks>
        /// The XML must contain only one root node.
        /// </remarks>
        /// <param name="text">
        /// A string containing the XML to parse into an <see cref="XElement"/>.
        /// </param>
        /// <returns>
        /// An <see cref="XElement"/> created from the XML string passed in.
        /// </returns>
        public static XElement Parse(string text)
        {
            return Parse(text, LoadOptions.None);
        }
 
        /// <summary>
        /// Parses a string containing XML into an <see cref="XElement"/> and optionally
        /// preserves the Whitespace. See <see cref="XmlReaderSettings.IgnoreWhitespace"/>.
        /// </summary>
        /// <remarks>
        /// <list>
        /// <item>The XML must contain only one root node.</item>
        /// <item>
        /// If LoadOptions.PreserveWhitespace is enabled the underlying
        /// <see cref="XmlReaderSettings"/>'
        /// property <see cref="XmlReaderSettings.IgnoreWhitespace"/> will be set to false.
        /// </item>
        /// </list>
        /// </remarks>
        /// <param name="text">
        /// A string containing the XML to parse into an <see cref="XElement"/>.
        /// </param>
        /// <param name="options">
        /// A set of <see cref="LoadOptions"/>.
        /// </param>
        /// <returns>
        /// An <see cref="XElement"/> created from the XML string passed in.
        /// </returns>
        public static XElement Parse(string text, LoadOptions options)
        {
            using (StringReader sr = new StringReader(text))
            {
                XmlReaderSettings rs = GetXmlReaderSettings(options);
                using (XmlReader r = XmlReader.Create(sr, rs))
                {
                    return Load(r, options);
                }
            }
        }
 
        /// <summary>
        /// Removes content and attributes from this <see cref="XElement"/>.
        /// <seealso cref="XElement.RemoveAttributes"/>
        /// <seealso cref="XContainer.RemoveNodes"/>
        /// </summary>
        public void RemoveAll()
        {
            RemoveAttributes();
            RemoveNodes();
        }
 
        /// <summary>
        /// Removes that attributes of this <see cref="XElement"/>.
        /// <seealso cref="XElement.RemoveAll"/>
        /// <seealso cref="XElement.RemoveAttributes"/>
        /// </summary>
        public void RemoveAttributes()
        {
            if (SkipNotify())
            {
                RemoveAttributesSkipNotify();
                return;
            }
            while (lastAttr != null)
            {
                XAttribute a = lastAttr.next!;
                NotifyChanging(a, XObjectChangeEventArgs.Remove);
                if (lastAttr == null || a != lastAttr.next) throw new InvalidOperationException(SR.InvalidOperation_ExternalCode);
                if (a != lastAttr)
                {
                    lastAttr.next = a.next;
                }
                else
                {
                    lastAttr = null;
                }
                a.parent = null;
                a.next = null;
                NotifyChanged(a, XObjectChangeEventArgs.Remove);
            }
        }
 
        /// <overloads>
        /// Replaces the child nodes and the attributes of this element with the
        /// specified content. The content can be simple content, a collection of
        /// content objects, a parameter list of content objects, or null.
        /// </overloads>
        /// <summary>
        /// Replaces the children nodes and the attributes of this element with the specified content.
        /// </summary>
        /// <param name="content">
        /// The content that will replace the child nodes and attributes of this element.
        /// </param>
        /// <remarks>
        /// See XContainer.Add(object content) for details about the content that can be added
        /// using this method.
        /// </remarks>
        public void ReplaceAll(object? content)
        {
            content = GetContentSnapshot(content);
            RemoveAll();
            Add(content);
        }
 
        /// <summary>
        /// Replaces the children nodes and the attributes of this element with the specified content.
        /// </summary>
        /// <param name="content">
        /// A parameter list of content objects.
        /// </param>
        /// <remarks>
        /// See XContainer.Add(object content) for details about the content that can be added
        /// using this method.
        /// </remarks>
        public void ReplaceAll(params object?[] content)
        {
            ReplaceAll((object)content);
        }
 
        /// <overloads>
        /// Replaces the attributes of this element with the specified content.
        /// The content can be simple content, a collection of
        /// content objects, a parameter list of content objects, or null.
        /// </overloads>
        /// <summary>
        /// Replaces the attributes of this element with the specified content.
        /// </summary>
        /// <param name="content">
        /// The content that will replace the attributes of this element.
        /// </param>
        /// <remarks>
        /// See XContainer.Add(object content) for details about the content that can be added
        /// using this method.
        /// </remarks>
        public void ReplaceAttributes(object? content)
        {
            content = GetContentSnapshot(content);
            RemoveAttributes();
            Add(content);
        }
 
        /// <summary>
        /// Replaces the attributes of this element with the specified content.
        /// </summary>
        /// <param name="content">
        /// A parameter list of content objects.
        /// </param>
        /// <remarks>
        /// See XContainer.Add(object content) for details about the content that can be added
        /// using this method.
        /// </remarks>
        public void ReplaceAttributes(params object?[] content)
        {
            ReplaceAttributes((object)content);
        }
 
 
        /// <summary>
        /// Output this <see cref="XElement"/> to the passed in <see cref="Stream"/>.
        /// </summary>
        /// <remarks>
        /// The format will be indented by default.  If you want
        /// no indenting then use the SaveOptions version of Save (see
        /// <see cref="XElement.Save(Stream, SaveOptions)"/>) enabling
        /// SaveOptions.DisableFormatting.
        /// There is also an option SaveOptions.OmitDuplicateNamespaces for removing duplicate namespace declarations.
        /// Or instead use the SaveOptions as an annotation on this node or its ancestors, then this method will use those options.
        /// </remarks>
        /// <param name="stream">
        /// The <see cref="Stream"/> to output this <see cref="XElement"/> to.
        /// </param>
        public void Save(Stream stream)
        {
            Save(stream, GetSaveOptionsFromAnnotations());
        }
 
        /// <summary>
        /// Output this <see cref="XElement"/> to a <see cref="Stream"/>.
        /// </summary>
        /// <param name="stream">
        /// The <see cref="Stream"/> to output the XML to.
        /// </param>
        /// <param name="options">
        /// If SaveOptions.DisableFormatting is enabled the output is not indented.
        /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed.
        /// </param>
        public void Save(Stream stream, SaveOptions options)
        {
            XmlWriterSettings ws = GetXmlWriterSettings(options);
            using (XmlWriter w = XmlWriter.Create(stream, ws))
            {
                Save(w);
            }
        }
 
        /// <summary>
        /// Output this <see cref="XElement"/> to a <see cref="Stream"/>.
        /// </summary>
        /// <param name="stream">
        /// The <see cref="Stream"/> to output the XML to.
        /// </param>
        /// <param name="options">
        /// If SaveOptions.DisableFormatting is enabled the output is not indented.
        /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed.
        /// </param>
        /// <param name="cancellationToken">A cancellation token.</param>
        public async Task SaveAsync(Stream stream, SaveOptions options, CancellationToken cancellationToken)
        {
            XmlWriterSettings ws = GetXmlWriterSettings(options);
 
            ws.Async = true;
 
            XmlWriter w = XmlWriter.Create(stream, ws);
            await using (w.ConfigureAwait(false))
            {
                await SaveAsync(w, cancellationToken).ConfigureAwait(false);
            }
        }
 
        /// <summary>
        /// Output this <see cref="XElement"/> to the passed in <see cref="TextWriter"/>.
        /// </summary>
        /// <remarks>
        /// The format will be indented by default.  If you want
        /// no indenting then use the SaveOptions version of Save (see
        /// <see cref="XElement.Save(TextWriter, SaveOptions)"/>) enabling
        /// SaveOptions.DisableFormatting.
        /// There is also an option SaveOptions.OmitDuplicateNamespaces for removing duplicate namespace declarations.
        /// Or instead use the SaveOptions as an annotation on this node or its ancestors, then this method will use those options.
        /// </remarks>
        /// <param name="textWriter">
        /// The <see cref="TextWriter"/> to output this <see cref="XElement"/> to.
        /// </param>
        public void Save(TextWriter textWriter)
        {
            Save(textWriter, GetSaveOptionsFromAnnotations());
        }
 
        /// <summary>
        /// Output this <see cref="XElement"/> to a <see cref="TextWriter"/>.
        /// </summary>
        /// <param name="textWriter">
        /// The <see cref="TextWriter"/> to output the XML to.
        /// </param>
        /// <param name="options">
        /// If SaveOptions.DisableFormatting is enabled the output is not indented.
        /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed.
        /// </param>
        public void Save(TextWriter textWriter, SaveOptions options)
        {
            XmlWriterSettings ws = GetXmlWriterSettings(options);
            using (XmlWriter w = XmlWriter.Create(textWriter, ws))
            {
                Save(w);
            }
        }
 
        /// <summary>
        /// Output this <see cref="XElement"/> to a <see cref="TextWriter"/>.
        /// </summary>
        /// <param name="textWriter">
        /// The <see cref="TextWriter"/> to output the XML to.
        /// </param>
        /// <param name="options">
        /// If SaveOptions.DisableFormatting is enabled the output is not indented.
        /// If SaveOptions.OmitDuplicateNamespaces is enabled duplicate namespace declarations will be removed.
        /// </param>
        /// <param name="cancellationToken">A cancellation token.</param>
        public async Task SaveAsync(TextWriter textWriter, SaveOptions options, CancellationToken cancellationToken)
        {
            XmlWriterSettings ws = GetXmlWriterSettings(options);
 
            ws.Async = true;
 
            XmlWriter w = XmlWriter.Create(textWriter, ws);
            await using (w.ConfigureAwait(false))
            {
                await SaveAsync(w, cancellationToken).ConfigureAwait(false);
            }
        }
 
        /// <summary>
        /// Output this <see cref="XElement"/> to an <see cref="XmlWriter"/>.
        /// </summary>
        /// <param name="writer">
        /// The <see cref="XmlWriter"/> to output the XML to.
        /// </param>
        public void Save(XmlWriter writer)
        {
            ArgumentNullException.ThrowIfNull(writer);
 
            writer.WriteStartDocument();
            WriteTo(writer);
            writer.WriteEndDocument();
        }
 
        /// <summary>
        /// Output this <see cref="XElement"/> to an <see cref="XmlWriter"/>.
        /// </summary>
        /// <param name="writer">
        /// The <see cref="XmlWriter"/> to output the XML to.
        /// </param>
        /// <param name="cancellationToken">A cancellation token.</param>
        public Task SaveAsync(XmlWriter writer, CancellationToken cancellationToken)
        {
            ArgumentNullException.ThrowIfNull(writer);
 
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled(cancellationToken);
            return SaveAsyncInternal(writer, cancellationToken);
        }
 
        private async Task SaveAsyncInternal(XmlWriter writer, CancellationToken cancellationToken)
        {
            await writer.WriteStartDocumentAsync().ConfigureAwait(false);
 
            await WriteToAsync(writer, cancellationToken).ConfigureAwait(false);
 
            cancellationToken.ThrowIfCancellationRequested();
            await writer.WriteEndDocumentAsync().ConfigureAwait(false);
        }
 
        /// <summary>
        /// Sets the value of an attribute. The value is assigned to the attribute with the given
        /// name. If no attribute with the given name exists, a new attribute is added. If the
        /// value is null, the attribute with the given name, if any, is deleted.
        /// <seealso cref="XAttribute.SetValue"/>
        /// <seealso cref="XElement.SetElementValue"/>
        /// <seealso cref="XElement.SetValue"/>
        /// </summary>
        /// <param name="name">
        /// The name of the attribute whose value to change.
        /// </param>
        /// <param name="value">
        /// The value to assign to the attribute. The attribute is deleted if the value is null.
        /// Otherwise, the value is converted to its string representation and assigned to the
        /// <see cref="Value"/> property of the attribute.
        /// </param>
        /// <exception cref="ArgumentException">
        /// Thrown if the value is an instance of <see cref="XObject"/>.
        /// </exception>
        public void SetAttributeValue(XName name, object? value)
        {
            XAttribute? a = Attribute(name);
            if (value == null)
            {
                if (a != null) RemoveAttribute(a);
            }
            else
            {
                if (a != null)
                {
                    a.Value = GetStringValue(value);
                }
                else
                {
                    AppendAttribute(new XAttribute(name, value));
                }
            }
        }
 
        /// <summary>
        /// Sets the value of a child element. The value is assigned to the first child element
        /// with the given name. If no child element with the given name exists, a new child
        /// element is added. If the value is null, the first child element with the given name,
        /// if any, is deleted.
        /// <seealso cref="XAttribute.SetValue"/>
        /// <seealso cref="XElement.SetAttributeValue"/>
        /// <seealso cref="XElement.SetValue"/>
        /// </summary>
        /// <param name="name">
        /// The name of the child element whose value to change.
        /// </param>
        /// <param name="value">
        /// The value to assign to the child element. The child element is deleted if the value
        /// is null. Otherwise, the value is converted to its string representation and assigned
        /// to the <see cref="Value"/> property of the child element.
        /// </param>
        /// <exception cref="ArgumentException">
        /// Thrown if the value is an instance of <see cref="XObject"/>.
        /// </exception>
        public void SetElementValue(XName name, object? value)
        {
            XElement? e = Element(name);
            if (value == null)
            {
                if (e != null) RemoveNode(e);
            }
            else
            {
                if (e != null)
                {
                    e.Value = GetStringValue(value);
                }
                else
                {
                    AddNode(new XElement(name, GetStringValue(value)));
                }
            }
        }
 
        /// <summary>
        /// Sets the value of this element.
        /// <seealso cref="XAttribute.SetValue"/>
        /// <seealso cref="XElement.SetAttributeValue"/>
        /// <seealso cref="XElement.SetElementValue"/>
        /// </summary>
        /// <param name="value">
        /// The value to assign to this element. The value is converted to its string representation
        /// and assigned to the <see cref="Value"/> property.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown if the specified value is null.
        /// </exception>
        public void SetValue(object value)
        {
            ArgumentNullException.ThrowIfNull(value);
 
            Value = GetStringValue(value);
        }
 
        /// <summary>
        /// Write this <see cref="XElement"/> to the passed in <see cref="XmlWriter"/>.
        /// </summary>
        /// <param name="writer">
        /// The <see cref="XmlWriter"/> to write this <see cref="XElement"/> to.
        /// </param>
        public override void WriteTo(XmlWriter writer)
        {
            ArgumentNullException.ThrowIfNull(writer);
 
            new ElementWriter(writer).WriteElement(this);
        }
 
        /// <summary>
        /// Write this <see cref="XElement"/> to the passed in <see cref="XmlTextWriter"/>.
        /// </summary>
        /// <param name="writer">
        /// The <see cref="XmlTextWriter"/> to write this <see cref="XElement"/> to.
        /// </param>
        /// <param name="cancellationToken">A cancellation token.</param>
        public override Task WriteToAsync(XmlWriter writer, CancellationToken cancellationToken)
        {
            ArgumentNullException.ThrowIfNull(writer);
 
            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled(cancellationToken);
            return new ElementWriter(writer).WriteElementAsync(this, cancellationToken);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="string"/>.
        /// </summary>
        /// <remarks>
        /// If the <see cref="XElement"/> is a subtree (an <see cref="XElement"/>
        /// that has <see cref="XElement"/> children.  The concatenated string
        /// value of all of the <see cref="XElement"/>'s text and descendants
        /// text is returned.
        /// </remarks>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to a string.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="string"/>.
        /// </returns>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator string?(XElement? element)
        {
            if (element == null) return null;
            return element.Value;
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="bool"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="bool"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="bool"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the element does not contain a valid boolean value.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator bool(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToBoolean(element.Value.ToLowerInvariant());
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="bool"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="bool"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="bool"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the element does not contain a valid boolean value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator bool?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToBoolean(element.Value.ToLowerInvariant());
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="int"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="int"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="int"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the element does not contain a valid integer value.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator int(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToInt32(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="int"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="int"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="int"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid integer value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator int?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToInt32(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="uint"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="uint"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="uint"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid unsigned integer value.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator uint(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToUInt32(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="uint"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="uint"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="uint"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid unsigned integer value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator uint?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToUInt32(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="long"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="long"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="long"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the element does not contain a valid long integer value.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator long(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToInt64(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="long"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="long"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="long"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid long integer value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator long?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToInt64(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="ulong"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="ulong"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="ulong"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid unsigned long integer value.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator ulong(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToUInt64(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="ulong"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="ulong"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="ulong"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid unsigned long integer value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator ulong?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToUInt64(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="float"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="float"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="float"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid float value.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator float(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToSingle(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="float"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="float"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="float"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid float value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator float?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToSingle(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="double"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="double"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="double"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid double value.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator double(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToDouble(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="double"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="double"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="double"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid double value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator double?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToDouble(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="decimal"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="decimal"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="decimal"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid decimal value.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator decimal(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToDecimal(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="decimal"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="decimal"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="decimal"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid decimal value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator decimal?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToDecimal(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="DateTime"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="DateTime"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="DateTime"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid <see cref="DateTime"/> value.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator DateTime(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return DateTime.Parse(element.Value, CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="DateTime"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="DateTime"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="DateTime"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid <see cref="DateTime"/> value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator DateTime?(XElement? element)
        {
            if (element == null) return null;
            return DateTime.Parse(element.Value, CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="DateTimeOffset"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="DateTimeOffset"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="DateTimeOffset"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid <see cref="DateTimeOffset"/> value.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator DateTimeOffset(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToDateTimeOffset(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="DateTimeOffset"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="DateTimeOffset"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="DateTimeOffset"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid <see cref="DateTimeOffset"/> value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator DateTimeOffset?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToDateTimeOffset(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="TimeSpan"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="TimeSpan"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="TimeSpan"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid <see cref="TimeSpan"/> value.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator TimeSpan(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToTimeSpan(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="TimeSpan"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="TimeSpan"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="TimeSpan"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid <see cref="TimeSpan"/> value.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator TimeSpan?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToTimeSpan(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to a <see cref="Guid"/>.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="Guid"/>.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="Guid"/>.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid guid.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the specified element is null.
        /// </exception>
        [CLSCompliant(false)]
        public static explicit operator Guid(XElement element)
        {
            ArgumentNullException.ThrowIfNull(element);
 
            return XmlConvert.ToGuid(element.Value);
        }
 
        /// <summary>
        /// Cast the value of this <see cref="XElement"/> to an <see cref="Guid"/>?.
        /// </summary>
        /// <param name="element">
        /// The <see cref="XElement"/> to cast to <see cref="Guid"/>?.
        /// </param>
        /// <returns>
        /// The content of this <see cref="XElement"/> as a <see cref="Guid"/>?.
        /// </returns>
        /// <exception cref="System.FormatException">
        /// Thrown if the specified element does not contain a valid guid.
        /// </exception>
        [CLSCompliant(false)]
        [return: NotNullIfNotNull(nameof(element))]
        public static explicit operator Guid?(XElement? element)
        {
            if (element == null) return null;
            return XmlConvert.ToGuid(element.Value);
        }
 
        /// <summary>
        /// This method is obsolete for the IXmlSerializable contract.
        /// </summary>
        XmlSchema? IXmlSerializable.GetSchema()
        {
            return null;
        }
 
        /// <summary>
        /// Generates a <see cref="XElement"/> from its XML representation.
        /// </summary>
        /// <param name="reader">
        /// The <see cref="XmlReader"/> stream from which the <see cref="XElement"/>
        /// is deserialized.
        /// </param>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            ArgumentNullException.ThrowIfNull(reader);
 
            if (parent != null || annotations != null || content != null || lastAttr != null) throw new InvalidOperationException(SR.InvalidOperation_DeserializeInstance);
            if (reader.MoveToContent() != XmlNodeType.Element) throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ExpectedNodeType, XmlNodeType.Element, reader.NodeType));
            ReadElementFrom(reader, LoadOptions.None);
        }
 
        /// <summary>
        /// Converts a <see cref="XElement"/> into its XML representation.
        /// </summary>
        /// <param name="writer">
        /// The <see cref="XmlWriter"/> stream to which the <see cref="XElement"/>
        /// is serialized.
        /// </param>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            WriteTo(writer);
        }
 
        internal override void AddAttribute(XAttribute a)
        {
            if (Attribute(a.Name) != null) throw new InvalidOperationException(SR.InvalidOperation_DuplicateAttribute);
            if (a.parent != null) a = new XAttribute(a);
            AppendAttribute(a);
        }
 
        internal override void AddAttributeSkipNotify(XAttribute a)
        {
            if (Attribute(a.Name) != null) throw new InvalidOperationException(SR.InvalidOperation_DuplicateAttribute);
            if (a.parent != null) a = new XAttribute(a);
            AppendAttributeSkipNotify(a);
        }
 
        internal void AppendAttribute(XAttribute a)
        {
            bool notify = NotifyChanging(a, XObjectChangeEventArgs.Add);
            if (a.parent != null) throw new InvalidOperationException(SR.InvalidOperation_ExternalCode);
            AppendAttributeSkipNotify(a);
            if (notify) NotifyChanged(a, XObjectChangeEventArgs.Add);
        }
 
        internal void AppendAttributeSkipNotify(XAttribute a)
        {
            a.parent = this;
            if (lastAttr == null)
            {
                a.next = a;
            }
            else
            {
                a.next = lastAttr.next;
                lastAttr.next = a;
            }
            lastAttr = a;
        }
 
        private bool AttributesEqual(XElement e)
        {
            XAttribute? a1 = lastAttr;
            XAttribute? a2 = e.lastAttr;
            if (a1 != null && a2 != null)
            {
                do
                {
                    a1 = a1.next!;
                    a2 = a2.next!;
                    if (a1.name != a2.name || a1.value != a2.value) return false;
                } while (a1 != lastAttr);
                return a2 == e.lastAttr;
            }
            return a1 == null && a2 == null;
        }
 
        internal override XNode CloneNode()
        {
            return new XElement(this);
        }
 
        internal override bool DeepEquals(XNode node)
        {
            XElement? e = node as XElement;
            return e != null && name == e.name && ContentsEqual(e) && AttributesEqual(e);
        }
 
        private IEnumerable<XAttribute> GetAttributes(XName? name)
        {
            XAttribute? a = lastAttr;
            if (a != null)
            {
                do
                {
                    a = a.next!;
                    if (name == null || a.name == name) yield return a;
                } while (a.parent == this && a != lastAttr);
            }
        }
 
        private string? GetNamespaceOfPrefixInScope(string prefix, XElement? outOfScope)
        {
            XElement? e = this;
            while (e != outOfScope)
            {
                Debug.Assert(e != null);
                XAttribute? a = e.lastAttr;
                if (a != null)
                {
                    do
                    {
                        a = a.next!;
                        if (a.IsNamespaceDeclaration && a.Name.LocalName == prefix) return a.Value;
                    }
                    while (a != e.lastAttr);
                }
                e = e.parent as XElement;
            }
            return null;
        }
 
        internal override int GetDeepHashCode()
        {
            int h = name.GetHashCode();
            h ^= ContentsHashCode();
            XAttribute? a = lastAttr;
            if (a != null)
            {
                do
                {
                    a = a.next!;
                    h ^= a.GetDeepHashCode();
                } while (a != lastAttr);
            }
            return h;
        }
 
        private void ReadElementFrom(XmlReader r, LoadOptions o)
        {
            ReadElementFromImpl(r, o);
 
            if (!r.IsEmptyElement)
            {
                r.Read();
                ReadContentFrom(r, o);
            }
 
            r.Read();
        }
 
        private async Task ReadElementFromAsync(XmlReader r, LoadOptions o, CancellationToken cancellationTokentoken)
        {
            ReadElementFromImpl(r, o);
 
            if (!r.IsEmptyElement)
            {
                cancellationTokentoken.ThrowIfCancellationRequested();
                await r.ReadAsync().ConfigureAwait(false);
 
                await ReadContentFromAsync(r, o, cancellationTokentoken).ConfigureAwait(false);
            }
 
            cancellationTokentoken.ThrowIfCancellationRequested();
            await r.ReadAsync().ConfigureAwait(false);
        }
 
        /// <summary>
        /// Shared implementation between ReadElementFrom / ReadElementFromAsync.
        /// </summary>
        private void ReadElementFromImpl(XmlReader r, LoadOptions o)
        {
            if (r.ReadState != ReadState.Interactive) throw new InvalidOperationException(SR.InvalidOperation_ExpectedInteractive);
            name = XNamespace.Get(r.NamespaceURI).GetName(r.LocalName);
            if ((o & LoadOptions.SetBaseUri) != 0)
            {
                string? baseUri = r.BaseURI;
                if (!string.IsNullOrEmpty(baseUri))
                {
                    SetBaseUri(baseUri);
                }
            }
            IXmlLineInfo? li = null;
            if ((o & LoadOptions.SetLineInfo) != 0)
            {
                li = r as IXmlLineInfo;
                if (li != null && li.HasLineInfo())
                {
                    SetLineInfo(li.LineNumber, li.LinePosition);
                }
            }
            if (r.MoveToFirstAttribute())
            {
                do
                {
                    XAttribute a = new XAttribute(XNamespace.Get(r.Prefix.Length == 0 ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value);
                    if (li != null && li.HasLineInfo())
                    {
                        a.SetLineInfo(li.LineNumber, li.LinePosition);
                    }
                    AppendAttributeSkipNotify(a);
                } while (r.MoveToNextAttribute());
                r.MoveToElement();
            }
        }
 
        internal void RemoveAttribute(XAttribute a)
        {
            bool notify = NotifyChanging(a, XObjectChangeEventArgs.Remove);
            if (a.parent != this) throw new InvalidOperationException(SR.InvalidOperation_ExternalCode);
            XAttribute? p = lastAttr!, n;
            while ((n = p.next!) != a) p = n;
            if (p == a)
            {
                lastAttr = null;
            }
            else
            {
                if (lastAttr == a) lastAttr = p;
                p.next = a.next;
            }
            a.parent = null;
            a.next = null;
            if (notify) NotifyChanged(a, XObjectChangeEventArgs.Remove);
        }
 
        private void RemoveAttributesSkipNotify()
        {
            if (lastAttr != null)
            {
                XAttribute a = lastAttr;
                do
                {
                    XAttribute next = a.next!;
                    a.parent = null;
                    a.next = null;
                    a = next;
                } while (a != lastAttr);
                lastAttr = null;
            }
        }
 
        internal void SetEndElementLineInfo(int lineNumber, int linePosition)
        {
            AddAnnotation(new LineInfoEndElementAnnotation(lineNumber, linePosition));
        }
 
        internal override void ValidateNode(XNode node, XNode? previous)
        {
            if (node is XDocument) throw new ArgumentException(SR.Format(SR.Argument_AddNode, XmlNodeType.Document));
            if (node is XDocumentType) throw new ArgumentException(SR.Format(SR.Argument_AddNode, XmlNodeType.DocumentType));
        }
    }
}