Nested for-each with external xml file (document('other.xml')) Issue

S

sloan

I am having an issue with the xsl document command. "document" being the
way to refer to a different xml file as seen here:
http://www.w3schools.com/Xsl/func_document.asp


I have some absolute xpaths in my xml, and I loop over them.
I've created a simple sample below.
Basically, I can loop over (for-each) using an absolute path on the primary
xml file.
I can loop over the secondary xml file (another for-each statement).
However, if I nest a for-each (referring to the primary xml data) inside a
for-each statement (looping over data in the secondary xml), the for-each
statement referring to the primary xml data comes back empty.

I kinda understand that I'm in two different "contexts" for lack of a better
word (feel free to correct my syntax), but I have no idea how to solve it.
Below is a simple sample, with what I'm getting, and the desired results I
would like to get.
I'm probably missing some basic xsl premise, but truth be told, this is my
first experience (this week) with the "document" and applying the xsl to a
secondary xml source. This was the article that started this little
journey.
http://msdn.microsoft.com/en-us/library/ms256465.aspx

Regardless, maybe someone can explain what I'm missing in the
document('someOtherFile.xml') usage.




<!-- PrimaryData.xml contents below -->

<?xml version="1.0" encoding="utf-8"?>
<PeopleRoot>
<Persons>
<Person SSN="222-22-2222">
<LastName>Wayne</LastName>
<FirstName>John</FirstName>
</Person>
<Person SSN="333-33-3333">
<LastName>Gable</LastName>
<FirstName>Clark</FirstName>
</Person>
</Persons>
</PeopleRoot>



<!-- SecondaryData.xml contents below -->

<?xml version="1.0" encoding="utf-8"?>
<GeographyRoot>
<Places>
<Place>
<City>Chicago</City>
<State>IL</State>
</Place>
<Place>
<City>Houston</City>
<State>TX</State>
</Place>
</Places>
</GeographyRoot>



<!-- MyXsl.xsl contents below -->

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:blush:utput method="xml" />
<xsl:param name="ExternalXmlSourceParameter" select="'SecondaryData.xml'"
/>
<xsl:template match="/">
<OutputRoot>
<ShowMeBefore>
<xsl:for-each select="//PeopleRoot/Persons/Person">
<PersonGenerateID>
<xsl:value-of select="generate-id()" />
</PersonGenerateID>
<PersonValue>
<xsl:value-of select="." />
</PersonValue>
</xsl:for-each>
</ShowMeBefore>
<xsl:comment>Comment1===============================</xsl:comment>
<ShowMeExternal>
<xsl:for-each
select="document($ExternalXmlSourceParameter)//GeographyRoot/Places/Place">
<xsl:variable name="SomeUniqueIdentificationVariable">
<xsl:value-of select="generate-id()" />
</xsl:variable>
<PlaceSomeUniqueIdentificationVariable>
<xsl:value-of select="$SomeUniqueIdentificationVariable" />
</PlaceSomeUniqueIdentificationVariable>
<PlaceValue>
<xsl:value-of select="." />
</PlaceValue>
<xsl:comment>Comment2===============================</xsl:comment>
<ShowMeInsideTheExternalForEach>
<xsl:for-each select="//PeopleRoot/Persons/Person">
<PersonGenerateID>
<xsl:value-of select="generate-id()" />
</PersonGenerateID>
<PersonValue>
<xsl:value-of select="." />
</PersonValue>
</xsl:for-each>
</ShowMeInsideTheExternalForEach>
<xsl:comment>Comment3===============================</xsl:comment>
</xsl:for-each>
</ShowMeExternal>
<xsl:comment>Comment4===============================</xsl:comment>
<ShowMeAfter>
<xsl:for-each select="//PeopleRoot/Persons/Person">
<PersonGenerateID>
<xsl:value-of select="generate-id()" />
</PersonGenerateID>
<PersonValue>
<xsl:value-of select="." />
</PersonValue>
</xsl:for-each>
</ShowMeAfter>
</OutputRoot>
</xsl:template>
</xsl:stylesheet>


<!-- END 3 FILES and their contents -->






<!-- The results I am getting now (below xml snipplet) (just the part that
is not desired)-->

<!--Comment2===============================-->
<ShowMeInsideTheExternalForEach />
<!--Comment3===============================-->



<!-- Desired Results -->

<!--Comment2===============================-->
<ShowMeInsideTheExternalForEach>
<PersonGenerateID>abcd</PersonGenerateID>
<PersonValue>
Wayne
John
</PersonValue>
<PersonGenerateID>bcde</PersonGenerateID>
<PersonValue>
Gable
Clark
</PersonValue>
</ShowMeInsideTheExternalForEach>
<!--Comment3===============================-->




<!-- I'm using Saxon right now, FYI, in case your xsl parser gives different
results. Below is the command line call -->
"C:\Program
Files\MSBuild\SaxonHE\bin\Transform.exe" -s:primaryData.xml -xsl:MyXsl.xsl -o:OutputStuff.xml

<!--
But in production, it will be MSBuild Community Tasks Project
(http://msbuildtasks.tigris.org/) Xslt call, which uses the standard DotNet
transform classes under the covers.
-->
 
M

Martin Honnen

sloan said:
I have some absolute xpaths in my xml, and I loop over them.
I've created a simple sample below.
Basically, I can loop over (for-each) using an absolute path on the primary
xml file.
I can loop over the secondary xml file (another for-each statement).
However, if I nest a for-each (referring to the primary xml data) inside a
for-each statement (looping over data in the secondary xml), the for-each
statement referring to the primary xml data comes back empty.

I kinda understand that I'm in two different "contexts" for lack of a better
word (feel free to correct my syntax), but I have no idea how to solve it.
Below is a simple sample, with what I'm getting, and the desired results I
would like to get.

If you are working with more than one document and you change the
context node (with for-each or apply-templates) to be a node from one
document but then want to access a node from a different document you
need to help yourself with a variable that for instance stores the root
node of the other document.

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:blush:utput method="xml" />
<xsl:param name="ExternalXmlSourceParameter" select="'SecondaryData.xml'"
/>
Put

<xsl:for-each
select="document($ExternalXmlSourceParameter)//GeographyRoot/Places/Place">
<xsl:variable name="SomeUniqueIdentificationVariable">
<xsl:value-of select="generate-id()" />
</xsl:variable>

Not related to your problem but instead of doing
<xsl:variable name="foo">
<xsl:value-of select="someExpression"/>
</xsl:variable>
you could use
<PlaceSomeUniqueIdentificationVariable>
<xsl:value-of select="$SomeUniqueIdentificationVariable" />
</PlaceSomeUniqueIdentificationVariable>
<PlaceValue>
<xsl:value-of select="." />
</PlaceValue>
<xsl:comment>Comment2===============================</xsl:comment>
<ShowMeInsideTheExternalForEach>

here you need e.g.
<xsl:for-each select="//PeopleRoot/Persons/Person">
<xsl:for-each select="$main-root/PeopleRoot/Persons/Person">
 
S

sloan

Thanks Martin (once again) (<<how many times have I said that?).

//
<xsl:variable name="main-root" select="/"/>
//

I'll get my little project done today now........just in the nick of
(msproject) time!
 
P

Peter Flynn

sloan said:
Basically, I can loop over (for-each) using an absolute path on the primary
xml file.

In addition to Martin's comments, it's usually better practice to use
apply-templates for anything you process in document order, and keep
for-each for material you process out of document order:

<xsl:template match="/">
<OutputRoot>
<ShowMeBefore>
<xsl:apply-templates select="//PeopleRoot/Persons/Person"/>
</ShowMeBefore>
....
</xsl:template>

<xsl:template match=Person>
<PersonGenerateID>
<xsl:value-of select="generate-id()" />
</PersonGenerateID>
<PersonValue>
<xsl:value-of select="." />
</PersonValue>
</xsl:template>

By doing this, you can reuse the same template for all the other
references to the Person element type. This makes the whole script much
shorter and easier to maintain.

///Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top