【问题标题】:XSLT on XML file with defaultnamespace. Xpath with 'artificial' prefix具有默认命名空间的 XML 文件上的 XSLT。带有“人工”前缀的 Xpath
【发布时间】:2014-07-21 23:16:48
【问题描述】:

我有一个带有默认命名空间的大型数据库 XML 文件,要使用 XSLT 进行转换。这是一个最小的例子。

该文件名为 server.xml。它包含不正确的数据。

<?xml version="1.0" encoding="UTF-8"?>
<bookstore xmlns="http://www.mybook.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <book author="Incorrect_Author">
    <title>Correct_Title</title>  
 </book>
</bookstore>

需要通过匹配另一个 XML 文件中的书名来将作者更改为正确的值。

第二个 xml 文件称为 client.xml。它包含该书的正确作者姓名。

<?xml version="1.0" encoding="UTF-8"?>
<bookstore xmlns="http://www.mybook.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <book author="Correct_Author">
    <title>Correct_Title</title>  
    <additionalstuff/>
 </book>
</bookstore>

但是,它还包含我不想要的其他信息。所以我想根据客户端修改服务器,按书名匹配。

我编写了以下 XSLT(基于 this answer)。如果我人为地删除我的 xml 文件中的默认 xmlns,它就可以完成这项工作。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" indent="yes"/>

<xsl:param name="clientXml" select="'client.xml'" />

<xsl:variable name="client" select="document($clientXml)//book" />

<xsl:template match="book/@author">
    <xsl:attribute name="author">
        <xsl:value-of select="$client[child::title=current()/../title]/@author" />
    </xsl:attribute>
</xsl:template>

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

但是,我需要它来处理文件原样(包含默认 ns)。我知道这是一个当前的问题,所以我根据找到的答案尝试了以下代码。它不起作用。 XPath 前缀有问题吗?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:v="http://www.mybook.com" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="v" >

 <xsl:output method="xml" indent="yes"/>

 <xsl:param name="clientXml" select="'client.xml'" />

 <xsl:variable name="client" select="document($clientXml)//v:book" />

 <xsl:template match="v:book/@author">
    <xsl:attribute name="author">
        <xsl:value-of select="$client[child::title=current()/../title]/@author" />
    </xsl:attribute>
 </xsl:template>

 <xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

【问题讨论】:

    标签: xml xslt xpath


    【解决方案1】:

    XPaths,包括那些在 XSLT 中使用的,是命名空间感知的......不幸的是,至少在 1.0 版本的规范中不支持默认命名空间的概念。要匹配命名空间节点,您必须在样式表的路径中使用正确绑定的前缀,即使您没有在输入文档中这样做。

    【讨论】:

    • 感谢您的精确。我的问题是,即使我理论上知道这一点,我也无法弄清楚在路径命令中将前缀写在哪里。
    • 为了它的价值,我试图说服 XPath 工作组支持伪语法 {namespaceURI}localname,而不是仅通过前缀或显式测试命名空间名称来支持命名空间和本地名称。他们认为强调前缀的使用对于采用 XML 命名空间很重要,并拒绝了该建议。哦,好吧。
    【解决方案2】:

    你快到了。前缀是正确的方法。您只是错过了为 title 元素选择器添加前缀:

    <xsl:template match="v:book/@author">
        <xsl:attribute name="status">
            <xsl:value-of select="$client[child::v:title=current()/../v:title]/@author" />
        </xsl:attribute>
    </xsl:template>
    

    由于源中的命名空间是 default 命名空间,因此该命名空间适用于所有后代元素,而不仅仅是 bookstorebook。在样式表中,您将该命名空间分配给前缀,因为 XPath 要求命名空间中的任何元素选择器都以 显式 为前缀。

    【讨论】:

    • 非常感谢。它现在确实有效。您对前缀的使用有很好的参考吗?我最初弄错了,因为我不确定在哪里添加前缀规范。例如,我不确定@author 是否也需要指定前缀。我明白不应该,但对我来说还是有点神秘。
    • 前缀只是将 name 限定为命名空间一部分的引用。当声明默认命名空间(xmlns 不带后缀)时,它由声明它的 元素 及其所有 继承后代元素(直到某些后代用另一个无后缀的xmlns 声明覆盖它。属性也可以由命名空间限定,但必须通过前缀显式完成。映射到前缀的命名空间声明也适用于当前元素 如果它是前缀 以及所有 prefixed 后代元素和属性。
    • 这是一个非常好的 XML 命名空间参考:msdn.microsoft.com/en-us/library/aa468565.aspx。也可以参考XML Names specification
    • 感谢您的有用帮助。
    【解决方案3】:

    如果命名空间对您的转换不那么重要 更改模板:

    <xsl:template match="book">
    

    到:

    <xsl:template match="*[local-name(.)=‘book']">
    

    无论其命名空间如何,模板都能正常工作。如果您需要处理简单的转换,使用它可能会对您有所帮助。

    【讨论】:

    • 感谢您的回答。但是,我无法让它发挥作用。我尝试了以下。这是你的意思吗?
    • 这也是使用 XPath 的有效方式。在这里,您忽略 命名空间并只处理 local 部分。作为名称空间一部分的名称(也称为 QName 或 qualified 名称)具有名称空间部分和本地部分。它通常像这样表示{http://www.mybook.com}:book。您不能在 XPath 或 XML 中使用此类标识符,因此您可以将名称空间 map 到前缀,或者如果它自动映射到 default 名称空间,则使用不带前缀的名称.当您使用*[local-name()='book'] 时,您将选择本地名称等于book所有 元素。
    • 并且完全忽略了命名空间。在您的情况下,您不仅必须将其应用于book,还必须应用于作为名称空间一部分的所有元素(例如title)。当您知道不会与其他命名空间的本地名称冲突(v:book 和其他一些bookxyz:book 的本地名称相同)时,这是可以的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-08
    • 2014-09-17
    • 2011-04-25
    • 2011-04-25
    相关资源
    最近更新 更多