Frage

Ich habe eine XElement bekommt tief innerhalb eines Dokuments. In Anbetracht der XElement (und XDocument?), Gibt es eine Erweiterungsmethode seine volle zu erhalten (das heißt absolut, zum Beispiel /root/item/element/child) XPath?

z. myXElement.GetXPath ()?

EDIT: Okay, sieht aus wie ich etwas sehr Wichtiges übersehen. Hoppla! Der Index des Elements muss berücksichtigt werden. Siehe meine letzte Antwort für die vorgeschlagene Lösung korrigiert.

War es hilfreich?

Lösung

Die Erweiterungen Methoden:

public static class XExtensions
{
    /// <summary>
    /// Get the absolute XPath to a given XElement
    /// (e.g. "/people/person[6]/name[1]/last[1]").
    /// </summary>
    public static string GetAbsoluteXPath(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        Func<XElement, string> relativeXPath = e =>
        {
            int index = e.IndexPosition();
            string name = e.Name.LocalName;

            // If the element is the root, no index is required

            return (index == -1) ? "/" + name : string.Format
            (
                "/{0}[{1}]",
                name, 
                index.ToString()
            );
        };

        var ancestors = from e in element.Ancestors()
                        select relativeXPath(e);

        return string.Concat(ancestors.Reverse().ToArray()) + 
               relativeXPath(element);
    }

    /// <summary>
    /// Get the index of the given XElement relative to its
    /// siblings with identical names. If the given element is
    /// the root, -1 is returned.
    /// </summary>
    /// <param name="element">
    /// The element to get the index of.
    /// </param>
    public static int IndexPosition(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        if (element.Parent == null)
        {
            return -1;
        }

        int i = 1; // Indexes for nodes start at 1, not 0

        foreach (var sibling in element.Parent.Elements(element.Name))
        {
            if (sibling == element)
            {
                return i;
            }

            i++;
        }

        throw new InvalidOperationException
            ("element has been removed from its parent.");
    }
}

Und der Test:

class Program
{
    static void Main(string[] args)
    {
        Program.Process(XDocument.Load(@"C:\test.xml").Root);
        Console.Read();
    }

    static void Process(XElement element)
    {
        if (!element.HasElements)
        {
            Console.WriteLine(element.GetAbsoluteXPath());
        }
        else
        {
            foreach (XElement child in element.Elements())
            {
                Process(child);
            }
        }
    }
}

Und Beispielausgabe:

/tests/test[1]/date[1]
/tests/test[1]/time[1]/start[1]
/tests/test[1]/time[1]/end[1]
/tests/test[1]/facility[1]/name[1]
/tests/test[1]/facility[1]/website[1]
/tests/test[1]/facility[1]/street[1]
/tests/test[1]/facility[1]/state[1]
/tests/test[1]/facility[1]/city[1]
/tests/test[1]/facility[1]/zip[1]
/tests/test[1]/facility[1]/phone[1]
/tests/test[1]/info[1]
/tests/test[2]/date[1]
/tests/test[2]/time[1]/start[1]
/tests/test[2]/time[1]/end[1]
/tests/test[2]/facility[1]/name[1]
/tests/test[2]/facility[1]/website[1]
/tests/test[2]/facility[1]/street[1]
/tests/test[2]/facility[1]/state[1]
/tests/test[2]/facility[1]/city[1]
/tests/test[2]/facility[1]/zip[1]
/tests/test[2]/facility[1]/phone[1]
/tests/test[2]/info[1]

Das soll dies regeln. Nein?

Andere Tipps

ich den Code von Chris aktualisiert Rechnung Namespacepräfixe zu nehmen. Nur die GetAbsoluteXPath Methode modifiziert wird.

public static class XExtensions
{
    /// <summary>
    /// Get the absolute XPath to a given XElement, including the namespace.
    /// (e.g. "/a:people/b:person[6]/c:name[1]/d:last[1]").
    /// </summary>
    public static string GetAbsoluteXPath(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        Func<XElement, string> relativeXPath = e =>
        {
            int index = e.IndexPosition();

            var currentNamespace = e.Name.Namespace;

            string name;
            if (currentNamespace == null)
            {
                name = e.Name.LocalName;
            }
            else
            {
                string namespacePrefix = e.GetPrefixOfNamespace(currentNamespace);
                name = namespacePrefix + ":" + e.Name.LocalName;
            }

            // If the element is the root, no index is required
            return (index == -1) ? "/" + name : string.Format
            (
                "/{0}[{1}]",
                name,
                index.ToString()
            );
        };

        var ancestors = from e in element.Ancestors()
                        select relativeXPath(e);

        return string.Concat(ancestors.Reverse().ToArray()) +
               relativeXPath(element);
    }

    /// <summary>
    /// Get the index of the given XElement relative to its
    /// siblings with identical names. If the given element is
    /// the root, -1 is returned.
    /// </summary>
    /// <param name="element">
    /// The element to get the index of.
    /// </param>
    public static int IndexPosition(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        if (element.Parent == null)
        {
            return -1;
        }

        int i = 1; // Indexes for nodes start at 1, not 0

        foreach (var sibling in element.Parent.Elements(element.Name))
        {
            if (sibling == element)
            {
                return i;
            }

            i++;
        }

        throw new InvalidOperationException
            ("element has been removed from its parent.");
    }
}

Lassen Sie mich zu dieser Klasse meine letzte Änderung teilen. Basicaly schließt sie Index, wenn Element keine Geschwister und enthält Namespaces mit local-name hat () Operator hat war ich Probleme mit dem Namespacepräfix haben.

public static class XExtensions
{
    /// <summary>
    /// Get the absolute XPath to a given XElement, including the namespace.
    /// (e.g. "/a:people/b:person[6]/c:name[1]/d:last[1]").
    /// </summary>
    public static string GetAbsoluteXPath(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }


        Func<XElement, string> relativeXPath = e =>
        {
            int index = e.IndexPosition();

            var currentNamespace = e.Name.Namespace;

            string name;
            if (String.IsNullOrEmpty(currentNamespace.ToString()))
            {
                name = e.Name.LocalName;
            }
            else
            {
                name = "*[local-name()='" + e.Name.LocalName + "']";
                //string namespacePrefix = e.GetPrefixOfNamespace(currentNamespace);
                //name = namespacePrefix + ":" + e.Name.LocalName;
            }

            // If the element is the root or has no sibling elements, no index is required
            return ((index == -1) || (index == -2)) ? "/" + name : string.Format
            (
                "/{0}[{1}]",
                name,
                index.ToString()
            );
        };

        var ancestors = from e in element.Ancestors()
                        select relativeXPath(e);

        return string.Concat(ancestors.Reverse().ToArray()) +
               relativeXPath(element);
    }

    /// <summary>
    /// Get the index of the given XElement relative to its
    /// siblings with identical names. If the given element is
    /// the root, -1 is returned or -2 if element has no sibling elements.
    /// </summary>
    /// <param name="element">
    /// The element to get the index of.
    /// </param>
    public static int IndexPosition(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        if (element.Parent == null)
        {
            // Element is root
            return -1;
        }

        if (element.Parent.Elements(element.Name).Count() == 1)
        {
            // Element has no sibling elements
            return -2;
        }

        int i = 1; // Indexes for nodes start at 1, not 0

        foreach (var sibling in element.Parent.Elements(element.Name))
        {
            if (sibling == element)
            {
                return i;
            }

            i++;
        }

        throw new InvalidOperationException
            ("element has been removed from its parent.");
    }
}

Im Rahmen eines ich eine Erweiterungsmethode entwickeln eine einfache XPath zu erzeugen ein Element. Es ist ähnlich wie die gewählte Antwort, aber unterstützt XAttribute, XText, XCData und XComment zusätzlich zu XElement. Es ist erhältlich als Code nuget , Projektseite hier: xmlspecificationcompare.codeplex.com

Wenn Sie nach etwas suchen nativ von .NET zur Verfügung gestellt ist die Antwort nein. Sie müßten Ihre eigene Erweiterungsmethode schreiben, dies zu tun.

Es kann mehrere XPaths sein, die auf das gleiche Element führen, so die einfachste XPath zu finden, die zu dem Knoten führt, ist nicht trivial.

Das heißt, es ist ganz einfach einen XPath an den Knoten zu finden. step up einfach den Knotenbaum, bis Sie den Wurzelknoten lesen und die Knotennamen kombinieren, und Sie haben einen gültigen XPath.

Mit dem „full xpath“ Ich nehme an, Sie da die Anzahl der XPaths eine einfache Kette von Tags bedeuten, die potentiell jedes Element sein könnte passen könnten sehr groß.

Das Problem hier ist, dass es sehr schwer ist, wenn nicht ausdrücklich unmöglich, einen bestimmten XPath zu bauen, die reversibel zurück auf das gleiche Element verfolgen werden - ist, dass eine Bedingung

Wenn „nein“, dann vielleicht Sie eine Abfrage durch rekursiv Looping mit Verweis auf die aktuelle Elemente parentNode bauen könnte. Wenn „ja“, dann wirst du zu betrachten erstreckt, dass durch Querverweise für Indexposition innerhalb Geschwister-Sets, referecing ID-ähnlicher Eigenschaften, wenn sie existieren, und dies wird sehr abhängig sein, auf dem XSD, wenn eine allgemeine Lösung möglich.

Microsoft hat eine Erweiterung Methode, dies zu tun, da .NET Framework bereitgestellt 3,5:

http://msdn.microsoft. com / en-us / library / bb156083 (v = VS.100) aspx

Fügen Sie einfach ein mit den folgenden Methoden System.Xml.XPath und ruft:

  • XPathSelectElement: wählen Sie ein einzelnes Element
  • XPathSelectElements: Wählen Sie Elemente und zurück als IEnumerable<XElement>
  • XPathEvaluate: select Knoten (nicht nur Elemente, sondern auch Text, Kommentare etc.) und zurück als IEnumerable<object>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top