views:

91

answers:

2

I have some XML like this:

<topics>
  <topic id="50"/>
  <topic id="51"/>
  <topic id="52"/>
</topics>

<discussions>
  <discussion type="foo">talked about T50 as Discussion 1000</discussion>
  <discussion type="bar">Food is yummy!</discussion>
  <discussion type="foo">talked about T52 as Discussion 1050</discussion>
</discussions>

Given a particular topic ID ($topicID), I would like to do the following:

  • Devise an XPath expression which is true if there is a <discussion> with type="foo" that contains the text T$topicID as Discussion <corresponding-discussion-id>.

  • Devise an XPath expression which, given that $topicID, will extract the text Discussion <corresponding-discussion-id>.

Is this possible?


Update:

For the first one, I'm thinking I need something like:

exists(
 //discussions/discussion[
  @type="foo" and
  contains(text(), concat($topicId, ??? ))  <-- What goes here? I think we need
 ]                                           some kind of matches(...) around the
)                                            concat(), too.
+1  A: 

Does a discussion of type=foo contains corresponding topicID:

/discussions/discussion[@type=foo AND contains(.,'T52')]
Naeem Sarfraz
+5  A: 

The following XPath will select the discussions attribute type="foo" and containing the text "T50 as Discussion" (when $topicid=50).

//discussions/discusson[@type='foo' and contains(., concat('T', $topicid, ' as Discussion ')]

Given a particular discussion element, the corresponding id is given by:

substring-after(normalize-space(.),' as Discussion ')

We can combine the 2 by replacing "." in the second expression with the entire first expression. Note that if multiple discussions match the inner expression, we will concat their values from the second.

substring-after(normalize-space(
    //discussions/discusson[@type='foo' and contains(., concat('T', $topicid, ' as Discussion ')]
),' as Discussion ')

If there were multiple matching discussions, you might process them like this:

<xsl:for-each select="//topics/topic">
    <xsl:variable name="topicid" select="@id" />
    <xsl:for-each select="//discussions/discusson[@type='foo' and contains(., concat('T', $topicid, ' as Discussion ')]">
        <xsl:variable name="relatedid" select="substring-after(normalize-space(.),' as Discussion ')" />

        <!-- do something with $topicid and $relatedid -->

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

Function references:

Personally, I cannot imagine doing serious XSLT development without Michael Kay's book nearby.

Lachlan Roche
Would retrieving the second one be something like `//discussions/discussion[...]/substring-after(...)`?
Kevin Stargel
This worked splendidly! Thanks very much for a thorough and complete answer, Lachlan. I wish I had more upvotes to give.
Kevin Stargel
This is eerily similar to some work I just did today! Are you guys spying on me? ;)
John Feminella