鉴于此源XML文档: input.xml
<body>
<p ilvl="1">content</p>
<p ilvl="1">content</p>
<p ilvl="2">content</p>
<p ilvl="3">content</p>
<p ilvl="1">content</p>
<p ilvl="2">content</p>
<p ilvl="2">content</p>
<p ilvl="3">content</p>
<p ilvl="1">content</p>
<p ilvl="1">content</p>
<p ilvl="3">content</p>
</body>
我想转换为output.xml:
<list>
<item>
<list>
<item>
<p ilvl="1">content</p>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2">content</p>
<list>
<item>
<p ilvl="3">content</p>
</item>
</list>
</item>
</list>
</item>
</list>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2">content</p>
等
属性ilvl是列表级别;它是一个从零开始的指数。
我尝试调整https://stackoverflow.com/a/11117548/1031689并得到输出:
<rs>
<p ilvl="1"/>
<p ilvl="1">
<p ilvl="2">
<p ilvl="3"/>
</p>
</p>
<p ilvl="1">
<p ilvl="2"/>
<p ilvl="2">
<p ilvl="3"/>
</p>
</p>
<p ilvl="1"/>
<p ilvl="1">
<p ilvl="3"/>
</p>
</rs>
我有两个问题:
在我尝试这个之前,我使用的是我自己的XSLT 1.0代码,附在下面。
棘手的部分是如何处理嵌套的减少,例如3级到1级:
<p ilvl="3">content</p>
<p ilvl="1">content</p>
已更新
我尝试在addList模板中处理这个问题,因为递归是&#34; unwound&#34;,但它还不是很正确;在我的输出中,当它返回到1级时,正在插入一个新列表,但如果我更正了,我会删除最后3个内容项......如果有人能解决这个问题,我会留下深刻的印象: - )< / p>
是的,我知道我的代码更复杂,所以如果对上面的for-each-groups方法有任何简单的解决方案,那么建议会很棒。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<!-- works, except makes new list for siblings -->
<xsl:template name="listSection">
<xsl:param name="last-level">-1</xsl:param>
<xsl:param name="items"/>
<xsl:variable name="currentItem" select="$items[1]"/>
<xsl:variable name="currentLevel">
<xsl:value-of select="number($currentItem/@ilvl)"/>
</xsl:variable>
<xsl:variable name="nextItems" select="$items[position() > 1]"/>
<xsl:choose>
<xsl:when test="$currentLevel = $last-level">
<!-- just add an item -->
<xsl:call-template name="addItem">
<xsl:with-param name="currentItem" select="$currentItem"/>
<xsl:with-param name="nextItems" select="$nextItems"/>
</xsl:call-template>
<!-- that handles next level higher case, and level same case-->
<!-- level lower is handled is addList template-->
</xsl:when>
<xsl:when test="$currentLevel > $last-level">
<xsl:call-template name="addList">
<xsl:with-param name="currentLevel" select="$last-level"/>
<xsl:with-param name="nextItems" select="$items"/> <!-- since haven't handled current item yet -->
</xsl:call-template>
</xsl:when>
<xsl:otherwise> this level < last level: should not happen?</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="addItem">
<xsl:param name="currentItem"/>
<xsl:param name="nextItems"/>
<xsl:variable name="currentLevel">
<xsl:value-of select="number($currentItem/@ilvl)"/>
</xsl:variable>
<item>
<xsl:apply-templates select="$currentItem"/>
<!-- is the next level higher?-->
<xsl:if test="(count($nextItems) > 0) and
(number($nextItems[1]/@ilvl) > $currentLevel)">
<!-- insert list/item to the necessary depth-->
<xsl:call-template name="addList">
<xsl:with-param name="currentLevel" select="$currentLevel"/>
<xsl:with-param name="nextItems" select="$nextItems"/>
</xsl:call-template>
</xsl:if>
</item>
<!-- next level same-->
<xsl:if test="(count($nextItems) > 0) and
(number($nextItems[1]/@ilvl) = $currentLevel)">
<xsl:call-template name="addItem">
<xsl:with-param name="currentItem" select="$nextItems[1]"/>
<xsl:with-param name="nextItems" select="$nextItems[position() > 1]"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="addList">
<xsl:param name="currentLevel">-1</xsl:param>
<xsl:param name="nextItems"/>
<xsl:variable name="targetLevel">
<xsl:value-of select="number($nextItems[1]/@ilvl)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$targetLevel - $currentLevel > 1">
<!-- interpolate -->
<list>
<xsl:variable name="stuff">
<item>
<xsl:call-template name="addList">
<xsl:with-param name="currentLevel" select="$currentLevel+1"/>
<xsl:with-param name="nextItems" select="$nextItems"/>
</xsl:call-template>
</item>
</xsl:variable>
<xsl:copy-of select="$stuff"/>
<xsl:variable name="currentPos" select="count(msxsl:node-set($stuff)//p)" />
<xsl:variable name="ascentLevel">
<xsl:value-of select="number($nextItems[$currentPos]/@ilvl)"/>
</xsl:variable>
<xsl:variable name="ascentItems" select="$nextItems[position() > $currentPos]"/>
<xsl:variable name="aftertargetLevel">
<xsl:value-of select="number($ascentItems[1]/@ilvl)"/>
</xsl:variable>
<xsl:if test="(count($ascentItems) > 1) and
($aftertargetLevel - $currentLevel = 1)">
<xsl:call-template name="listSection">
<xsl:with-param name="last-level" select="$currentLevel"/>
<xsl:with-param name="items" select="$ascentItems"/>
</xsl:call-template>
</xsl:if>
</list>
</xsl:when>
<xsl:when test="$targetLevel - $currentLevel = 1">
<!-- insert real item -->
<xsl:variable name="stuff">
<list>
<xsl:call-template name="addItem">
<xsl:with-param name="currentItem" select="$nextItems[1]"/>
<xsl:with-param name="nextItems" select="$nextItems[position() > 1]"/>
</xsl:call-template>
</list>
</xsl:variable>
<!-- might be items on the way out -->
<xsl:copy-of select="$stuff"/>
<xsl:variable name="currentPos" select="count(msxsl:node-set($stuff)//p)" />
<xsl:variable name="ascentLevel">
<xsl:value-of select="number($nextItems[$currentPos]/@ilvl)"/>
</xsl:variable>
<xsl:variable name="ascentItems" select="$nextItems[position() > $currentPos]"/>
<xsl:variable name="aftertargetLevel">
<xsl:value-of select="number($ascentItems[1]/@ilvl)"/>
</xsl:variable>
<xsl:if test="(count($ascentItems) > 1) and
($aftertargetLevel - $currentLevel = 1)">
<xsl:call-template name="listSection">
<xsl:with-param name="last-level" select="$currentLevel"/>
<xsl:with-param name="items" select="$ascentItems"/>
</xsl:call-template>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<!--should not happen!-->
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="body">
<xsl:call-template name="listSection">
<xsl:with-param name="items" select="*"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
答案 0 :(得分:1)
这是我第二次尝试为这个问题提供解决方案,在目前的状态下迫使人们(至少是我)想出想要的东西:
此转化:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pStartLevel" select="1"/>
<xsl:key name="kChildren" match="p"
use="generate-id(preceding-sibling::p
[not(@ilvl >= current()/@ilvl)][1])"/>
<xsl:template match="/*">
<list>
<item>
<xsl:apply-templates select="key('kChildren', '')[1]" mode="start">
<xsl:with-param name="pParentLevel" select="$pStartLevel"/>
<xsl:with-param name="pSiblings" select="key('kChildren', '')"/>
</xsl:apply-templates>
</item>
</list>
</xsl:template>
<xsl:template match="p" mode="start">
<xsl:param name="pParentLevel"/>
<xsl:param name="pSiblings"/>
<list>
<xsl:apply-templates select="$pSiblings">
<xsl:with-param name="pParentLevel" select="$pParentLevel"/>
</xsl:apply-templates>
</list>
</xsl:template>
<xsl:template match="p">
<xsl:param name="pParentLevel"/>
<xsl:apply-templates select="self::*[@ilvl - $pParentLevel > 1]"
mode="buildMissingLevels">
<xsl:with-param name="pParentLevel" select="$pParentLevel"/>
</xsl:apply-templates>
<xsl:apply-templates select="self::*[not(@ilvl - $pParentLevel > 1)]" mode="normal">
<xsl:with-param name="pParentLevel" select="$pParentLevel"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="p" mode="normal">
<xsl:param name="pParentLevel"/>
<item>
<xsl:copy-of select="."/>
<xsl:apply-templates mode="start"
select="key('kChildren',generate-id())[1]">
<xsl:with-param name="pParentLevel" select="@ilvl"/>
<xsl:with-param name="pSiblings"
select="key('kChildren',generate-id())"/>
</xsl:apply-templates>
</item>
</xsl:template>
<xsl:template match="p" mode="buildMissingLevels">
<xsl:param name="pParentLevel"/>
<item>
<p ilvl="{$pParentLevel +1}"/>
<list>
<xsl:apply-templates select=".">
<xsl:with-param name="pParentLevel" select="$pParentLevel +1"/>
</xsl:apply-templates>
</list>
</item>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档:
<body>
<p ilvl="1">content</p>
<p ilvl="1">content</p>
<p ilvl="2">content</p>
<p ilvl="3">content</p>
<p ilvl="1">content</p>
<p ilvl="2">content</p>
<p ilvl="2">content</p>
<p ilvl="3">content</p>
<p ilvl="1">content</p>
<p ilvl="1">content</p>
<p ilvl="3">content</p>
</body>
产生我认为需要的东西:
<list>
<item>
<list>
<item>
<p ilvl="1">content</p>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2">content</p>
<list>
<item>
<p ilvl="3">content</p>
</item>
</list>
</item>
</list>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2">content</p>
</item>
<item>
<p ilvl="2">content</p>
<list>
<item>
<p ilvl="3">content</p>
</item>
</list>
</item>
</list>
</item>
<item>
<p ilvl="1">content</p>
</item>
<item>
<p ilvl="1">content</p>
<list>
<item>
<p ilvl="2"/>
<list>
<item>
<p ilvl="3">content</p>
</item>
</list>
</item>
</list>
</item>
</list>
</item>
</list>