给定此(简化)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
的情况下进行此操作,即,我希望样式表合并任何相同的元素/属性组合,并由标点符号文本节点隔开。
标点字符应与Regex 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>
非常感谢。
我不确定有一个步骤解决方案,我能想到的一种方法是一个两步转换第二个转换步骤可以使用group-adjacent
。在以下内容中,我使用了XSLT 3和由元素的node-name()
组成的复合分组密钥和Node-name()排序属性值的序列:
<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>
https://xsltfiddle.liberty-development.net/gwvjqf6
在XSLT 2中,您没有复合分组键,但是当然,可以 string-join
xslt 3样本中使用的序列作为分组键,将分离器字符具有string-join
,该字符在元素名称和属性值中不出现。
而不是使用xsl:mode
,需要拼写身份转换,并且必须在可能的情况下用!
的使用替换CC_14或/
步骤:
<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="2.0">
<xsl:param name="sep" as="xs:string">|</xsl:param>
<xsl:template match="@*|node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<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(.), for $att in mf:sort(@* except @mf:punctuation) return data($att))), ($e2/(node-name(.), for $att in mf:sort(@* except @mf:punctuation) return data($att))))"/>
</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()" group-adjacent="if (. instance of element()) then string-join((string(node-name(.)), for $att in mf:sort(@* except @mf:punctuation) return data($att)), $sep) 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>
http://xsltransform.net/asnmys