Hi Eric,
Whew, this is a tangle! Getting the descendants of a given element is
easy in XPath, and your nested <xsl:for-each> instructions should do the
right thing. You could even combine the two instructions with this XPath:
.//presentation//person
The difficulty you noted is that there's no hierarchy to explicitly
connect any given person to their affiliation. For each person, you need
to get the following elements named 'affiliation' that _do not_ occur
after a following <person>. This is not an easy XPath to write!
When I've run into this problem in the past, I've used a named XSLT
template which takes the next X number of siblings and tests if they
meet an inclusion criteria (here, "am I an <affiliation>?" or
`exists(self::affiliation)` ). Everything after the first non-match is
thrown out.
However, this is not a method I'd recommend to anyone, because it turns
out there's a somewhat easier method using <xsl:for-each-group>:
<xsl:for-each-group select="presentation/*"
group-starting-with="person">
<xsl:variable name="personAdjGrp" select="current-group()"/>
<xsl:variable name="currentPerson"
select="$personAdjGrp[self::person]"/>
<xsl:variable name="allAffiliations"
select="$personAdjGrp[self::affiliation]"/>
<xsl:variable name="paperTitle"
select="normalize-space(../paper)"/>
<xsl:message>
<xsl:text>Working on </xsl:text>
<xsl:value-of select="normalize-space($currentPerson)"/>
<xsl:text> of </xsl:text>
<xsl:value-of select="$paperTitle"/>
</xsl:message>
<xsl:value-of
select="normalize-space($currentPerson)"/><xsl:text>	</xsl:text>
<xsl:value-of
select="string-join($allAffiliations/normalize-space(),
'|')"/><xsl:text>	</xsl:text>
<xsl:value-of select="$paperTitle"/><xsl:text> </xsl:text>
</xsl:for-each-group>
I found out about this method in Michael Kay's excellent _XSLT 2.0 and
XPath 2.0_. It forms a group of all sibling elements that occur after
<person>, but before the next <person>. You can then use the
`current-group()` function to get the sequence of elements that for sure
are associated with this particular person, and tests on the self axis
to narrow _those_ down to only the elements of a certain type. I added
some special sauce with the `string-join()` function to put a '|'
between each affiliation for a given person.
Hope this helps!
Best,
Ashley
On 11/16/17 12:42 PM, Eric Lease Morgan wrote:
> How can I use XSLT to find the direct descendants of a given element with a given name?
>
> I want to create a tab-delimited version of an XML file. I have the following XML snippet, and notice how each of the persons in the first presentation have a single affiliation, but the person of the second presentation has two affiliations:
>
> <session>
> <presentation>
> <person>RICHARD G. ANDERSON</person>
> <affiliation>Lindenwood University</affiliation>
> <person>AREERAT KICHKHA</person>
> <affiliation>Lindenwood University</affiliation>
> <paper>Is Less More?</paper>
> </presentation>
> <presentation>
> <person>BRIAN W. SLOBODA</person>
> <affiliation>University of Maryland</affiliation>
> <affiliation>University College</affiliation>
> <paper>Inflation Policies</paper>
> </presentation>
> </session>
>
> I want my resulting tab-delimited file to look like this:
>
> RICHARD G. ANDERSON Lindenwood University Is Less More?
> AREERAT KICHKHA Lindenwood University Is Less More?
> BRIAN W. SLOBODA University of Maryland|University College Inflation Policies
>
> I have the following XSLT snippet, but my process of getting affiliations is not nearly correct:
>
> <xsl:for-each select=".//presentation">
> <xsl:for-each select=".//person">
> <xsl:value-of select="normalize-space(.)"/><xsl:text>	</xsl:text>
> <xsl:value-of select="normalize-space(../affiliation)"/><xsl:text>	</xsl:text>
> <xsl:value-of select="normalize-space(../paper)"/><xsl:text> </xsl:text>
> </xsl:for-each>
> </xsl:for-each>
>
> Can you offer any suggestions? What sort of XPath expression should I be using to find all of the affiliation elements between person elements? Something with following-sibling?
--
Ashley M. Clark
XML Applications Developer
Digital Scholarship Group
Northeastern University Libraries
[log in to unmask] <mailto:[log in to unmask]>
(617) 373-5983
|