问题
I have looked at Muenchian Grouping - group within a node, not within the entire document but it is not quite working for me. The Muenchian method alone does not do it either for me.
I have also looked at XSLT 1.0: grouping and removing duplicate but cannot follow it completely.
I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<MT_MATERIALDATA>
<items item="475053">
<Recordset>
<CodeBusinessUnit>99</CodeBusinessUnit>
<PriceValue>250</PriceValue>
</Recordset>
<Recordset>
<CodeBusinessUnit>1</CodeBusinessUnit>
<PriceValue>250</PriceValue>
</Recordset>
</items>
<items item="475054">
<Recordset>
<CodeBusinessUnit>1</CodeBusinessUnit>
<PriceValue>255.34</PriceValue>
</Recordset>
<Recordset>
<CodeBusinessUnit>10</CodeBusinessUnit>
<PriceValue>299</PriceValue>
</Recordset>
</items>
</MT_MATERIALDATA>
The outcome should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<MT_MATERIALDATA>
<Mi item="475053">
<PriceList>
<Prices>
<Price Value="250"/>
<PriceConfig>
<Stores>99,1</Stores>
</PriceConfig>
</Prices>
</PriceList>
</Mi>
<Mi item="475054">
<PriceList>
<Prices>
<Price Value="255.34"/>
<PriceConfig>
<Stores>1</Stores>
</PriceConfig>
</Prices>
<Prices>
<Price Value="299"/>
<PriceConfig>
<Stores>10</Stores>
</PriceConfig>
</Prices>
</PriceList>
</Mi>
</MT_MATERIALDATA>
So for matching <PriceValue>
elements
in <Recordset>
, all respective <CodeBusinessUnits>
need to be listed in <Stores>
.
If not, an extra <Prices>
node needs to be created.
I have been trying for hours but either the Store-numbers are always duplicate or they are not aggregated even if the PriceValue is the same. I appreciate any help, I don't know what to do anymore. Thank you for your help!
Best regards, Peter
回答1:
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kPriceByValAndItem" match="PriceValue"
use="concat(../../@item, '|', .)"/>
<xsl:template match="/*">
<MT_MATERIALDATA>
<xsl:apply-templates/>
</MT_MATERIALDATA>
</xsl:template>
<xsl:template match="items">
<MI item="{@item}">
<PriceList>
<xsl:for-each select=
"*/PriceValue
[generate-id()
=
generate-id(key('kPriceByValAndItem',
concat(../../@item, '|', .)
)[1]
)
]
">
<Prices>
<Price Value="{.}"/>
<PriceConfig>
<Stores>
<xsl:for-each select=
"key('kPriceByValAndItem',
concat(../../@item, '|', .)
)">
<xsl:value-of select="../CodeBusinessUnit"/>
<xsl:if test="not(position()=last())">,</xsl:if>
</xsl:for-each>
</Stores>
</PriceConfig>
</Prices>
</xsl:for-each>
</PriceList>
</MI>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<MT_MATERIALDATA>
<items item="475053">
<Recordset>
<CodeBusinessUnit>99</CodeBusinessUnit>
<PriceValue>250</PriceValue>
</Recordset>
<Recordset>
<CodeBusinessUnit>1</CodeBusinessUnit>
<PriceValue>250</PriceValue>
</Recordset>
</items>
<items item="475054">
<Recordset>
<CodeBusinessUnit>1</CodeBusinessUnit>
<PriceValue>255.34</PriceValue>
</Recordset>
<Recordset>
<CodeBusinessUnit>10</CodeBusinessUnit>
<PriceValue>299</PriceValue>
</Recordset>
</items>
</MT_MATERIALDATA>
produces the wanted, correct result:
<MT_MATERIALDATA>
<MI item="475053">
<PriceList>
<Prices>
<Price Value="250"/>
<PriceConfig>
<Stores>99,1</Stores>
</PriceConfig>
</Prices>
</PriceList>
</MI>
<MI item="475054">
<PriceList>
<Prices>
<Price Value="255.34"/>
<PriceConfig>
<Stores>1</Stores>
</PriceConfig>
</Prices>
<Prices>
<Price Value="299"/>
<PriceConfig>
<Stores>10</Stores>
</PriceConfig>
</Prices>
</PriceList>
</MI>
</MT_MATERIALDATA>
回答2:
I think the following solves the problem, at least for the grouping:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="k1" match="items/Recordset" use="concat(generate-id(..), '|', PriceValue)"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items">
<Mi item="{@item}">
<PriceList>
<xsl:apply-templates select="Recordset[generate-id() = generate-id(key('k1', concat(generate-id(..), '|', PriceValue))[1])]"/>
</PriceList>
</Mi>
</xsl:template>
<xsl:template match="Recordset">
<Prices>
<Price Value="{PriceValue}"/>
<PriceConfig>
<Stores>
<xsl:apply-templates select="key('k1', concat(generate-id(..), '|', PriceValue))/CodeBusinessUnit"/>
</Stores>
</PriceConfig>
</Prices>
</xsl:template>
<xsl:template match="CodeBusinessUnit">
<xsl:if test="position() > 1">,</xsl:if>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
回答3:
I'm also going to post an stylesheet, because everybody do it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kBUnitByItem-Price"
match="CodeBusinessUnit"
use="concat(../../@item, '++', ../PriceValue)"/>
<xsl:template match="/">
<MT_MATERIALDATA>
<xsl:apply-templates/>
</MT_MATERIALDATA>
</xsl:template>
<xsl:template match="items">
<MI item="{@item}">
<PriceList>
<xsl:apply-templates/>
</PriceList>
</MI>
</xsl:template>
<xsl:template match="CodeBusinessUnit[
count(.|key('kBUnitByItem-Price',
concat(../../@item,'++',../PriceValue)
)[1]
) = 1
]">
<Prices>
<Price Value="{../PriceValue}"/>
<PriceConfig>
<Stores>
<xsl:apply-templates
select="key('kBUnitByItem-Price',
concat(../../@item,'++',../PriceValue))"
mode="sequence"/>
</Stores>
</PriceConfig>
</Prices>
</xsl:template>
<xsl:template match="text()"/>
<xsl:template match="node()" mode="sequence">
<xsl:if test="position()!=1">,</xsl:if>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Note: Grouping stores by item and price. A little more pull than push style (That's because there is no duplicate @item.)
Output:
<MT_MATERIALDATA>
<MI item="475053">
<PriceList>
<Prices>
<Price Value="250" />
<PriceConfig>
<Stores>99,1</Stores>
</PriceConfig>
</Prices>
</PriceList>
</MI>
<MI item="475054">
<PriceList>
<Prices>
<Price Value="255.34" />
<PriceConfig>
<Stores>1</Stores>
</PriceConfig>
</Prices>
<Prices>
<Price Value="299" />
<PriceConfig>
<Stores>10</Stores>
</PriceConfig>
</Prices>
</PriceList>
</MI>
</MT_MATERIALDATA>
I think we cover all the variations: key value, push-pull, sequence separator condition.
来源:https://stackoverflow.com/questions/5067633/grouping-elements-and-deleting-duplicate-nodes-xslt-1-0