【问题标题】:Read namespaces from xml document从 xml 文档中读取命名空间
【发布时间】:2011-03-30 11:24:42
【问题描述】:

如何从 XML 文档中读取命名空间?

<fx:FIBEX xmlns:fx="http://www.asam.net/xml/fbx" xmlns:can="http://www.asam.net/xml/fbx/can" xmlns:flexray="http://www.asam.net/xml/fbx/flexray"      
    xmlns:ho="http://www.asam.net/xml" xmlns:ni="http://www.ni.com/xnet"    
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     
    xsi:schemaLocation="http://www.asam.net/xml/fbx/all http://www.asam.net/xml/fbx/2_0_1/xsd/fibex4multiplatform.xsd"     
     VERSION="2.0.1">

如何从这个根元素中读取命名空间?

实际上最终的问题是“如何使用 xPath 检索节点,例如

//fx:ELEMENTS/fx:CLUSTERS/fx:CLUSTER 

?

我已经使用该字符串尝试了 XmlDocument.SelectSingleNode() ,但这给了我一些关于 XmlNamespaces 的错误,我在谷歌上发现你需要有一个 XmlNamespaceManager。

【问题讨论】:

  • 这取决于您在 C# 中访问文档的方式。 xml文档? xml阅读器?还有什么?
  • 啊当然。在 C++/CLI 中进行,但这应该不是问题。使用 XmlDocument
  • 实际上最终的问题是“如何使用 xPath 检索节点,例如 '//fx:ELEMENTS/fx:CLUSTERS/fx:CLUSTER'”?
  • 我相信XmlDocument 已经过时了——你考虑过改用XDocument 等吗?
  • 并非如此。编译器(3.5)没有给我任何警告

标签: xml xsd c++-cli xmldocument


【解决方案1】:

您的问题实际上似乎包括两个不同的问题:

  1. 如何选择在某个命名空间中声明的元素?
  2. 如何选择元素的命名空间节点?

问题 1

为了选择某个命名空间中的节点,您需要指定前缀命名空间映射。所有 XPath 处理器都允许这样做,但如何完成取决于实现。通常,这意味着创建一个对 - 一个前缀和一个 URI - 然后将此对添加为命名空间上下文。 URI 很重要。它需要与元素的命名空间 URI 完全相同。前缀不需要与文档中使用的相同,它可以是任何东西,但您需要一个前缀,即使该元素位于默认命名空间中并且因此没有前缀。

在您的 XPath 解析器知道所需的命名空间后,您可以像在问题中一样选择元素://fx:ELEMENTS/fx:CLUSTERS/fx:CLUSTER。确保您使用的前缀与您在映射中使用的前缀相同(如果前缀与文档中的不同)。请注意,在 XPath 表达式中,总是假定没有前缀的元素具有 没有命名空间,因此即使从默认命名空间中选择元素,您也需要在 XPath 表达式中使用命名空间前缀。

对于肮脏的 hack,您还可以尝试完全避免使用元素名称。在 Xpath 中,* 匹配所有元素节点,node() 匹配所有节点。您可以通过创造性地使用使用name()、local-name()、position() 或last() 等函数的谓词来缩小选择范围。例如,XPath 表达式//*[name()='fx:CLUSTER'] 选择所有具有前缀“fx”和本地名称“CLUSTER”的元素即使没有前缀命名空间映射,它也能正常工作。请注意,尽管在 XML 中,前缀并不能保证保持不变,它们也可以在文档中重用/覆盖,以便相同的前缀在文档的不同部分引用不同的 URI。

问题 2

使用namespace:: 轴选择命名空间节点。命名空间节点名称的本地部分是命名空间前缀,字符串值是命名空间 URI。默认命名空间的节点名称为空。您可以使用 XPath 表达式 //namespace::*[.=http://example.com/ns] 选择所有具有 URI http://example.com/ns 的命名空间节点,并使用 //namespace::*[name()='px'] 选择所有前缀为“px:”的命名空间节点。就像属性节点一样,命名空间节点不是其所属元素的子元素,但该元素是该命名空间节点的父元素。使用parent::轴获取命名空间节点的所有者。

命名空间不需要在根元素中声明(尽管这很常见且非常合理),但它们可以在任何元素中声明。搜索根元素的命名空间节点并不能保证您知道本文档中使用的所有命名空间 URI 和前缀。

问题是所有元素都有一个或多个命名空间节点,而不管元素是否有明确的命名空间声明。元素有一组命名空间节点,一个用于默认命名空间(如果它在此元素的范围内),一个用于在此元素范围内的每个前缀,包括 xml 前缀。下面是不同情况下命名空间节点的代码示例。

这个例子有 3 个文件:一个具有不同命名空间的 XML 文档,一个为每个元素打印命名​​空间节点值的 XSLT 文档,以及 XSLT 文档的输出。

已处理的 XML 文档

<root xmlns:ns1="http://example.com/namespace-1">
 <nothing/>
 <default xmlns="http://example.com/default">
  <override xmlns=""/>
 </default>
 <ns1:namespace-1 ns1:attribute-1="x" attribute-2="y"/>
 <ns2:namespace-2 xmlns:ns2="http://example.com/namespace-2"/>
</root>

XSLT 文档

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output encoding="utf-8" method="text"/>

 <xsl:template match="/">
  <xsl:text>Find and print all element names and namespace nodes&#10;&#10;</xsl:text>
  <xsl:apply-templates select="//*|//@*"/>
  <xsl:text>End of tests&#10;</xsl:text>
 </xsl:template>

 <xsl:template match="*|@*">
  <xsl:text>name          = "</xsl:text>
  <xsl:value-of select="name(.)"/>
  <xsl:text>"&#10;</xsl:text>
  <xsl:text>namespace URI = "</xsl:text>
  <xsl:value-of select="namespace-uri(.)"/>
  <xsl:text>"&#10;</xsl:text>
  <xsl:text>This node has </xsl:text>
  <xsl:value-of select="count(./namespace::*)"/>
  <xsl:text> namespace nodes:&#10;</xsl:text>
  <xsl:for-each select="./namespace::*">
   <xsl:value-of select="position()"/>
   <xsl:text>. namespace prefix = "</xsl:text>
   <xsl:value-of select="name(.)"/>
   <xsl:text>"&#10;   namespace URI    = "</xsl:text>
   <xsl:value-of select="."/>
   <xsl:text>"&#10;</xsl:text>
  </xsl:for-each>
  <xsl:text>&#10;</xsl:text>
 </xsl:template>
</xsl:stylesheet>

XSLT 文档的输出

Find and print all element names and namespace nodes

name          = "root"
namespace URI = ""
This node has 2 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"

name          = "nothing"
namespace URI = ""
This node has 2 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"

name          = "default"
namespace URI = "http://example.com/default"
This node has 3 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"
3. namespace prefix = ""
   namespace URI    = "http://example.com/default"

name          = "override"
namespace URI = ""
This node has 3 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"
3. namespace prefix = ""
   namespace URI    = ""

name          = "ns1:namespace-1"
namespace URI = "http://example.com/namespace-1"
This node has 2 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"

name          = "ns1:attribute-1"
namespace URI = "http://example.com/namespace-1"
This node has 0 namespace nodes:

name          = "attribute-2"
namespace URI = ""
This node has 0 namespace nodes:

name          = "ns2:namespace-2"
namespace URI = "http://example.com/namespace-2"
This node has 3 namespace nodes:
1. namespace prefix = "xml"
   namespace URI    = "http://www.w3.org/XML/1998/namespace"
2. namespace prefix = "ns1"
   namespace URI    = "http://example.com/namespace-1"
3. namespace prefix = "ns2"
   namespace URI    = "http://example.com/namespace-2"

End of tests

该示例显示的一些内容:

  1. 所有元素都有一个用于 xml 命名空间的节点
  2. 元素 root 和 nothing 具有前缀命名空间“namespace-1”的节点,即使元素不使用它
  3. 默认命名空间的名字是空的,见default
  4. 元素root 没有默认命名空间的节点,但override 有,即使它们都不属于命名空间。这是因为override 有一个默认的命名空间声明,只有在这种情况下它才被重置为空。
  5. namespace-1 与元素 nothing 具有相似的命名空间节点,尽管另一个带有前缀并使用命名空间,而另一个则没有
  6. namespace-2 是唯一具有 ns2 前缀节点的元素,因为兄弟元素不在命名空间声明的范围内
  7. 有趣的是,属性可以属于一个命名空间,但它们似乎仍然没有任何命名空间节点。根据规范,命名空间节点也有扩展名称,但 URI 始终为空。

在比较命名空间时,请记住元素不共享命名空间节点。即使父子节点具有所有相似的命名空间节点,它们实际上也不是相同的节点。就像两个不同对象的变量不一样,即使它们具有相同的名称和相同的值。

【讨论】:

    猜你喜欢
    • 2013-05-11
    • 2010-10-22
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 2016-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多