我希望得到一些关于如何使用XSLT 1.0在无序列表中对具有相同名称的相邻元素进行分组的建议。
以下是一些XML示例:
<Article>
<TextContent>
<p>lorem</p>
<VisualContent id="1" />
<VisualContent id="2" />
<VisualContent id="3" />
<p>ipsum</p>
<VisualContent id="4" />
<p>dolor</p>
<VisualContent id="5" />
</TextContent>
</Article>
这是我想要的输出:
<Article>
<HtmlContent>
<p>lorem</p>
<ul>
<li><img data-id="1" /></li>
<li><img data-id="2" /></li>
<li><img data-id="3" /></li>
</ul>
<p>ipsum</p>
<img data-id="4" />
<p>dolor</p>
<img data-id="5" />
</HtmlContent>
</Article>
不幸的是,XSLT 1.0是对此的严格要求。任何建议都将不胜感激。
答案 0 :(得分:2)
我已跳过<TextContent>
变为<HtmlContent>
且<VisualContent id="n" />
变为<img data-id="n" />
的部分,因为如果没有这些分心,这个问题就很难了。
我选择的方法会查看前面的第一个兄弟,其名称不与当前元素的名称相同。该兄弟的唯一ID是可以将相同名称的相邻元素分组的关键:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="prevByName" match="TextContent/*" use="generate-id(preceding-sibling::*[not(name()=name(current()))][1])" />
<xsl:template match="/">
<Article><TextContent>
<xsl:for-each select="Article/TextContent/*[generate-id()=generate-id(key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))[1])]">
<xsl:variable name="myGroup" select="key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))" />
<xsl:choose>
<xsl:when test="count($myGroup) > 1">
<ul>
<xsl:for-each select="$myGroup">
<li><xsl:copy-of select="."/></li>
</xsl:for-each>
</ul>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</TextContent></Article>
</xsl:template>
</xsl:stylesheet>
应用于输入示例时,会产生以下结果:
<?xml version="1.0" encoding="utf-8"?>
<Article>
<TextContent>
<p>lorem</p>
<ul>
<li>
<VisualContent id="1"/>
</li>
<li>
<VisualContent id="2"/>
</li>
<li>
<VisualContent id="3"/>
</li>
</ul>
<p>ipsum</p>
<VisualContent id="4"/>
<p>dolor</p>
<VisualContent id="5"/>
</TextContent>
</Article>
修改强> 这是一个修改版本,只对相邻的“VisualContent”元素进行分组:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="prevByName" match="TextContent/*" use="generate-id(preceding-sibling::*[not(name()=name(current()))][1])" />
<xsl:template match="/Article/TextContent">
<Article><TextContent>
<xsl:apply-templates select="*"/>
</TextContent></Article>
</xsl:template>
<xsl:template match="TextContent/*[not(self::VisualContent)]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="VisualContent[generate-id()=generate-id(key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))[1])]">
<xsl:variable name="myGroup" select="key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))" />
<xsl:choose>
<xsl:when test="count($myGroup) > 1">
<ul>
<xsl:for-each select="$myGroup">
<li><xsl:copy-of select="."/></li>
</xsl:for-each>
</ul>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
这可能会使用一些精简,但我认为原则很明确,我需要睡一觉......
答案 1 :(得分:0)
我也是XSLT的新手。
这不是您的预期输出,我无法获得<ul>
,但看看您是否可以在此基础上构建。我也会尝试。
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8" />
<xsl:template match="/">
<Article>
<HtmlContent>
<xsl:apply-templates />
</HtmlContent>
</Article>
</xsl:template>
<xsl:template match="VisualContent">
<li>
<xsl:element name="img">
<xsl:attribute name="date-id"><xsl:number/></xsl:attribute>
</xsl:element>
</li>
</xsl:template>
<xsl:template match="p">
<xsl:element name="p">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
这是输出:
<Article>
<HtmlContent>
<p>lorem</p>
<li><img date-id="1" /></li>
<li><img date-id="2" /></li>
<li><img date-id="3" /></li>
<p>ipsum</p>
<li><img date-id="4" /></li>
<p>dolor</p>
<li><img date-id="5" /></li>
</HtmlContent>
</Article>
答案 2 :(得分:0)
在发现之后,我需要当前版本的xsltproc(1.1.29),我进一步更改了样式表。分组仅适用于TextContent的子项,但我需要分组才能处理任何嵌套级别。
以下是我的意见:
<Article>
<TextContent>
<p>lorem</p>
<VisualContent id="1" />
<VisualContent id="2" />
<VisualContent id="3" />
<p>ipsum</p>
<VisualContent id="4" />
<p>dolor</p>
<p>sit</p>
<VisualContent id="5" />
</TextContent>
<p>amet</p>
<VisualContent id="6" />
<VisualContent id="7" />
<p>consectetur</p>
<VisualContent id="8" />
</Article>
我使用以下样式表:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<!-- IdentityTransform -->
<xsl:template match="/ | @* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<xsl:key name="prevByName" match="*/*" use="generate-id(preceding-sibling::*[not(name()=name(current()))][1])" />
<xsl:template match="VisualContent">
<xsl:if test="generate-id()=generate-id(key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))[1])">
<xsl:variable name="myGroup" select="key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))" />
<xsl:choose>
<xsl:when test="count($myGroup) > 1">
<ul>
<xsl:for-each select="$myGroup">
<li>
<xsl:copy-of select="."/>
</li>
</xsl:for-each>
</ul>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
现在,分组适用于任何元素级别。 : - )
<?xml version="1.0" encoding="utf-8"?>
<Article>
<TextContent>
<p>lorem</p>
<ul><li><VisualContent id="1"/></li><li><VisualContent id="2"/></li><li><VisualContent id="3"/></li></ul>
<p>ipsum</p>
<VisualContent id="4"/>
<p>dolor</p>
<p>sit</p>
<VisualContent id="5"/>
</TextContent>
<p>amet</p>
<ul><li><VisualContent id="6"/></li><li><VisualContent id="7"/></li></ul>
<p>consectetur</p>
<VisualContent id="8"/>
</Article>