Reputation: 11
I am stuck on a looping question and need some direction from the group. I am able to loop through one child, but having trouble adding the second.
This is my XML:
<wd:Report_Entry>
<wd:Beneficiaries_-_All>
<wd:EMPLID>00025</wd:EMPLID>
<wd:Last>LastName</wd:Last>
<wd:First>FirstName</wd:First>
<wd:WID>key1234</wd:WID>
</wd:Beneficiaries_-_All>
<wd:Beneficiaries_-_All>
<wd:EMPLID>00025</wd:EMPLID>
<wd:Last>LastName</wd:Last>
<wd:First>First2</wd:First>
<wd:WID>key4567</wd:WID>
</wd:Beneficiaries_-_All>
<wd:Beneficiaries_-_People>
<wd:DOB>Birth1</wd:DOB>
<wd:ID>ID1</wd:ID>
<wd:WID>key1234</wd:WID>
</wd:Beneficiaries_-_People>
<wd:Beneficiaries_-_People>
<wd:DOB>Birth2</wd:DOB>
<wd:ID>ID2</wd:ID>
<wd:WID>key4567</wd:WID>
</wd:Beneficiaries_-_People>
</wd:Report_Entry>
My XSLT is below
<xsl:output method="text"/>
<xsl:variable name="linefeed" select="'
'"></xsl:variable>
<xsl:template match="/">
<xsl:for-each select="/wd:Report_Data/wd:Report_Entry">
<xsl:for-each select="wd:Beneficiaries_-_All">
<xsl:text>"</xsl:text>
<xsl:value-of select="wd:EMPLID"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="wd:Last"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="wd:First"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="$linefeed"/>
</xsl:for-each>
<xsl:for-each select="wd:Beneficiaries_-_People">
<xsl:value-of select="wd:DOB"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="wd:ID"/>
<xsl:text>"</xsl:text>
</xsl:for-each>
<xsl:value-of select="$linefeed"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
My desired output is CSV like this:
"00025","LastName","First1","Birth1","ID1"
"00025","LastName","First2","Birth2","ID2"
"00026","LastName","First1","Birth1","ID1" (continues in this fashion)
But I am getting for each Report Entry:
"00025","LastName","First1","
"00025","LastName","First2","
Birth1","ID1" Birth2","ID2"
My research on this site indicates that <apply-templates>
is preferred over <for-each>
. The format gives me a problem when I try that method. Thanks group!
Upvotes: 0
Views: 945
Reputation: 338108
Here is a solution that shows a few XSLT features:
<xsl:for-each>
)(I've used a bogus namespace URI for the wd
prefix)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wd="http://some.namespace/wd"
>
<xsl:output method="text" />
<xsl:key name="kPeople" match="wd:Beneficiaries_-_People" use="wd:WID" />
<xsl:template match="/">
<xsl:apply-templates select="wd:Report_Data/wd:Report_Entry/wd:Beneficiaries_-_All" />
</xsl:template>
<xsl:template match="wd:Beneficiaries_-_All">
<xsl:apply-templates select="wd:EMPLID" mode="csv" />
<xsl:apply-templates select="wd:Last" mode="csv" />
<xsl:apply-templates select="wd:First" mode="csv" />
<xsl:apply-templates select="key('kPeople', wd:WID)/wd:DOB" mode="csv" />
<xsl:apply-templates select="key('kPeople', wd:WID)/wd:ID" mode="csv-nl" />
</xsl:template>
<xsl:template match="*" mode="csv">
<xsl:value-of select="concat('"', ., '",')" />
</xsl:template>
<xsl:template match="*" mode="csv-nl">
<xsl:value-of select="concat('"', ., '"
')" />
</xsl:template>
</xsl:stylesheet>
For your sample data this results in:
"00025","LastName","FirstName","Birth1","ID1"
"00025","LastName","First2","Birth2","ID2"
Gotchas:
<wd:DOB>
) do not exist. For uniform input where the elements are always there but sometimes empty, everything is fine. If elements can be missing, the script would need to be changed.In both cases: Know your data.
Template matching is a double-edged sword. It's great for input-driven transformations. The input XML is mapped through the templates in the stylesheet with minimal plumbing.
CSV on the other hand is a rigid output format, creating it in an input-driven fashion requires the input to be just as rigid. Since XML allows missing elements, there might not even be an element that can be turned into an output field. In this case, named templates and parameters can help:
Instead of
<xsl:apply-templates select="wd:EMPLID" mode="csv" />
we could have
<xsl:call-template name="csv">
<xsl:with-param name="val" value="wd:EMPLID" />
</xsl:call-template>
and
<xsl:template name="csv">
<xsl:param name="val" />
<xsl:value-of select="concat('"', $val, '",')" />
</xsl:call-template>
This would make sure that even when an element is missing, the empty field "",
would still be printed.
Upvotes: 1
Reputation: 1458
If I understand it correctly, you just need a variable to make it work:
<xsl:for-each select="/wd:Report_Data/wd:Report_Entry">
<xsl:for-each select="wd:Beneficiaries_-_All">
<xsl:text>"</xsl:text>
<xsl:value-of select="wd:EMPLID"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="wd:Last"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="wd:First"/>
<xsl:text>","</xsl:text>
<!-- new code starts here -->
<xsl:variable name="WID" select="wd:WID"/>
<xsl:value-of select="../wd:Beneficiaries_-_People[wd:WID = $WID]/wd:DOB"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="../wd:Beneficiaries_-_People[wd:WID = $WID]/wd:ID"/>
<xsl:text>"</xsl:text>
<!-- end new code -->
<xsl:value-of select="$linefeed"/>
</xsl:for-each>
</xsl:for-each>
Hope this helps!
Upvotes: 0