Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Westi on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Help with XSL, <xsl:for-each ...>

Status
Not open for further replies.

Sidney5

Programmer
Nov 2, 2006
12
GB
Hi

Below is a sample of an XML file that I have. In every <Entity>, 3 tags always appear exactly once, which are <Name>, <Date> and <Speed>.
The other two tags, <Setting> and <Label> are a pair and they may appear any number of times. For instance, an <Entity> could contain 3 <Setting> and <Label> and another may contain 5.

<Entity>
<Name> a </Name>
<Date> b </Date>
<Speed>
<Setting> c </Setting>
<Label> d </Label>

<Setting> e </Setting>
<Label> f </Label>

<Setting> g </Setting>
<Label> h </Label>

</Speed>
</Entity>

I am trying to produce a XSL for give output something like

Name Date Setting Label
a b c d
a b e f
a b g h

Each time there is a <Setting> and <Label>, a new row gets output with the same <Name>, <Date> but with new <Setting> and <Label>.

Please can someone give me some sample code of a XSL to achieve this.

Many thanks
 
Please post the XSL you have already created, and we can help you with any problems you might be having.

If you need a tutorial on XSLT, which happens to build an example that is quite close to what you are wanting, look here. Some hints...

The way your document is structured, you will need to use either the Setting or Label element in the select of your xsl:for-each. Assuming you use the Setting element, you will need to use the following-sibling axis to refer to the corresponding Label element. This is explained in the final post in this thread thread426-1293307.

Again, since the context within the xsl:for-each will be a Setting element, you can obtain the value-of the Name and Date elements by using the ancestor axis (abbreviated '..').

So, let's see what your attempt produces.

Tom Morrison
 
Hi

Here is what my XML file looks like:

<root>
<entity>
<name>C</name>
<settingDate>2006-06-23</settingDate>
<label>dwg</label>
<defaultSpeed>Base</defaultSpeed>
<schedules>
<schedule>
<speed>Low</speed>
<items>
<item>
<date>2006-06-01</date>
<factor>0.9988</factor>
</item>
<item>
<date>2006-06-20</date>
<factor>0.9975</factor>
</item>

....
</entity>
<entity> ……


Here is my XSL file so far.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="
<xsl:template match="/">
<html>
<body>
<table>
<tr>
<th>Name</th>
<th>Setting Date</th>
<th>Label</th>
<th>Default Speed</th>
<th>Speed</th>
<th>Date</th>
<th>Factor</th>
</tr>

<xsl:for-each select="root/entity">
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="settingDate"/></td>
<td><xsl:value-of select="label"/></td>
<td><xsl:value-of select="defaultSpeed"/></td>
<td><xsl:value-of select="schedules/schedule/speed"/></td>
<td><xsl:value-of select="schedules/schedule/items/item[1]/date"/></td>
<td><xsl:value-of select="schedules/schedule/items/item[1]/factor"/></td>
</tr>

<xsl:for-each select="schedules/schedule/items/item[position()>1]">
<tr>
<td></td> <td></td> <td></td> <td></td> <td></td>
<td><xsl:value-of select="date"/></td>
<td><xsl:value-of select="factor"/></td>
</tr>
</xsl:for-each>

</xsl:for-each>

</table>
</body>
</html>

</xsl:template>
</xsl:stylesheet>

This works to some extent but each time. However, when there is a new <date> and <factor> a new row should get output with the existing <name>, <starting date>, etc. I need to modify the line in red so that it works.
Can you provide any assistance please, thanks
 
Is it not doing so, adding a new row and that's what you want(?), right now? or I miss something...
 
Maybe you mean you want your first post showing? In that case, it is this which is much more general and less clumsy using position() and singling out position 1 as what you have right now.
[tt]
<xsl:template match="/">
<html>
<body>
<table border="1">
<tr>
<th>Name</th>
<th>Setting Date</th>
<th>Label</th>
<th>Default Speed</th>
<th>Speed</th>
<th>Date</th>
<th>Factor</th>
</tr>
<xsl:for-each select="root/entity">
<xsl:for-each select="schedules/schedule/items/item">
<tr>
<td><xsl:value-of select="../../../../name"/></td>
<td><xsl:value-of select="../../../../settingDate"/></td>
<td><xsl:value-of select="../../../../label"/></td>
<td><xsl:value-of select="../../../../defaultSpeed"/></td>
<td><xsl:value-of select="../../speed"/></td>
<td><xsl:value-of select="date"/></td>
<td><xsl:value-of select="factor"/></td>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
[/tt]
 
Hi

Thank you very much! That seems to work now :)
I was in the process of typing a response to your first reply and this is how far I got when I got your solution ....

Each entity has got one Name, Setting Date, Label, Default Speed, and Speed tag but it can contain one or more <item> tags. Each <item> tag has got a pair of <date> and <factor> tags.

My logic is supposed to be: when there is another <item> tag, create a new row with the same Name, Setting Date, Label, Default Speed, and Speed values but with the new <date> and <factor> value.

cheers
 
Let me show a slight variation of tsuji's work.
Code:
<xsl:template match="/">
  <html>
  <body>
    <table border="1">
      <tr>
        <th>Name</th>
        <th>Setting Date</th>
        <th>Label</th>
        <th>Default Speed</th>
        <th>Speed</th>
        <th>Date</th>
        <th>Factor</th>
      </tr>
      <xsl:for-each select="root/entity">
        <xsl:variable name="entityName" select="name"/>
        <xsl:variable name="entitySettingDate" select="settingDate"/>
        <xsl:variable name="entityLabel" select="label"/>
        <xsl:variable name="entityDefaultSpeed" select="defaultSpeed"/>
        <xsl:for-each select="schedules/schedule">
          <xsl:variable name="scheduleSpeed" select="speed"/>
          <xsl:for-each select="items/item">
            <tr>
              <td><xsl:value-of select="$entityName"/></td>
              <td><xsl:value-of select="$entitySettingDate"/></td>
              <td><xsl:value-of select="$entityLabel"/></td>
              <td><xsl:value-of select="$entityDefaultSpeed"/></td>
              <td><xsl:value-of select="$scheduleSpeed"/></td>
              <td><xsl:value-of select="date"/></td>
              <td><xsl:value-of select="factor"/></td>
            </tr>
          </xsl:for-each>
        </xsl:for-each>
      </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>
I prefer this since it eliminates all the ../.. stuff which is prone to error and can become a code maintenance burden.

Now, since there seems to be some flexibility in the input document, you can see that the <schedules> level and <items> level are really unnecessary and can be eliminated with a corresponding correction in the xsl:for-each selects.

Tom Morrison
 
Hi Guys

I know that you helped me last week with my XSL but I found another snag in it and since I am relatively new to XSL, I find to tricky to solve. Here is my XSL as it stands.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="
<xsl:template match="/">
<html>
<body>
<table>
<tr>
<th>Name</th>
<th>Setting Date</th>
<th>Label</th>
<th>Default Speed</th>
<th>Speed</th>
<th>Date</th>
<th>Factor</th>
</tr>

<xsl:for-each select="focc_CCAmortScheduleGroups/focc_CCAmortScheduleGroup">
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="settingDate"/></td>
<td><xsl:value-of select="label"/></td>
<td><xsl:value-of select="defaultSpeed"/></td>
<td><xsl:value-of select="schedules/schedule/speed"/></td>
<td><xsl:value-of select="schedules/schedule/items/item[1]/date"/></td>
<td><xsl:value-of select="schedules/schedule/items/item[1]/factor"/></td>
</tr>

<xsl:for-each select="schedules/schedule/items/item[position()>1]">

<tr>
<td><xsl:value-of select="../../../../name"/></td>
<td><xsl:value-of select="../../../../settingDate"/></td>
<td><xsl:value-of select="../../../../label"/></td>
<td><xsl:value-of select="../../../../defaultSpeed"/></td>
<td><xsl:value-of select="../../speed"/></td>
<td><xsl:value-of select="date"/></td>
<td><xsl:value-of select="factor"/></td>
</tr>
</xsl:for-each>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>


As expected, whenever there is a new <item> tag, another line gets output with the existing <Name>, <Setting Date> <Label>, <Default Speed> and <Speed> but with the new <Date> and <Factor>. I thought the logic worked fine but it does not. There are 3 types of speed: Low, base and high. For each speed there are 344 records therefore I should expect to get 1032 records back. However, I only get back 1030 records and I know why. When the XSL comes across a new <Speed> it does not output the first line for that new speed which is why I am missing 2 records. It all boils down to a particular line:
<xsl:for-each select="schedules/schedule/items/item[position()>1]">

I am including some of the XML. Please can you have a look and give me your thoughts because I am really stuck.

Thanks

<?xml version="1.0" encoding="UTF-8" ?>
- <focc_CCAmortScheduleGroups>
- <focc_CCAmortScheduleGroup>
<name>C</name>
<settingDate>2006-06-23</settingDate>
<label>dwg</label>
<defaultSpeed>Base</defaultSpeed>
- <schedules>
- <schedule>
<speed>Low</speed>
- <items>
- <item>
<date>2006-06-01</date>
<factor>0.9988</factor>
</item>
- <item>
<date>2006-06-20</date>
<factor>0.9975</factor>
</item>
- <item>
<date>2006-07-20</date>
<factor>0.9963</factor>
</item>
- <item>
<date>2006-08-20</date>
<factor>0.995</factor>
</item>

......
- <item>
<date>2034-12-20</date>
<factor>0.1</factor>
</item>
</items>
</schedule>
- <schedule>
<speed>Base</speed>
- <items>
- <item>
<date>2006-06-01</date>
<factor>0.9974</factor>
</item>
- <item>
<date>2006-06-20</date>
<factor>0.9948</factor>
</item>
......
- <item>
<date>2034-12-20</date>
<factor>0.1</factor>
</item>
</items>
</schedule>
- <schedule>
<speed>High</speed>
- <items>
- <item>
<date>2006-06-01</date>
<factor>0.995</factor>
</item>
 
What if you do not single out positon() like what I posted? I would not expect the problem there.

Otherwise, you have to do with another xsl:for-each on schedules/schedule and then again the xsl:for-each on items/item, making three nested loop.
 
Hi

Yes, thanks! I must have over looked the fact that you took off 'item[position()>1' without realising its effects if I kept it in.

Also, I have taken away all of the '../..' stuff and I have incorperated for variables, eg <xsl:variable name="x".

I can see that one of the main benefits of this would be that if the structure of the XML file got modified, I would not be so concerned about whether I need to modify the supporting XSL.
Thanks again
 
There are postion()=1 and position()>1 for schedule and item. Simple math makes it 4 categories. If you insist, this is what it would look in your approach.
[tt]
<xsl:for-each select="root/entity">
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="settingDate"/></td>
<td><xsl:value-of select="label"/></td>
<td><xsl:value-of select="defaultSpeed"/></td>
<td><xsl:value-of select="schedules/schedule[1]/speed"/></td>
<td><xsl:value-of select="schedules/schedule[1]/items/item[1]/date"/></td>
<td><xsl:value-of select="schedules/schedule[1]/items/item[1]/factor"/></td>
</tr>

<xsl:for-each select="schedules/schedule[1]/items/item[position()>1]">
<tr>
<td><xsl:value-of select="../../../../name"/></td>
<td><xsl:value-of select="../../../../settingDate"/></td>
<td><xsl:value-of select="../../../../label"/></td>
<td><xsl:value-of select="../../../../defaultSpeed"/></td>
<td><xsl:value-of select="../../speed"/></td>
<td><xsl:value-of select="date"/></td>
<td><xsl:value-of select="factor"/></td>
</tr>
</xsl:for-each>

<xsl:for-each select="schedules/schedule[position()>1]/items/item[1]">
<tr>
<td><xsl:value-of select="../../../../name"/></td>
<td><xsl:value-of select="../../../../settingDate"/></td>
<td><xsl:value-of select="../../../../label"/></td>
<td><xsl:value-of select="../../../../defaultSpeed"/></td>
<td><xsl:value-of select="../../speed"/></td>
<td><xsl:value-of select="date"/></td>
<td><xsl:value-of select="factor"/></td>
</tr>
</xsl:for-each>

<xsl:for-each select="schedules/schedule[position()>1]/items/item[position()>1]">
<tr>
<td><xsl:value-of select="../../../../name"/></td>
<td><xsl:value-of select="../../../../settingDate"/></td>
<td><xsl:value-of select="../../../../label"/></td>
<td><xsl:value-of select="../../../../defaultSpeed"/></td>
<td><xsl:value-of select="../../speed"/></td>
<td><xsl:value-of select="date"/></td>
<td><xsl:value-of select="factor"/></td>
</tr>
</xsl:for-each>
</xsl:for-each>
[/tt]
Looks terrible. And I even wouldn't bother to proper ordering the display for position()>1 schedule. The question is why. Is that a given of an assignment?
 
Hi

I originally posted some questions a few months ago about an XSL that I was writing. It has come back to haunt me because it is not doing something that it was supposed to do. Please could you scroll up to maybe refresh your memory.

The <date> and <factor> elements can appear many times. Each time a new pair of <date> and <factor> elements appear, the XSL is supposed to print a new row that should contain all of the existing data from the previous row but just different values for the <date> and <factor> elements.

However, there can be 3 <schedule> tags so the above should be repeated for each new <schedule>. Unfortunately this is not happening. Currently, the XSL only works for the first <schedule> element and does not print anything for the other 2 <schedule>. Please could you see from my XSL where I am going wrong?

Here is how my XSL looks:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet version="1.0"?>
<xsl:stylesheet xmlns:xsl=" version="1.0">
<xsl:eek:utput method="text"/>
<xsl:template match="/">
<xsl:text>Name&#9;Setting Date&#9;Label&#9;Default Speed&#9;Speed&#9;Date&#9;Factor&#9;</xsl:text>
<xsl:text>&#10;</xsl:text>

<xsl:for-each select="focc_CCAmortScheduleGroups/focc_CCAmortScheduleGroup">
<xsl:variable name="entityName" select="name"/>
<xsl:variable name="entitySettingDate" select="settingDate"/>
<xsl:variable name="entityLabel" select="label"/>
<xsl:variable name="entityDefaultSpeed" select="defaultSpeed"/>
<xsl:variable name="entitySpeed" select="schedules/schedule/speed"/>

<xsl:for-each select="schedules/schedule/items/item">
<xsl:value-of select="$entityName"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="$entitySettingDate"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="$entityLabel"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="$entityDefaultSpeed"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="$entitySpeed"/>
<xsl:text>&#9;</xsl:text>

<xsl:value-of select="date"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="factor"/>
<xsl:text>&#9;&#10;</xsl:text>

</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>


Here is some sample XML:

<?xml version="1.0" encoding="UTF-8" ?>
<focc_CCAmortScheduleGroups>
<focc_CCAmortScheduleGroup>
<name>C</name>
<settingDate>2006-06-23</settingDate>
<label>dwg</label>
<defaultSpeed>Base</defaultSpeed>
<schedules>
<schedule>
<speed>Low</speed>
<items>
<item>
<date>2006-06-01</date>
<factor>0.9988</factor>
</item>
<item>
<date>2006-06-20</date>
<factor>0.9975</factor>
</item>
<item>
<date>2006-07-20</date>
<factor>0.9963</factor>
</item>

</schedule>
<schedule>
<speed>Base</speed>
<items>
<item>
<date>2006-06-01</date>
<factor>0.9974</factor>
</item>
<item>
<date>2006-06-20</date>
<factor>0.9948</factor>
</item>

</schedule>
<schedule>
<speed>High</speed>
<items>
<item>
<date>2006-06-01</date>
<factor>0.995</factor>
</item>

</items>
</schedule>
</schedules>
</focc_CCAmortScheduleGroup>
</focc_CCAmortScheduleGroups>


Here is how the result should look like when doing a view source:

Name Setting Date Label Default Speed Speed Date Factor
C 2006-06-23 dwg Base Low 2006-06-01 0.9988
C 2006-06-23 dwg Base Low 2006-06-20 0.9975
C 2006-06-23 dwg Base Low 2006-07-20 0.9963
C 2006-06-23 dwg Base Base 2006-06-01 0.9974
C 2006-06-23 dwg Base Base 2006-06-20 0.9948
C 2006-06-23 dwg Base High 2006-06-01 0.995
 
Have to keep variables evaluated in their proper context. Like this?
[tt]
<xsl:for-each select="focc_CCAmortScheduleGroups/focc_CCAmortScheduleGroup">
<xsl:variable name="entityName" select="name"/>
<xsl:variable name="entitySettingDate" select="settingDate"/>
<xsl:variable name="entityLabel" select="label"/>
<xsl:variable name="entityDefaultSpeed" select="defaultSpeed"/>
[blue]<xsl:for-each select="schedules/schedule">
<xsl:variable name="entitySpeed" select="speed"/>
<xsl:for-each select="items/item">[/blue]
<xsl:value-of select="$entityName"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="$entitySettingDate"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="$entityLabel"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="$entityDefaultSpeed"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="$entitySpeed"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="date"/>
<xsl:text>&#9;</xsl:text>
<xsl:value-of select="factor"/>
<xsl:text>&#9;&#10;</xsl:text>
[blue]</xsl:for-each>[/blue]
</xsl:for-each>
</xsl:for-each>
[/tt]
Effectively, it makes one more use of xsl:for-each.
 
Hi

Here is my XML doc.
Below that is what my style sheet looks like.
I want the output to disply
CL005812 0 ITX_XVER_S6_5_vol 20000000 EUR LnB Tar

where the calendar value is LnB Tar with a space between LnD and Tar
but the calendar value does not come out correct
any ideas why

<fosw_Securitys>
<fosw_CDSOptionSec>
<name>CL005812</name>
<optionLeg>
<isBuy>0</isBuy>
<volName>ITX_XVER_S6_5_vol</volName>
<asset>
<notional>20000000</notional>
<ccy>EUR</ccy>
<calendar>
<caltag>LnB</caltag>
<caltag>Tar</caltag>
</calendar
</asset>
</optionLeg>
</fosw_CDSOptionSec>
</fosw_Securitys>


<xsl:for-each select="fosw_Securitys/fosw_CDSOptionSec">
<xsl:value-of select="name"/>
<xsl:for-each select="optionLeg">
<xsl:text>&#9;</xsl:text> <xsl:value-of select="isBuy"/>
<xsl:text>&#9;</xsl:text> <xsl:value-of select="volName"/>
<xsl:for-each select="asset">
<xsl:text>&#9;</xsl:text> <xsl:value-of select="ccy"/>
<xsl:text>&#9;</xsl:text> <xsl:value-of select="notional"/>
<xsl:for-each select="calendar">
</xsl:text> <xsl:value-of select="caltag"/>
</xsl:for-each>
<xsl:text>&#10;</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
 
Perhaps this instead?

Code:
      <xsl:for-each select="[COLOR=white red]calendar/caltag[/color]">
        [COLOR=white red]<xsl:text> [/color]</xsl:text><xsl:value-of select="[COLOR=white red].[/color]"/>
      </xsl:for-each>

Tom Morrison
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top