Grouping elements and deleting duplicate nodes - XSLT 1.0

情到浓时终转凉″ 提交于 2019-12-14 00:35:58

问题


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() &gt; 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!