XSLT 2.0:如果仅由标点分隔,则合并同级节点

时间:2019-03-02 17:35:14

标签: xslt xslt-2.0

给出以下(简化的)XML:

<p>
    <hi rend="italic">Some text</hi>, <hi rend="italic">and some more</hi>: <hi rend="italic"
        >followed by some more.</hi>
    <hi rend="bold">This text is fully in bold.</hi> Here we have plain text, which should't be
    touched. <hi rend="bold">Here we go with bold</hi>, <hi rend="bold">yet again.</hi>
</p>

我想将所有具有相同名称和属性的节点以及它们之间的所有文本节点合并在一起,但前提是这些文本节点的normalize-space()可以简化为标点符号。

换句话说,如果两个或多个hi[@rend='italic']hi[@rend='bold']节点被仅包含标点和空格的文本节点分隔,则应将它们合并。

另一方面,如果两个hi[@rend='italic']或两个hi[@rend='bold']节点之间的文本节点无法简化为标点符号,则不应对其进行触摸。

我想学习如何在不对元素hi和属性@rend进行硬编码的情况下执行此操作,即我希望样式表合并由标点符号文本节点分隔的所有相同元素/属性组合。

标点符号应与正则表达式\p{P}匹配。

输出应如下所示:

<p>
    <hi rend="italic">Some text, and some more: followed by some more.</hi>
    <hi rend="bold">This text is fully in bold.</hi> Here we have plain text, which should't be
    touched. <hi rend="bold">Here we go with bold, yet again.</hi>
</p>

非常感谢。

1 个答案:

答案 0 :(得分:3)

我不确定是否有一个单步解决方案,我可以想到的一种方法是两步转换,在第一步中,将元素间标点文本节点转换为元素,以便第二步可以使用{{ 1}}。在下面的内容中,我使用了XSLT 3和一个由元素的group-adjacent和node-name()排序的属性值序列组成的复合分组键:

node-name()

https://xsltfiddle.liberty-development.net/gWvjQf6

在XSLT 2中,您没有复合分组键,但是当然可以将XSLT 3示例中用作分组键的序列<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf" exclude-result-prefixes="#all" version="3.0"> <xsl:mode on-no-match="shallow-copy"/> <xsl:mode name="text-to-el" on-no-match="shallow-copy"/> <xsl:function name="mf:match" as="xs:boolean"> <xsl:param name="e1" as="element()"/> <xsl:param name="e2" as="element()"/> <xsl:sequence select="deep-equal(($e1!(node-name(), mf:sort(@* except @mf:punctuation)!data())), ($e2!(node-name(), mf:sort(@* except @mf:punctuation)!data())))"/> </xsl:function> <xsl:function name="mf:sort" as="attribute()*"> <xsl:param name="attributes" as="attribute()*"/> <xsl:perform-sort select="$attributes"> <xsl:sort select="node-name()"/> </xsl:perform-sort> </xsl:function> <xsl:template match="text()[matches(normalize-space(.), '^\p{P}+$') and mf:match(preceding-sibling::node()[1], following-sibling::node()[1])]" mode="text-to-el"> <xsl:element name="{node-name(preceding-sibling::node()[1])}" namespace="{namespace-uri(preceding-sibling::node()[1])}"> <xsl:apply-templates select="preceding-sibling::node()[1]/@*" mode="#current"/> <xsl:attribute name="mf:punctuation">true</xsl:attribute> <xsl:value-of select="."/> </xsl:element> </xsl:template> <xsl:variable name="punctuation-text-to-element"> <xsl:apply-templates mode="text-to-el"/> </xsl:variable> <xsl:template match="/"> <xsl:apply-templates select="$punctuation-text-to-element/node()"/> </xsl:template> <xsl:template match="*[*]"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:for-each-group select="node()" composite="yes" group-adjacent="if (. instance of element()) then (node-name(), mf:sort(@* except @mf:punctuation)!data()) else false()"> <xsl:choose> <xsl:when test="current-grouping-key() instance of xs:boolean and not(current-grouping-key())"> <xsl:apply-templates select="current-group()"/> </xsl:when> <xsl:otherwise> <xsl:copy> <xsl:apply-templates select="@*, current-group()/node()"/> </xsl:copy> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </xsl:copy> </xsl:template> </xsl:stylesheet> 变成某个单个字符串分组键,您只需要确保您在元素名称和属性值中没有使用string-join使用分隔符。

除了使用string-join之外,还需要阐明身份转换,并且在可能的情况下,必须用xsl:mode表达式或!步骤替换for .. return的使用:

/

http://xsltransform.net/asnmyS