我有许多类似于以下a.xml文件的文件,尽管它们要大得多:
<?xml version="1.0" encoding="UTF-8"?>
<a version="3.0">
<b bb="P1">
<!--============== b:P1 c:1 ==============-->
<c cc="1">
<d dd="61">d1
</d>
</c>
<!--============== b:P1 c:2 ==============-->
<c cc="2">
<d dd="17">d2
</d>
</c>
</b>
</a>
对于每个c,前面只有一个注释。
我要输出的文件与以下a.csv文件的结构相同:
1|1|a|0| |0| |0|
2|1|a|1|b|0| |0|
3|1|a|1|b|1|!|0|
3|1|a|1|b|2|c|0|
4|1|a|1|b|2|c|1|d
3|1|a|1|b|3|!|0|
3|1|a|1|b|4|c|0|
4|1|a|1|b|4|c|1|d
它代表a.xml的层次树:
字段1是层次结构级别。例如,a具有1级,b具有2级,依此类推。
字段2、4、6和8等于:
字段3、5、7和9等于:
在此示例中,级别3包含注释。
我找不到一种很好的方法来进行同时包含节点和注释的for-each。当我使用<xsl:for-each select="*">
时,我只会遍历节点。
因此,我提出了以下xslt,用于检查当前节点前面是否有注释:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:for-each select="*">
<xsl:variable name="elm01" select="local-name()" />
<xsl:text>1</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|0| |0| |0|</xsl:text>
<xsl:text> </xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm02" select="local-name()" />
<xsl:text>2</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|0| |0|</xsl:text>
<xsl:text> </xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm03" select="local-name()" />
<xsl:if test="preceding-sibling::comment()[1]">
<xsl:text>3</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|!</xsl:text>
<xsl:text>|0|</xsl:text>
<xsl:text> </xsl:text>
</xsl:if>
<xsl:text>3</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<!-- TODO: I want to count the total of preceding siblings and comments -->
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|comment())+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
<xsl:text>|0|</xsl:text>
<xsl:text> </xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm04" select="local-name()" />
<xsl:text>4</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm04" />
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
但是,当我运行以下命令时:
xsltproc a.xslt a.xml > a.csv
我得到以下a.csv文件:
1|1|a|0| |0| |0|
2|1|a|1|b|0| |0|
3|1|a|1|b|1|!|0|
3|1|a|1|b|1|c|0|
4|1|a|1|b|1|c|1|d
3|1|a|1|b|2|!|0|
3|1|a|1|b|2|c|0|
4|1|a|1|b|2|c|1|d
请注意,字段6不正确:
第一个注释以及第一个节点c及其子节点都等于1
第二个注释以及第二个节点c及其子节点都等于2
您有什么建议的解决方案吗?
解决方案(作者蒂姆)
我现在可以通过使用以下xslt文件获得正确的输出:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:for-each select="*">
<xsl:variable name="elm01" select="local-name()" />
<xsl:text>1</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|0| |0| |0|</xsl:text>
<xsl:text> </xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm02" select="local-name()" />
<xsl:text>2</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|0| |0|</xsl:text>
<xsl:text> </xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm03" select="local-name()" />
<xsl:if test="preceding-sibling::comment()[1]">
<xsl:text>3</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment())"/>
<xsl:text>|!</xsl:text>
<xsl:text>|0|</xsl:text>
<xsl:text> </xsl:text>
</xsl:if>
<xsl:text>3</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment())+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
<xsl:text>|0|</xsl:text>
<xsl:text> </xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm04" select="local-name()" />
<xsl:text>4</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*|../preceding-sibling::comment())+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm04" />
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
或者,您也可以在蒂姆的回复中使用xslt,从而避免重复。
答案 0 :(得分:1)
您想要的表情是这个...
<xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment()) + 1" />
或者这也可以工作...
<xsl:value-of select="count(preceding-sibling::node()[self::*|self::comment()]) + 1" />
但是您也可以使用xsl:number
<xsl:number count="*|comment()" />
尽管如此,您的样式表似乎有些过于复杂,而且重复很多。尝试使用这种更通用的方法。递归地调用每个级别,每次调用都传递到构造的行中,以节省每次构建它的时间。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:param name="maxLevel" select="4" />
<xsl:template match="*|comment()">
<xsl:param name="level" select="1" />
<xsl:param name="prev" />
<xsl:variable name="new">
<xsl:value-of select="$prev" />
<xsl:text>|</xsl:text>
<xsl:number count="*|comment()" />
<xsl:text>|</xsl:text>
<xsl:choose>
<xsl:when test="self::*">
<xsl:value-of select="local-name()" />
</xsl:when>
<xsl:otherwise>
<xsl:text>!</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$level" />
<xsl:value-of select="$new" />
<xsl:call-template name="pad">
<xsl:with-param name="levels" select="$maxLevel - $level" />
</xsl:call-template>
<xsl:text> </xsl:text>
<xsl:apply-templates select="*|comment()">
<xsl:with-param name="level" select="$level + 1" />
<xsl:with-param name="prev" select="$new" />
</xsl:apply-templates>
</xsl:template>
<xsl:template name="pad">
<xsl:param name="levels" />
<xsl:if test="$levels > 0">
<xsl:text>|0| </xsl:text>
<xsl:call-template name="pad">
<xsl:with-param name="levels" select="$levels - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>