XML with nested elements filtering with calculation using XSLT v1.0 C#
I have got a sample xml file where root has element with name "element". This elements can be nested.
I would like to exclude elements with name "position" where "position" value x = ("number" + "another") * count is greater than average of sum(("number" + "another") * "count") from all "position" elements.
How to process this xml file with xslt v 1.
<?xml version="1.0" encoding="utf-8" ?>
<root>
<element>
<position>
<number>
1
</number>
<another>
2
</another>
<count>
3
</count>
</position>
<position>
<number>
3
</number>
<another>
1
</another>
<count>
5
</count>
</position>
<element>
<position>
<number>
3
</number>
<another>
3
</another>
<count>
5
</count>
</position>
<position>
<number>
3
</number>
<another>
6
</another>
<count>
5
</count>
</position>
<element>
<position>
<number>
3
</number>
<another>
3
</another>
<count>
5
</count>
</position>
<position>
<number>
3
</number>
<another>
7
</another>
<count>
5
</count>
</position>
<element>
<position>
<number>
33
</number>
<another>
4
</another>
<count>
5
</count>
</position>
<position>
<number>
34
</number>
<another>
3
</another>
<count>
5
</count>
</position>
</element>
</element>
</element>
</element>
<element>
<position>
<number>
5
</number>
<another>
1
</another>
<count>
2
</count>
</position>
<position>
<number>
3
</number>
<another>
3
</another>
<count>
9
</count>
</position>
<element>
<position>
<number>
5
</number>
<another>
3
</another>
<count>
2
</count>
</position>
<position>
<number>
3
</number>
开发者_如何学编程 <another>
3
</another>
<count>
5
</count>
</position>
</element>
</element>
</root>
I would use the following two-pass approach:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes"/>
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="/*" mode="getScore"/>
</xsl:variable>
<xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)/*"/>
<xsl:variable name="vAverage" select=
"sum($vPass1//position/@score) div count($vPass1//position)"/>
<xsl:template match="node()|@*" name="identity" mode="getScore">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="getScore"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$vPass1"/>
</xsl:template>
<xsl:template match="position" mode="getScore">
<position score="{(number + another)*count}">
<xsl:apply-templates mode="getScore"/>
</position>
</xsl:template>
<xsl:template match="position">
<xsl:if test="not(@score > $vAverage)">
<position>
<xsl:apply-templates mode="getScore"/>
</position>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
<element>
<position>
<number>1</number>
<another>2</another>
<count>3</count>
</position>
<position>
<number>3</number>
<another>1</another>
<count>5</count>
</position>
<element>
<position>
<number>3</number>
<another>3</another>
<count>5</count>
</position>
<position>
<number>3</number>
<another>6</another>
<count>5</count>
</position>
<element>
<position>
<number>3</number>
<another>3</another>
<count>5</count>
</position>
<position>
<number>3</number>
<another>7</another>
<count>5</count>
</position>
<element>
<position>
<number>33</number>
<another>4</another>
<count>5</count>
</position>
<position>
<number>34</number>
<another>3</another>
<count>5</count>
</position>
</element>
</element>
</element>
</element>
<element>
<position>
<number>5</number>
<another>1</another>
<count>2</count>
</position>
<position>
<number>3</number>
<another>3</another>
<count>9</count>
</position>
<element>
<position>
<number>5</number>
<another>3</another>
<count>2</count>
</position>
<position>
<number>3</number>
<another>3</another>
<count>5</count>
</position>
</element>
</element>
</root>
the wanted, correct result is produced:
<root>
<element>
<position>
<number>1</number>
<another>2</another>
<count>3</count>
</position>
<position>
<number>3</number>
<another>1</another>
<count>5</count>
</position>
<element>
<position>
<number>3</number>
<another>3</another>
<count>5</count>
</position>
<position>
<number>3</number>
<another>6</another>
<count>5</count>
</position>
<element>
<position>
<number>3</number>
<another>3</another>
<count>5</count>
</position>
<position>
<number>3</number>
<another>7</another>
<count>5</count>
</position>
<element>
</element>
</element>
</element>
</element>
<element>
<position>
<number>5</number>
<another>1</another>
<count>2</count>
</position>
<position>
<number>3</number>
<another>3</another>
<count>9</count>
</position>
<element>
<position>
<number>5</number>
<another>3</another>
<count>2</count>
</position>
<position>
<number>3</number>
<another>3</another>
<count>5</count>
</position>
</element>
</element>
</root>
Just for fun, this stylesheet without extensions:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vAvgPosition">
<xsl:apply-templates select="/descendant::position[1]"
mode="avgPosition"/>
</xsl:variable>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="position" mode="avgPosition">
<xsl:param name="pSum" select="0"/>
<xsl:param name="pCount" select="0"/>
<xsl:variable name="vSum"
select="$pSum + (number + another) * count"/>
<xsl:variable name="vCount" select="$pCount + 1"/>
<xsl:variable name="vNext" select="following::position[1]"/>
<xsl:apply-templates select="$vNext" mode="avgPosition">
<xsl:with-param name="pSum" select="$vSum"/>
<xsl:with-param name="pCount" select="$vCount"/>
</xsl:apply-templates>
<xsl:if test="not($vNext)">
<xsl:value-of select="$vSum div $vCount"/>
</xsl:if>
</xsl:template>
<xsl:template match="position">
<xsl:if test="not((number + another) * count > $vAvgPosition)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output:
<root>
<element>
<position>
<number>1</number>
<another>2</another>
<count>3</count>
</position>
<position>
<number>3</number>
<another>1</another>
<count>5</count>
</position>
<element>
<position>
<number>3</number>
<another>3</another>
<count>5</count>
</position>
<position>
<number>3</number>
<another>6</another>
<count>5</count>
</position>
<element>
<position>
<number>3</number>
<another>3</another>
<count>5</count>
</position>
<position>
<number>3</number>
<another>7</another>
<count>5</count>
</position>
<element></element>
</element>
</element>
</element>
<element>
<position>
<number>5</number>
<another>1</another>
<count>2</count>
</position>
<position>
<number>3</number>
<another>3</another>
<count>9</count>
</position>
<element>
<position>
<number>5</number>
<another>3</another>
<count>2</count>
</position>
<position>
<number>3</number>
<another>3</another>
<count>5</count>
</position>
</element>
</element>
</root>
Look how compact an XSLT 2.0 solution is:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vAvgPosition"
select="avg(//position/((number+another)*count))"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="position[(number+another)*count > $vAvgPosition]"/>
</xsl:stylesheet>
精彩评论