not(@attribute)测试不能在JDom XSL转换中工作?

时间:2015-08-25 20:39:32

标签: xslt xslt-1.0 jdom xalan

我有一个XSLT样式表可以使用xsltproc按预期工作但在我的实际应用程序中产生不同的输出,其中转换是通过org.jdom.transform.XSLTransformer(jdom 1.0)应用的,我相信使用Xalan。

样式表片段(这是a larger template的一部分,起点如下:<xsl:template match="/dspace:dim[@dspaceType='ITEM']">):

<xsl:if test="//dspace:field[@mdschema='dc' and @element='rights']">
  <rightsList>
    <xsl:if test="//dspace:field[@mdschema='dc' and @element='rights' and not(@qualifier) and @language='*']">
      <rights>
        <xsl:if test="//dspace:field[@mdschema='dc' and @element='rights' and @qualifier='uri' and @language='*']">
          <xsl:attribute name="rightsUri">
            <xsl:value-of select="//dspace:field[@mdschema='dc' and @element='rights' and @qualifier='uri' and @language='*']"/>
          </xsl:attribute>
        </xsl:if>
        <xsl:value-of select="//dspace:field[@mdschema='dc' and @element='rights' and not(@qualifier) and @language='*']" />
      </rights>
    </xsl:if>
    <xsl:apply-templates select="//dspace:field[@mdschema='dc' and @element='rights' and not(@language='*')]" />
  </rightsList>
</xsl:if>

<xsl:template match="//dspace:field[@mdschema='dc' and @element='rights' and not(@language='*')]">
    <rights><xsl:value-of select="." /></rights>
</xsl:template>

XML片段:

<dim:dim dspaceType="ITEM" xmlns:dim="http://www.dspace.org/xmlns/dspace/dim">
  <dim:field element="rights" language="en_NZ" mdschema="dc">Actual text redacted</dim:field>
  <dim:field element="rights" language="*" mdschema="dc">Attribution 3.0 New Zealand</dim:field>
  <dim:field element="rights" qualifier="uri" language="*" mdschema="dc">http://creativecommons.org/licenses/by/3.0/nz/</dim:field>
</dim:dim>

使用xsltproc,这会产生

<rightsList>
  <rights rightsUri="http://creativecommons.org/licenses/by/3.0/nz/">Attribution 3.0 New Zealand</rights>
  <rights>Actual text redacted</rights>
</rightsList>

在我的应用程序中,这会产生

<rightsList>
  <rights>Actual text redacted</rights>
  <rights>Attribution 3.0 New Zealand</rights>
  <rights>http://creativecommons.org/licenses/by/3.0/nz/</rights>
</rightsList>

所以对我来说,看起来not(@qualifier)位并不能使用jdom。

我很感激能够深入了解这里发生了什么以及如何更改样式表,以便在我目前通过xsltproc获得的应用程序中获得相同的结果。

编辑添加:以防它有任何区别,样式表以

开头
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:dspace="http://www.dspace.org/xmlns/dspace/dim"
            xmlns:exslt="http://exslt.org/common"
            xmlns="http://datacite.org/schema/kernel-3"
            extension-element-prefixes="exslt"
            exclude-result-prefixes="exslt"
            version="1.0">

并且还包含此模板:

<!-- Don't copy everything by default! -->
<xsl:template match="@* | text()" />

请参阅下面的答案,XML结构实际上与我的想法不同,所以问题根本不在XSL中。

1 个答案:

答案 0 :(得分:2)

除了解决原始问题之外,让我们快速了解如何重新组织代码。

您使用了很多//foo个表达式。使用//foo启动表达式意味着“在任何级别搜索名称为foo的元素的整个文档”。除了这是一个可能很昂贵的操作之外,这通常会产生不必要的副作用并使您的代码难以阅读,因为它要求您唯一地指定每个元素,从而导致大量重复的代码。

你也使用了很多xsl:if,但是在XSLT中,几乎没有必要使用if语句(XSLT 1.0和2.0中的一个例外是当你处理节点以外的东西时)。几乎在所有情况下,您都可以使用简单xsl:if替换xsl:apply-templates

那就是说,让我们来看看我们如何重写你的代码以获得相同的效果并减少错误的机会:

<xsl:if test="//dspace:field[@mdschema='dc' and @element='rights']">
    <rightsList>
      .....

类似于具有匹配模板,如下所示(假设您有一个不感兴趣的节点的丢弃模板):

<xsl:template match="dspace:dim[dspace:field[@mdschema='dc' and @element='rights']]">
    <rightsList>

这表示:如果您遇到dim元素,其中任何field元素都设置了这些属性,则输出<rightsList>

然后你有:

<xsl:if test="//dspace:field[@mdschema='dc' and @element='rights' and not(@qualifier) and @language='*']">
    <rights>

这恰好等同于以下apply-template表达式(假设与其匹配的模板):

<xsl:apply-templates select="dspace:field[@mdschema='dc' and @element='rights' and not(@qualifier) and @language='*']" />

在这里,我们发现稍微低一点,我们有一个几乎相同的表达式,这次是not(@language='*')。那么让我们看看我们是否可以完全摆脱这些重复的表达。

首先,让我们回过头来看看你在做什么:

  • 如果任何“dc”和“rights”,则创建<rightsList>
  • 如果其中任何一个没有限定词但语言为“*”,请创建<rights>
  • 在此内容中,如果任何限定符具有值“uri”和语言“*”,则创建属性rightsUri,将其值设置为您找到的第一个
  • 在此<rights>元素之后(当前结构中最多只有一个元素),为每个字段元素创建一个<rights>列表,其中包含语言“*”

如果这是正确的,那么可以按如下方式重写:

<xsl:template match="dspace:dim[dspace:field[@mdschema='dc' and @element='rights']]">
    <xsl:variable name="adjusted">
        <xsl:copy-of select="dspace:field[@mdschema='dc' and @element='rights']"/>
    </xsl:variable>

    <rightsList>
        <xsl:apply-templates select="exsl:node-set($adjusted)/*[not(@qualifier) and @language='*'][1]" mode="noquali"/>
        <xsl:apply-templates select="exsl:node-set($adjusted)/*[not(@language='*')]" />
    </rightsList>
</xsl:template>

<xsl:template match="dspace:field" mode="noquali">
    <rights>
        <xsl:apply-templates select="/dspace:field[@qualifier='uri' and @language='*'][1]" mode="uri"/>
        <xsl:value-of select="."/>
    </rights>
</xsl:template>

<xsl:template match="dspace:field" mode="uri">
    <xsl:attribute name="rightsUri" select="." />
</xsl:template>

<!-- matching anything else -->
<xsl:template match="dspace:field">
    <rights><xsl:value-of select="." /></rights>
</xsl:template>

几乎每个XSLT 1.0处理器都支持exsl:node-set函数,只需将名称空间xmlns:exsl="http://exslt.org/common"添加到xsl:stylesheet声明中。

请注意,我在select-expressions中添加了几次[1]。虽然您不在代码中执行此操作,但您当前的代码具有相同的效果,但如果您使用apply-templates,如果您遇到多个匹配项,则必须指定您只对第一个匹配项感兴趣。

我认为您的代码可以进一步简化,但我想确保逻辑保持完全相同。如您所见,最终结果是没有任何//。但是,您确实看到一个/,它现在指向节点集的根,它只是方便地拥有您感兴趣的节点:具有模式“dc”和“rights”元素属性的节点,所以我们不必一遍又一遍地重复这个表达。

您可以尝试重写,看看它是否有助于您当前的错误,否则我很乐意帮助您。

修改

编辑完成后,原始上下文项目已经为dspace:dim。如果您不介意总是输出<rightsList>(即使它最终为空),您只需将上面的第一个模板匹配模式替换为现有的dspace:dim模式。