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

not(@attribute) test not working in JDom XSL transform?

我有一块 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 中。

除了解决您原来的问题,让我们快速了解一下如何重组您的代码。

您使用了很多 //foo 表达式。以 //foo 开头的表达式表示 "search the whole document, at any level, for the element with the name 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>

这恰好等同于以下应用模板表达式(假设与之匹配的模板):

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

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

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

  • 如果任何地方有 "dc" 和 "rights",则创建一个 <rightsList>
  • 如果其中任何一个没有限定符但有语言“*”,则创建 <rights>
  • 在其中,创建属性 rightsUri 如果任何限定符具有值 "uri" 和语言“*”,将其值设置为您找到的第一个
  • 在这个 <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 表达式中添加了几次 [1]。虽然你没有在你的代码中这样做,但你当前的代码具有相同的效果,但是如果你使用应用模板,如果你遇到多个匹配项,你必须指定你只对第一个匹配项感兴趣。

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

你可以试试这个重写,看看它是否有助于解决你当前的错误,否则我很乐意进一步帮助你。

编辑

在您编辑之后,您的原始上下文项将已经 dspace:dim。如果你不介意总是输出 <rightsList>(即使它最终为空),你可以简单地用你现有的 dspace:dim 模式替换我上面的第一个模板匹配模式。

呃。 Forest/trees确实如此。尽管语言属性在应用程序的其他地方几乎都被称为 "language"(另请参阅我提供的 XML 片段),它实际上在 XML 中被称为 "lang"我的样式表在其上运行 - 我最终放弃并使用 this answer 来确定 XML 结构是什么。惊喜!

无论如何,我部分地听从了建议 Abel gave in 并大大简化了这个特定案例的模板。我现在只有

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

在大模板中,加上几个自定义模板:

<xsl:template match="dspace:field[@mdschema='dc' and @element='rights']">
    <xsl:choose>
        <xsl:when test="@qualifier='uri'"/>
        <xsl:otherwise>
            <rights>
                <xsl:if test="@lang='*'">
                    <xsl:apply-templates select="//dspace:field[@mdschema='dc' and @element='rights' and @qualifier='uri' and @lang='*'][1]" mode="rightsURI"/>
                </xsl:if>
                <xsl:value-of select="."/>
            </rights>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="dspace:field[@mdschema='dc' and @element='rights' and @qualifier='uri' and @lang='*']" mode="rightsURI">
    <xsl:attribute name="rightsURI"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>