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