2

I have to following XML document structure:

<option_set id="1">
  <option>Yes</option>
  <option>No</option>
  <option>Maybe</option>
</option_set>

<question option_set="1">
  <text>Do you like cake?</text>
</question>
<question option_set="1">
  <text>Is the cake a lie?</text>
</question>

In the interests of keeping things DRY, the idea is to have a number of different questions which share common sets of options. These can then be built using XSLT. My templates are as follows:

<xsl:template match="question[@option_set and not(option)]">
  <!-- Build a whole question with its options
       (copy the options across and then apply-templates?) -->
</xsl:template>

<xsl:template match="question[option]">
  <!-- Match a whole question, with options, for making pretty HTML out of -->
</xsl:template>

The idea is that once the top template has matched my question, I will be left with something that looks like the following:

<question>
  <text>Do you like cake?</text>
  <option>Yes</option>
  <option>No</option>
  <option>Maybe</option>
</question>

... Which can then be matched by the bottom template and put into my HTML document. My question is how do I create the (top) template that actually does that. I'm close, but this still isn't working:

<xsl:template match="question[@option_set and not(option)]">
  <xsl:variable name="optset" select="@option_set"/>

  <xsl:copy>
    <xsl:copy-of select="text"/>
    <xsl:copy-of select="//option_set[@id=$optset]/option"/>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>

The transformed question block, along with its options is being copied over to the document, instead of being picked up by the top template and made into pretty HTML.

If I try to <xsl:apply-templates select="."/> then I get caught in an infinite loop.

Dave
  • 4,356
  • 4
  • 37
  • 40
  • 1
    There is no `question` that contains an `option` in your example, so what is your `` meant to be for? – Tomalak May 12 '11 at 20:56
  • It seems like the goal of the question is for the `` to generate a `` node of the same form that `` takes so that THAT template can produce some final output that is in some other format. – Justin W May 12 '11 at 21:05
  • 1
    @Justin ...which is not possible since templates produce RTFs and not nodes that can be subjected to further matching. – Tomalak May 12 '11 at 21:07
  • Yes, as Justin W says, the purpose of the first template is copy the options onto the question, which is then matched by ``. Please explain what these RTFs are? I think that might explain the problem I'm having :) – Dave May 12 '11 at 21:10
  • @Dave RTFs are Result Tree Fragments. BTW what version of XSLT are you working with? – Tomalak May 12 '11 at 21:22
  • @Tomalak: Using libxml 20706, libxslt 10126 and libexslt 815. So, are you saying that its impossible to build up a bunch of tags in XSLT and then `apply-templates` on those tags? – Dave May 12 '11 at 21:27
  • @Dave I was more interested in the version of XSLT itself, not your tooling. Your XSLT stylesheet element should carry a version attribute. ;-) - Yes, that's what I'm saying, and it's also the reason why I ask for your XSLT version, since exactly at this point there's a mayor difference between 1.0 and 2.0. (Maybe you should also explain why you are not producing HTML straight-away but rather try this two-step approach.) – Tomalak May 12 '11 at 21:34
  • Ah, okay, that would make it XSLT 1.0 then I'm afraid, since it is libxslt on Ubuntu linux. As for the two step approach, this is because some of the questions do have `` on them, others just have the `option_set="..."` tag, so I wanted to build the questions first and then `apply-templates` to all of them. But I'm starting to understand this much better thanks to your help so far. – Dave May 12 '11 at 21:43
  • Okay, maybe it's best to post an example of XML containing any combination of elements you expect and the desired output for it. I'm pretty sure there is a short and elegant solution to your problem, it does not seem too hard to do. Trying a two-step approach is shooting yourself in the foot a bit. – Tomalak May 12 '11 at 21:47
  • As an aside, it IS possible to use RTFs to feed your `apply-template`s **if** your xslt 1.0 processor supports [exsl:node-set()](http://exslt.org/exsl/functions/node-set/index.html), though it's really not the best way to handle this particular situation. You should just make one template that handles both situations. – Justin W May 12 '11 at 21:49
  • @Tomalak: I've gone with the modified answer of Justin's below. But thanks so much for the help. I've uprated yours too because of that cool `` trick. Thanks. I learn a new thing every day :) – Dave May 12 '11 at 21:52

2 Answers2

2
<xsl:key name="kOptionSet" match="option_set" use="@id" />

<xsl:template match="question">
  <xsl:copy>
    <xsl:copy-of select="text" />
    <xsl:copy-of select="key('kOptionSet', @option_set)/option" />
    <xsl:apply-templates />
  </xsl:copy>
</xsl:template>

Should pretty much do what you want. I'm not sure why you are recursing in the first place.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
2

Not sure what you're ultimately trying to do, but this might help you.

<xsl:template match="question">
  <xsl:value-of select="text"/>: 
  <select>
     <xsl:variable name="option_set_id" select="@option_set"/>
     <xsl:apply-templates select="option | //option_set[@id=$option_set_id]/option"/>
  </select>
</xsl:template>

<xsl:template match="option">
   <option>
      <xsl:value-of select="."/>
   </option>
</xsl:template>

There are tweaks, like adding the key above, and checking for unused option_sets etc. but this'll get you started.

Justin W
  • 1,315
  • 9
  • 14
V a a m Y o b
  • 492
  • 3
  • 7
  • Thanks V a am Y o b, but unfortunately I don't think this helps solve my original problem. The goal was to go from `` *(without options)* to `` to `` *(with options)* to `` to *(bunch of HTML)* – Dave May 12 '11 at 21:30
  • I updated the answer to function for questions with their own options too. Now you don't need the "recursive" step before your output. – Justin W May 12 '11 at 21:36
  • @Justin, okay wait, that edit looks interesting. I think I might be able to use that... let me try. – Dave May 12 '11 at 21:47
  • Yep, that does the trick. `apply-templates` with the options on the question, or the options belonging to the relevant ``. Thanks! – Dave May 12 '11 at 21:51
  • ok, only after Justin's edit did I really understand your situation. Glad you found something that works. – V a a m Y o b May 12 '11 at 22:21