Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 获取XElement的XPath?_C#_Xml_Xpath_Xelement - Fatal编程技术网

C# 获取XElement的XPath?

C# 获取XElement的XPath?,c#,xml,xpath,xelement,C#,Xml,Xpath,Xelement,我在一份文件中找到了一个元素。给定XElement(和XDocument?),是否有一个扩展方法来获取其完整XPath(即绝对值,例如/root/item/element/child) 例如,myXElement.GetXPath() 编辑: 好吧,看来我忽略了一些非常重要的事情。哎呀!需要考虑元素的索引。有关建议的更正解决方案,请参阅我的上一个答案。如果您正在寻找.NET本机提供的内容,则答案是否定的。您必须编写自己的扩展方法来实现这一点。可能有多个xpath指向同一元素,因此找到指向节点的最

我在一份文件中找到了一个元素。给定XElement(和XDocument?),是否有一个扩展方法来获取其完整XPath(即绝对值,例如
/root/item/element/child

例如,myXElement.GetXPath()

编辑:
好吧,看来我忽略了一些非常重要的事情。哎呀!需要考虑元素的索引。有关建议的更正解决方案,请参阅我的上一个答案。

如果您正在寻找.NET本机提供的内容,则答案是否定的。您必须编写自己的扩展方法来实现这一点。

可能有多个xpath指向同一元素,因此找到指向节点的最简单xpath并非易事

也就是说,很容易找到节点的xpath。只需升级节点树,直到读取根节点并组合节点名称,就可以得到有效的xpath。

我认为“完整xpath”是指一个简单的标记链,因为可能匹配任何元素的xpath数量可能非常大

这里的问题是,构建任何给定的xpath都非常困难,如果不是特别不可能的话,它将可逆地追溯到同一个元素——这是一个条件吗


如果“否”,那么您可能可以通过递归地循环引用当前元素parentNode来构建查询。如果“是”,那么您将考虑通过交叉引用同级集合中的索引位置来扩展它,引用类ID属性(如果存在),如果有通用解决方案,这将非常依赖于您的XSD。

这实际上是一个重复的问题。虽然没有将其标记为答案,但该问题中的方法是明确表示XML文档中节点的XPath的唯一方法,该方法在所有情况下都能工作。(它也适用于所有节点类型,而不仅仅是元素。)

如您所见,它生成的XPath既丑陋又抽象。但它解决了许多回答者在这里提出的问题。这里提出的大多数建议都会生成一个XPath,当用于搜索原始文档时,它将生成一组包含目标节点的一个或多个节点。问题在于“或更多”。例如,如果我有一个数据集的XML表示,则特定数据行元素的原始XPath,
/DataSet1/DataTable1
,也会返回数据表中所有其他数据行的元素。如果不知道XML是如何进行格式化的(比如,是否有主键元素?),就无法消除歧义


但是
/node()[1]/node()[4]/node()[11]
,不管发生什么,它只会返回一个节点。

扩展方法:

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.");
    }
}
和样本输出:

/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]

这应该解决这个问题。没有

我更新了Chris的代码,将名称空间前缀考虑在内。只修改了GetAbsoluteXPath方法

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.");
    }
}
公共静态类XExtensions
{
/// 
///获取给定XElement(包括命名空间)的绝对XPath。
///(例如“/a:people/b:person[6]/c:name[1]/d:last[1]”)。
/// 
公共静态字符串GetAbsoluteXPath(此XElement元素)
{
if(元素==null)
{
抛出新的ArgumentNullException(“元素”);
}
Func=h=e=>
{
int index=e.IndexPosition();
var currentNamespace=e.Name.Namespace;
字符串名;
如果(currentNamespace==null)
{
name=e.name.LocalName;
}
其他的
{
字符串namespacePrefix=e.GetPrefixOfNamespace(currentNamespace);
name=namespacePrefix+“:”+e.name.LocalName;
}
//如果元素是根,则不需要索引
返回(索引==-1)?“/”+名称:string.Format
(
"/{0}[{1}]",
名称
index.ToString()
);
};
var祖先=来自元素中的e。祖先()
选择H(e);
返回字符串.Concat(祖先.Reverse().ToArray())+
相对论h(元素);
}
/// 
///获取给定元素相对于其属性的索引
///具有相同名称的同级。如果给定元素为
///返回根-1。
/// 
/// 
///要获取其索引的元素。
/// 
公共静态int-IndexPosition(此XElement元素)
{
if(元素==null)
{
抛出新的ArgumentNullException(“元素”);
}
if(element.Parent==null)
{
返回-1;
}
int i=1;//节点的索引从1开始,而不是从0开始
foreach(element.Parent.Elements(element.Name)中的var同级)
{
if(同级==元素)
{
返回i;
}
i++;
}
抛出新的InvalidOperationException
(“元素已从其父元素中删除。”);
}
}
作为扩展方法的一部分,我开发了一个扩展方法来生成元素的简单XPath。它与所选答案类似,但除了XElement之外还支持XAttribute、XText、XCData和XComment。
它可以在这里的项目页面上找到:

让我分享我对这个类的最新修改。 基本上,如果元素没有同级元素,并且包含带有local-name()运算符的名称空间,则不包括索引。我在名称空间前缀方面遇到了问题

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.");
    }
}
公共静态类XExtensions
{
/// 
///获取给定XElement(包括命名空间)的绝对XPath。
///(例如“/a:people/b:person[6]/c:name[1]/d:last[1]”)。
/// 
公共静态字符串GetAbsoluteXPath(此XElement元素)
{
if(元素==null)
{
抛出新的ArgumentNullException(“元素”);
}
Func=h=e=
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.");
    }
}
public static string GetAbsoluteXPath(XElement element,int xpversion)
{
    IEnumerable<XElement> ancestors = element.AncestorsAndSelf();
    string xpath = ancestors.Aggregate(new StringBuilder(),
                        (str, elem) => str.Insert(0, (xpversion > 1 ? ("/*:" + elem.Name.LocalName) : ("/*[local-name(.) = '" + elem.Name.LocalName + "']")) + "[" + (int)(elem.ElementsBeforeSelf().Where(el => el.Name.LocalName == elem.Name.LocalName).Count() + 1) + "]"),
                        str => str.ToString());
    return xpath;
}