我有一个XSLT样式表,使用xsltproc可以像预期的那样工作,但在我的实际应用程序中产生不同的输出,其中转换是通过org.jdom.transform.XSLTransformer
(jdom 1.0)应用的,我相信使用的是Xalan。
样式表片段(这是一个更大的模板的一部分,开头如下:<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
开头的表达式意味着"在任何级别搜索整个文档,查找名称为foo
的元素"。除了这是一个潜在的昂贵的操作,这通常有不必要的副作用,并使您的代码难以阅读,因为它要求您唯一地指定每个元素,导致大量重复的代码。
您还使用了大量的xsl:if
,但是在XSLT中,几乎没有必要使用if语句(XSLT 1.0和2.0中的一个例外是当您处理节点以外的东西时)。在几乎所有情况下,您都可以用简单的xsl:apply-templates
替换xsl:if
。
也就是说,让我们看看如何重写代码以获得相同的效果并减少出错的机会:
<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"one_answers"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
声明中。
注意,我在选择表达式中添加了几次[1]
。虽然您没有在代码中这样做,但您当前的代码具有相同的效果,但是如果您使用apply-templates,如果遇到多个匹配,则必须指定仅对第一个匹配感兴趣。
我认为你的代码可以进一步简化,但我想确保逻辑保持完全相同。正如您所看到的,最终结果是没有任何//
。但是,您确实看到了一个/
,它现在指向节点集的根,方便地只包含您感兴趣的节点:具有模式"dc"one_answers"rights"元素属性的节点,因此我们不必一遍又一遍地重复该表达式。
您可以尝试重写,看看它是否有助于解决您当前的错误,否则我很乐意进一步帮助您。
编辑
编辑后,您的原始上下文项将已经是dspace:dim
。如果您不介意总是输出<rightsList>
(即使它最终为空),您可以简单地将上面的第一个模板匹配模式替换为现有的dspace:dim
模式。
废话。森林和树木。尽管语言属性在应用程序的其他地方几乎都被称为"语言"(参见我给出的XML片段),但在样式表所操作的XML中,它实际上被称为"lang"——我最终屈服了,并使用这个答案来确定XML结构是什么。惊喜!
无论如何,我遵循了Abel在他的部分回答中给出的建议,并为这个特殊情况简化了模板。现在只有
<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>