tags:

views:

49

answers:

2

I need to transform the incoming XML so that I can extract all "item" with "categorie" is equal "two" and move these into a separate "records" node with an attribute initialized as type="two".

Here's a sample of the incoming XML.

<datafeed>
<records type="one">
    <purchases>
    <items>
            <item>
                <categorie>one</categorie>
                <intrant>String</intrant>
            </item>
            <item>
                <categorie>two</categorie>
                <intrant>String</intrant>
            </item>
            <item>
                <categorie>one</categorie>
                <intrant>String</intrant>
            </item>
            <item>
                <categorie>two</categorie>
                <intrant>String</intrant>
            </item>                         
        </items>
    </purchases>
    </records>
<exchange/>
<context/>
<pilotage/>
</datafeed>

Here's what I would like:

<datafeed>
<records type="one">
    <purchases>
    <items>
            <item>
                <categorie>one</categorie>
                <intrant>String</intrant>
            </item>
            <item>
                <categorie>one</categorie>
                <intrant>String</intrant>
            </item>                 
        </items>
    </purchases>
    </records>
  <records type="two">
    <purchases>
    <items>
            <item>
                <categorie>two</categorie>
                <intrant>String</intrant>
            </item>
            <item>
                <categorie>two</categorie>
                <intrant>String</intrant>
            </item>                         
        </items>
    </purchases>
    </records>
<exchange/>
<context/>
<pilotage/>
</datafeed>

I now have two "records" both initialize with it's predefined type (always one or two). The records that were extracted, were moved, hence deleted from the original record.

Thanks

+3  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kitemByCategory" match="item"
  use="categorie"/>

 <xsl:template match="node()|@*" name="identity">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="records">
  <xsl:call-template name="identity"/>

  <xsl:variable name="vCat2Items" select=
   "key('kitemByCategory', 'two')"/>

  <xsl:if test="$vCat2Items">
    <records type="two">
        <purchases>
          <items>
            <xsl:copy-of select="$vCat2Items"/>
          </items>
        </purchases>
    </records>
  </xsl:if>
 </xsl:template>

 <xsl:template match="item[categorie = 'two']"/>
</xsl:stylesheet>

when applied on the provided XML document, produces the wanted, correct result:

<datafeed>
   <records type="one">
      <purchases>
         <items>
            <item>
               <categorie>one</categorie>
               <intrant>String</intrant>
            </item>
            <item>
               <categorie>one</categorie>
               <intrant>String</intrant>
            </item>
         </items>
      </purchases>
   </records>
   <records type="two">
      <purchases>
         <items>
            <item>
               <categorie>two</categorie>
               <intrant>String</intrant>
            </item>
            <item>
               <categorie>two</categorie>
               <intrant>String</intrant>
            </item>
         </items>
      </purchases>
   </records>
   <exchange/>
   <context/>
   <pilotage/>
</datafeed>

Do note:

  1. The use and overriding of the identity rule.

  2. How items of categorie "two" are excluded from processing by using an empty template matching them.

  3. The use of keys for efficient and convenient locationg of items by categorie.

Dimitre Novatchev
+1 for you as well :-D
gef
excellent, thank you, works like a charm.
Brian
+2  A: 

With this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:key name="itemsBycategorie" match="item" use="categorie"/>
    <xsl:template match="@*|node()">
        <xsl:param name="items"/>
        <xsl:copy>
            <xsl:apply-templates select="@*|node()">
                <xsl:with-param name="items" select="$items"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="records">
        <xsl:variable name="me" select="."/>
        <xsl:for-each select="*/*/*[count(.|key('itemsBycategorie',categorie)[1])=1]">
            <records type="{categorie}">
                <xsl:apply-templates select="$me/node()">
                    <xsl:with-param name="items" select="key('itemsBycategorie',categorie)"/>
                </xsl:apply-templates>
            </records>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="items">
        <xsl:param name="items"/>
        <xsl:copy>
            <xsl:apply-templates select="$items"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Result:

<datafeed>
    <records type="one">
        <purchases>
            <items>
                <item>
                    <categorie>one</categorie>
                    <intrant>String</intrant>
                </item>
                <item>
                    <categorie>one</categorie>
                    <intrant>String</intrant>
                </item>
            </items>
        </purchases>
    </records>
    <records type="two">
        <purchases>
            <items>
                <item>
                    <categorie>two</categorie>
                    <intrant>String</intrant>
                </item>
                <item>
                    <categorie>two</categorie>
                    <intrant>String</intrant>
                </item>
            </items>
        </purchases>
    </records>
    <exchange></exchange>
    <context></context>
    <pilotage></pilotage>
</datafeed>

Note: Muenchian Method of grouping. And "poor man's tunnel" params (Dimitre quot).

Alejandro
+1 for taking the time to do someone's homework ;-)
gef
"tunnel params" in XSLT 1.0 ???
Dimitre Novatchev
@Dimitre: Yor are right! I should wrote that into quots. Ja! But, what else can you call this pattern?
Alejandro
I'd call this "poor man's tunnel patterns" :)
Dimitre Novatchev
@Dimitre: Ja! Well, yes! Sure, I am a poor man!
Alejandro
thanks alejandro, this also worked. cheers.
Brian