Xslt XSL-如何匹配连续的逗号分隔标记
我试图匹配一系列逗号分隔的xml标记,然后对整个节点组和文本应用xslt转换。例如,给定以下部分XML:Xslt XSL-如何匹配连续的逗号分隔标记,xslt,xpath,Xslt,Xpath,我试图匹配一系列逗号分隔的xml标记,然后对整个节点组和文本应用xslt转换。例如,给定以下部分XML: <p>Some text here <xref id="1">1</xref>, <xref id="2">2</xref>, <xref id="3">3</xref>. </p> 这里有一些文本 1. 2. 3. 最后,我想说: <p>Some tex
<p>Some text here
<xref id="1">1</xref>,
<xref id="2">2</xref>,
<xref id="3">3</xref>.
</p>
这里有一些文本
1.
2.
3.
最后,我想说:
<p>Some text here <sup>1,2,3</sup>.</p>
这里有一些文本1,2,3
在这一点上,一个更混乱的替代方案也是可以接受的:
<p>Some text here <sup>1</sup><sup>,</sup><sup>2</sup><sup>,</sup><sup>3</sup>.</p>
这里有一些文本1,2,3
我有从单个外部参照到sup的转换:
<xsl:template match="xref"">
<sup><xsl:apply-templates/></sup>
</xsl:template>
第二种选择可以通过
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()[normalize-space() = ',' and preceding-sibling::node()[1][self::xref]]">
<sup>,</sup>
</xsl:template>
<xsl:template match="xref">
<sup>
<xsl:apply-templates/>
</sup>
</xsl:template>
</xsl:stylesheet>
,
如果您可以使用XSLT 2.0(例如,与Saxon 9或AltovaXML或XQSharp一起使用),那么下面是一个XSLT 2.0解决方案,它应该会生成您要求的第一个输出:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<xsl:for-each-group select="node()" group-adjacent="self::xref or self::text()[normalize-space() = ',']">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<sup>
<xsl:value-of select="current-group()/normalize-space()" separator=""/>
</sup>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
有趣的问题+一,
以下是XSLT 2.0解决方案:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:variable name="comma-regex">^\s*,\s*$</xsl:variable>
<!-- Identity transform -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- Don't directly process xrefs that are second or later in a comma-separated series.
Note that this template has a higher default priority than the following one,
because of the predicate. -->
<xsl:template match="xref[preceding-sibling::node()[1]/
self::text()[matches(., $comma-regex)]/
preceding-sibling::*[1]/self::xref]" />
<!-- Don't directly process comma text nodes that are in the middle of a series. -->
<xsl:template match="text()[matches(., $comma-regex) and
preceding-sibling::*[1]/self::xref and following-sibling::*[1]/self::xref]" />
<!-- for xrefs that first (or solitary) in a comma-separated series: -->
<xsl:template match="xref">
<sup>
<xsl:call-template name="process-xref-series">
<xsl:with-param name="next" select="." />
</xsl:call-template>
</sup>
</xsl:template>
<xsl:template name="process-xref-series">
<xsl:param name="next"/>
<xsl:if test="$next">
<xsl:value-of select="$next"/>
<xsl:variable name="followingXref"
select="$next/following-sibling::node()[1]/
self::text()[matches(., $comma-regex)]/
following-sibling::*[1]/self::xref"/>
<xsl:if test="$followingXref">
<xsl:text>,</xsl:text>
<xsl:call-template name="process-xref-series">
<xsl:with-param name="next" select="$followingXref"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
^\s*,\s*$
,
(如果我们可以对输入进行一些假设,这可以简化。)
根据您提供的示例输入运行,结果为:
<p>Some text here
<sup>1,2,3</sup>.
</p>
这里有一些文本
1,2,3.
更新:感谢@flyn1179提醒我解决方案没有产生想要的输出,我对其进行了轻微修改现在生成所需的“良好”格式
此XSLT 1.0转换:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()[1]|@*"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match=
"xref[not(preceding-sibling::node()[1]
[self::text() and starts-with(.,',')]
)
]">
<xsl:variable name="vBreakText" select=
"following-sibling::text()[not(starts-with(.,','))][1]"/>
<xsl:variable name="vPrecedingTheBreak" select=
"$vBreakText/preceding-sibling::node()"/>
<xsl:variable name="vFollowing" select=
".|following-sibling::node()"/>
<xsl:variable name="vGroup" select=
"$vFollowing[count(.|$vPrecedingTheBreak)
=
count($vPrecedingTheBreak)
]
"/>
<sup>
<xsl:apply-templates select="$vGroup" mode="group"/>
</sup>
<xsl:apply-templates select="$vBreakText"/>
</xsl:template>
<xsl:template match="text()" mode="group">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
</xsl:stylesheet>
<p>Some text here
<sup>1,2,3</sup>.
<ttt/>
<sup>4,5,6</sup>.
<zzz/>
</p>
应用于以下XML文档时(基于提供的文档,但变得更复杂和有趣):
这里有一些文本
1.
2.
3.
4.
5.
6.
准确地生成所需的正确结果:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()[1]|@*"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match=
"xref[not(preceding-sibling::node()[1]
[self::text() and starts-with(.,',')]
)
]">
<xsl:variable name="vBreakText" select=
"following-sibling::text()[not(starts-with(.,','))][1]"/>
<xsl:variable name="vPrecedingTheBreak" select=
"$vBreakText/preceding-sibling::node()"/>
<xsl:variable name="vFollowing" select=
".|following-sibling::node()"/>
<xsl:variable name="vGroup" select=
"$vFollowing[count(.|$vPrecedingTheBreak)
=
count($vPrecedingTheBreak)
]
"/>
<sup>
<xsl:apply-templates select="$vGroup" mode="group"/>
</sup>
<xsl:apply-templates select="$vBreakText"/>
</xsl:template>
<xsl:template match="text()" mode="group">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
</xsl:stylesheet>
<p>Some text here
<sup>1,2,3</sup>.
<ttt/>
<sup>4,5,6</sup>.
<zzz/>
</p>
这里有一些文本
1,2,3.
4,5,6.
说明:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()[1]|@*"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match=
"xref[not(preceding-sibling::node()[1]
[self::text() and starts-with(.,',')]
)
]">
<xsl:variable name="vBreakText" select=
"following-sibling::text()[not(starts-with(.,','))][1]"/>
<xsl:variable name="vPrecedingTheBreak" select=
"$vBreakText/preceding-sibling::node()"/>
<xsl:variable name="vFollowing" select=
".|following-sibling::node()"/>
<xsl:variable name="vGroup" select=
"$vFollowing[count(.|$vPrecedingTheBreak)
=
count($vPrecedingTheBreak)
]
"/>
<sup>
<xsl:apply-templates select="$vGroup" mode="group"/>
</sup>
<xsl:apply-templates select="$vBreakText"/>
</xsl:template>
<xsl:template match="text()" mode="group">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
</xsl:stylesheet>
<p>Some text here
<sup>1,2,3</sup>.
<ttt/>
<sup>4,5,6</sup>.
<zzz/>
</p>
我们使用“细粒度”标识规则,该规则按文档顺序逐节点处理文档,并“按原样”复制匹配的节点
我们使用一个模板覆盖标识规则,该模板匹配任何xref
元素,该元素是xref
元素组中的第一个元素,每个元素(但最后一个)后面都有一个以“,”字符开头的直接文本节点同级。这里我们找到了第一个违反规则的文本节点同级(它的起始字符不是“,”
然后,我们使用Kayessian(在@Michael Kay之后)公式计算两个节点集的交点,找到组中的所有节点。这个公式是:$ns1[count(.|$ns2)=count($ns2)]
然后,我们以名为“组”的模式处理组中的所有节点
最后,我们将模板(在匿名模式下)应用于中断文本节点(即组后面的第一个节点),以便处理链继续
对于你的“混乱的选择”,有一个几乎微不足道的解决方案:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="xref">
<sup>
<xsl:apply-templates />
</sup>
</xsl:template>
<xsl:template match="text()[normalize-space(.)=',']">
<sup>,</sup>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*| node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
有趣…你可以使用布尔值作为分组键?!我认为,一旦你将注意力集中在它周围,这是有意义的…虽然非常优雅,但这种方法的缺点是,如果只有逗号的文本节点出现在xref
旁边,而不是出现在两个xref
之间,它仍然会被视为
的一部分<代码>一些文本1和更多。
:逗号将作为
的一部分出现,但不应该出现。顺便说一句,这需要XSLT 2.0的唯一原因是正则表达式匹配函数。如果将匹配(,$comma regex)
更改为规范化空格(.)=','
,正如在@Martin的回答中一样,您可以使用XSLT 1.0。注意-我需要添加priority=“1”进入xsl:template,该模板与第一次外部参照之后发生的外部参照相匹配。我收到了不明确的匹配警告,并且其他外部参照正在通过过程外部参照系列。这很好-谢谢!同样在同一模板中,将前面的同级::node()/
更改为前面的同级::node()[1]/
允许在单个p
@Pocket中使用多个外部参照跨距:感谢对前面的兄弟姐妹::node()[1]-这是我应该首先编写的内容。现在编辑我的答案。实际上我必须在另外两个地方添加[1]
(在匹配text()
的模板中)还有。@Pocket:哪个XSLT处理器给了您不明确的匹配警告?您提到的模板应该具有默认优先级0.5,因为匹配有一个谓词。具有match=“xref”
的模板应该具有默认优先级0(请参阅)。好问题,+1。有关完整、不太长、也不太复杂的XSLT 1.0解决方案,请参阅我的答案。还添加了详细说明。在@Flynn1179提醒我的解决方案生成的输出不完全是所需的输出后,我已更新了解决方案。现在,此解决方案生成了所需的“良好格式”!给出的结果不是想要的结果;逗号应该包含在sup
标记中。我建议添加此模板:,
@Flynn1179:谢谢--我没有注意到这一点。现在我更新了解决方案,以生成“良好”格式。:)有趣的标识规则+1Hm,当输入一些文本1、2和更多时。
,它输出一些文本1、2和更多。
。“和更多”不应该在
里面,是吗?另外,输入一些文本1,以及更多。
给出了一些文本:1消失了。@LarsH:我没有时间对此进行广泛的测试,但是