元素的Xpath与命名空间

时间:2014-04-23 08:21:22

标签: c# xml xpath

我需要知道XDocument中XElement的xpath,使用命名空间,因为我需要它来在XDocument中进行稍后的查找。

我目前正在使用Get the XPath to an XElement?中的示例,它适用于没有命名空间的文档。但是没有带有命名空间的xml文档,并且支持它的工具不能给我正确的路径。

示例我有xml文档

<?xml version="1.0" encoding="utf-8"?>
<Report xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition">
  <AutoRefresh>0</AutoRefresh>
  <DataSources>

以下代码会在/:Report

中找到XPathSelectElement和崩溃的路径
var x = XDocument.Load(@"file.xml");
var path = x.Elements().First().GetAbsoluteXPath();
var element = x.XPathSelectElement(path);

路径假设如何?我已经手动试过&#34; / rd:Report&#34;它仍然很难选择元素。

扩展,在另一个主题中找到:

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.");
    }
}

1 个答案:

答案 0 :(得分:2)

好吧,首先/:Report不是有效的XPath表达式,如果您使用的函数创建了该路径,那么在XML文档具有默认命名空间(如xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition"的情况下,它不会输出有效的XPath) 。事实上,在输入中给出这样的默认命名空间声明,任何创建路径的函数都需要生成一个前缀(并且通常为不同的命名空间生成多个)并将其绑定到默认的命名空间URI。或者您需要采用不同的方法并生成*[local-name() = 'Report' and namespace-uri() = 'http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition']形式的步骤。使用XPath 1.0,这是我作为前缀生成的方式,它们与命名空间URI的绑定取决于您使用的特定XPath API,而另一种方法适用于任何XPath API。