【问题标题】:Why does NamespaceManager not use DefaultNamespace when using no prefix in XPath为什么在 XPath 中不使用前缀时 NamespaceManager 不使用 DefaultNamespace
【发布时间】:2014-10-29 12:57:25
【问题描述】:

当我想使用 XPath 遍历我的 XmlDocument 时,我发现文档中有很多丑陋的命名空间,所以我开始使用 NamespaceManager 和 XPath。

XML 看起来像这样

<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:o="urn:schemas-microsoft-com:office:office"
 xmlns:x="urn:schemas-microsoft-com:office:excel"
 xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:html="http://www.w3.org/TR/REC-html40">
    <Worksheet ss:Name="KA0100401">
        <Table>
            <Row>
                <Cell>Data</Cell>
            </Row>
            <!-- more rows... -->
        </Table>
    </Worksheet>
    <Worksheet ss:Name="KA0100402">
        <!-- .... --->
    </Worksheet>
</Workbook>

现在,根据我在本文档中看到的情况,"urn:schemas-microsoft-com:office:spreadsheet" 是默认命名空间,因为它位于根元素上。

所以,天真地,我这样配置了我的NamespaceManager

XmlDocument document = new XmlDocument();
document.Load(reader);
XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
manager.AddNamespace(String.Empty, "urn:schemas-microsoft-com:office:spreadsheet");
manager.AddNamespace("o", "urn:schemas-microsoft-com:office:office");
manager.AddNamespace("x", "urn:schemas-microsoft-com:office:excel");
manager.AddNamespace("ss", "urn:schemas-microsoft-com:office:spreadsheet");
manager.AddNamespace("html", "http://www.w3.org/TR/REC-html40");

但是,当我尝试访问一个节点时

foreach (XmlNode row in document.SelectNodes("/Workbook/Worksheet[1]/Table/Row", manager))

我从来没有得到任何结果。我的印象是,通过将第一个命名空间设置为空前缀,在该工作区中搜索节点时我不需要设置它。

但是,因为它是stated on the AddNamespace method

如果 XPath 表达式不包含前缀,则假定命名空间统一资源标识符 (URI) 是空命名空间。

这是为什么呢?而且,更重要的是:如果不使用前缀将它们设置为空的命名空间,我如何访问默认命名空间中的节点?

如果在搜索节点时我什至无法访问它,那么在管理器上设置默认命名空间有什么好处?

【问题讨论】:

  • "What good is..." - 你有没有想过 XmlNamespaceManager 被更多的代码使用,而不是 just 将它用于 XPath 查询和在 那些 情况下,默认命名空间可能非常有用?
  • 可能重复:stackoverflow.com/questions/585812/…@Damien:不必粗鲁。
  • @LarsH - 这是一种修辞手法。很抱歉你觉得这很粗鲁,我经常用这种评论方式让人们质疑他们的假设,这是第一次有人断言这是粗鲁的。很抱歉给你造成了冒犯。
  • 好评,因为;是的,它确实有。我不知道这个经理还有什么其他应用程序。
  • @Damien:感谢您对反馈持开放态度。

标签: c# .net xml xpath xml-namespaces


【解决方案1】:

来自XPath 1.0 spec

节点测试中的 QName 使用表达式上下文中的命名空间声明扩展为扩展名称。 除了不使用用 xmlns 声明的默认命名空间之外,这与对开始和结束标记中的元素类型名称进行扩展的方式相同:如果 QName 没有前缀,则命名空间 URI 为空(这与扩展属性名称的方式相同)。 如果 QName 具有在表达式上下文中没有命名空间声明的前缀,则会出错。

所以这不是关于NamespaceManager 的问题,而是 XPath 被定义为工作的方式。


您缺少的一点是您在 NamespaceManager 中使用的前缀不必与 XML 文档中的前缀相同。如果需要,您可以使用 xcel 前缀作为 urn:schemas-microsoft-com:office:excel,并使用 sp 前缀作为 urn:schemas-microsoft-com:office:spreadsheet。事实上,您已经在命名空间管理器中为该 URN 分配了一个前缀,因此您可以使用它:

foreach (XmlNode row in 
       document.SelectNodes("/ss:Workbook/ss:Worksheet[1]/ss:Table/ss:Row", manager))


关于这个问题:

如果在搜索节点时我什至无法访问它,在管理器上设置默认命名空间有什么好处?

好处是XmlNamespaceManager 不仅仅用于评估 XPath。例如,它可以用于跟踪 XML 文档中的命名空间,其中有一个默认命名空间的概念。

【讨论】:

    【解决方案2】:

    @JLRishe 的回答对于访问默认命名空间中的节点是正确的(即始终将前缀映射到 XmlNamespaceManager 中的默认命名空间)。

    从您的引用 (MSDN XmlNamespaceManager.AddNamespace) 中读取链接的整个上下文,说明 XPath 表达式中不使用默认的“空”前缀。

    前缀 类型:System.String

    与要添加的命名空间相关联的前缀。使用 String.Empty 添加默认命名空间。>

    注意如果 XmlNamespaceManager 将用于解析 XML 路径语言 (XPath) 表达式中的命名空间,则必须指定前缀。如果 XPath 表达式不包含前缀,则假定名称空间统一资源标识符 (URI) 是空名称空间。有关 XPath 表达式和 XmlNamespaceManager 的详细信息,请参阅 XmlNode.SelectNodes 和 XPathExpression.SetContext 方法。

    【讨论】:

      【解决方案3】:

      我无法回答您的最后一个问题(“有什么好处......”),除非它在非 XPath 情况下可能有所帮助。但是关于“如何访问默认命名空间中的节点,如果不使用前缀会将它们设置为空命名空间?”,答案是您必须使用前缀。

      因此,在这种情况下,由于您将前缀 ss 声明为绑定到其 URI 为 urn:schemas-microsoft-com:office:spreadsheet 的命名空间,这与默认命名空间相同,因此您只需在您的命名空间中使用 ss 前缀XPath 表达式:

      foreach (XmlNode row in document.SelectNodes("/ss:Workbook/ss:Worksheet[1]/ss:Table/ss:Row",
        manager))
      

      【讨论】:

        【解决方案4】:

        我发现如果你删除默认命名空间

        xmlns="urn:schemas-microsoft-com:office:spreadsheet"

        或将默认命名空间设为 null

        xmlns=""

        使用XPath搜索时,XPath不需要在“没有命名空间的元素”之前添加命名空间。

        那么,默认命名空间声明真的很重要吗?

        如果没有,我可能会删除默认的命名空间声明,这使得使用 XPath 搜索更容易,因为不需要像往常一样添加命名空间。

        我尝试过另一种方法是添加一个名为“default”的默认命名空间,由我自己提供,

        我写了一个方法,可以在没有其他命名空间的情况下自动将“默认”添加到元素中:

        public static string XPathAddDeafultNameSpaceProccess(this string XPathProcess)
                {
                    string[] XPSplit = XPathProcess.Split('/');
        
                    for (int i = 0; i < XPSplit.Length; i++)//if element no namespace, add default
                    {
                        if (!XPSplit[i].Contains(':') && !XPSplit[i].Contains('@'))
                            XPSplit[i] = "default:" + XPSplit[i];
                    }
                    for (int i = 0; i < XPSplit.Length; i++)
                    {
                        if (i != XPSplit.Length - 1)//if not the last, add"/"                       
                        XPSplit[i] += "/";
                    }
        
                    string output = "";
                    foreach (string s in XPSplit)//combine
                        output += s;
                    return output;
                }
        

        可以转

        aa/xx:cc/dd/hh:gg/bb"

        "默认:aa/xx:cc/默认:dd/hh:gg/默认:bb"

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-30
          • 2020-10-27
          • 1970-01-01
          • 2012-03-24
          • 2014-08-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多