tags:

views:

104

answers:

2

Say I have an xml that looks like so:

<Items>
  <ItemType1>A</ItemType1>
  <ItemValue1>100</ItemValue1>
  <IteType2>B</ItemType2>
  <ItemValue2>10</ItemValue2>
  <ItemType3>C</ItemType3>
  <ItemValue3>0</ItemValue3>
  <ItemType4>D</ItemType4>
  <ItemValue4>50</ItemValue4>
</Items>

And I'm interested in getting the content of ItemValueX where ItemTypeX value is C. In this case, I should be getting 0 (ItemType3 = C, ItemValue3 = 0)

I need to do this in XSLT.

So the end result might be something like:

<ChoosenItem>
  <ItemType>C</ItemType>
  <ItemValue>0</ItemValue>
</ChoosenItem>

Any idea?

+2  A: 

This works, assuming of course that you will always have matched pairs of ItemType and ItemValue

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:template match="/">
  <xsl:for-each select="/Items/*">
   <xsl:if test=". = 'D'">
    <xsl:variable name="pos" select="position() + 1"/>
    <ChoosenItem>
     <ItemType><xsl:value-of select="."/></ItemType>
     <ItemValue><xsl:value-of select="/Items/*[$pos]"/></ItemValue>
    </ChoosenItem>
   </xsl:if>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

Which should produce the following XML:

<?xml version="1.0" encoding="ISO-8859-1"?>
<ChoosenItem>
    <ItemType>D</ItemType>
    <ItemValue>50</ItemValue>
</ChoosenItem>
James
+2  A: 

Try this:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

  <xsl:param name="itemValue">C</xsl:param>
  <xsl:template match="/Items">
    <xsl:apply-templates 
         select="//*[starts-with(name(), 'ItemType') and . = $itemValue]" />
  </xsl:template>

  <xsl:template match="*">
    <ChoosenItem>
      <ItemType><xsl:value-of select="." /></ItemType>
      <ItemValue><xsl:value-of select="following-sibling::*" /></ItemValue>
    </ChoosenItem>
  </xsl:template>

</xsl:stylesheet>

Another approach, avoiding match="*"

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

  <xsl:param name="itemValue">C</xsl:param>
  <xsl:template match="/Items">
      <xsl:call-template name="GetItem">
        <xsl:with-param name="id"
             select="//*[starts-with(name(), 'ItemType') and . = $itemValue]" />
      </xsl:call-template>
  </xsl:template>

  <xsl:template name="GetItem">
    <xsl:param name="id" />
    <ChoosenItem>
      <ItemType><xsl:value-of select="$id" /></ItemType>
      <ItemValue><xsl:value-of select="$id/following-sibling::*" /></ItemValue>
    </ChoosenItem>
  </xsl:template>

</xsl:stylesheet>
Rubens Farias
This is a better approach as it avoids having to do a `for-each` only every child node, and the code looks cleaner :)
James
following-sibling::* just came up after reading your answer, so thank you too =)
Rubens Farias
This is exactly what I need and it's clean like James said :) Thanks.
Jimmy Chandra
How about when the input xml is sorted by node type, is there an alternative to `following-sibling`? Something in the way of `select="ReplaceTypeXByValueX"`?
xtofl
you could build entire tag name: //*[name() = ItemValue' + translate($typename, 'ItemType', '')]
Rubens Farias