Reputation: 219
I have an XML document listing 4 possible answers and then a list of the correct answers for a multiple choice/multiple answer question. When performing a transform, I need to list the correct answers as well as pull the one answer that is not correct into it's own element. How do I compare the "lists" to parse out the one incorrect answer to output?
SOURCE XML:
<assessment>
<div class="question" id="s1">
<div class="prompt" id="s2">
<p id="s3">SELECT ALL THAT APPLY. Which of the following colors do I like?</p>
</div>
<div class="answer" id="s4">
<div class="choice" id="s5">
<div class="list" id="s6">
<ul class="no-marker">
<li id="s7">Red</li>
<li id="s8">Blue</li>
<li id="s9">Green</li>
<li id="s10">Orange</li>
</ul>
</div>
</div>
</div>
<div class="response-processing" id="s11">
<div class="condition" id="s12">
<div class="correct" id="s13">
<div class="response" id="s14">
<p><a class="answer-ref" id="s15" href="#s5">s7</a></p>
<p><a class="answer-ref" id="s16" href="#s5">s9</a></p>
<p><a class="answer-ref" id="s17" href="#s5">s10</a></p>
</div>
</div>
</div>
</div>
</div>
</assessment>
Working XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:key name="answers" match="div[@class='response-processing']/div[@class='condition']/div[@class='correct']/div[@class='response']/p/a[@class='answer-ref']" use="text()"/>
<xsl:template match="*">
<xsl:call-template name="createItem"/>
</xsl:template>
<xsl:template name="createItem">
<xsl:for-each select="div[@class='question']">
<xsl:element name="item">
<xsl:attribute name="ident" select="@id"/>
<xsl:call-template name="createPresentation"/>
<xsl:call-template name="createReprocessing"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template name="createPresentation">
<xsl:element name="presentation">
<xsl:element name="flow">
<xsl:attribute name="class" select="'Block'"/>
<xsl:element name="flow">
<xsl:attribute name="class" select="'QUESTION_BLOCK'"/>
<xsl:element name="flow">
<xsl:attribute name="class" select="'FORMATTED_TEXT_BLOCK'"/>
<xsl:element name="material">
<xsl:element name="mat_extension">
<xsl:element name="mat_formattedtext">
<xsl:attribute name="type" select="'SMART_TEXT'"/>
<xsl:value-of select="div[@class='prompt']/p"/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
<xsl:element name="flow">
<xsl:attribute name="class" select="'RESPONSE_BLOCK'"/>
<xsl:element name="response_lid">
<xsl:attribute name="ident" select="div[@class='answer']/div[@class='choice']/@id"/>
<xsl:element name="render_choice">
<xsl:apply-templates select="div[@class='answer']/div[@class='choice']"/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template name="createReprocessing">
<xsl:element name="respcondition">
<xsl:attribute name="title" select="'correct'"/>
<xsl:element name="conditionvar">
<xsl:choose>
<xsl:when test="count(div[@class='response-processing']/div[@class='condition']/div[@class='correct']/div[@class='response']/p/a[@class='answer-ref']) <= 1">
<!--Use for-each to output if there are multiple answers-->
<xsl:for-each select="div[@class='response-processing']/div[@class='condition']/div[@class='correct']/div[@class='response']/p/a[@class='answer-ref']">
<xsl:element name="varequal">
<xsl:attribute name="respident" select="@href"/>
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:when>
<xsl:when test="count(div[@class='response-processing']/div[@class='condition']/div[@class='correct']/div[@class='response']/p/a[@class='answer-ref']) > 1">
<xsl:element name="and">
<xsl:if test="*[not(key('answers', ./div[@class='answer']/div[@class='choice']/div[@class='list']/ul/li/@id))]">
<xsl:for-each select=".">
<xsl:element name="not">
<xsl:element name="varequal">
<xsl:attribute name="respident" select="./@href"/>
<xsl:value-of select="key('answers',.)"/>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:if>
<!--Use for-each to output if there are multiple answers-->
<xsl:for-each select="div[@class='response-processing']/div[@class='condition']/div[@class='correct']/div[@class='response']/p/a[@class='answer-ref']">
<xsl:element name="varequal">
<xsl:attribute name="respident" select="@href"/>
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="div[@class='choice']">
<xsl:for-each select="div[@class='list']//li">
<xsl:element name="flow_label">
<xsl:attribute name="class" select="'Block'"/>
<xsl:element name="response_label">
<xsl:attribute name="ident" select="@id"/>
<xsl:if test="@class='no-shuffle'">
<xsl:attribute name="rshuffle" select="'No'"/>
</xsl:if>
<xsl:element name="flow_mat">
<xsl:attribute name="class" select="'FORMATTED_TEXT_BLOCK'"/>
<xsl:element name="material">
<xsl:element name="mat_extension">
<xsl:element name="mat_formattedtext">
<xsl:attribute name="type" select="'SMART_TEXT'"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XML OUTPUT (note comment for desired output within the element):
<item ident="s1">
<presentation>
<flow class="Block">
<flow class="QUESTION_BLOCK">
<flow class="FORMATTED_TEXT_BLOCK">
<material>
<mat_extension>
<mat_formattedtext type="SMART_TEXT">SELECT ALL THAT APPLY. Which of the
following colors do I like?</mat_formattedtext>
</mat_extension>
</material>
</flow>
</flow>
<flow class="RESPONSE_BLOCK">
<response_lid ident="s5">
<render_choice>
<flow_label class="Block">
<response_label ident="s7">
<flow_mat class="FORMATTED_TEXT_BLOCK">
<material>
<mat_extension>
<mat_formattedtext type="SMART_TEXT"
>Red</mat_formattedtext>
</mat_extension>
</material>
</flow_mat>
</response_label>
</flow_label>
<flow_label class="Block">
<response_label ident="s8">
<flow_mat class="FORMATTED_TEXT_BLOCK">
<material>
<mat_extension>
<mat_formattedtext type="SMART_TEXT"
>Blue</mat_formattedtext>
</mat_extension>
</material>
</flow_mat>
</response_label>
</flow_label>
<flow_label class="Block">
<response_label ident="s9">
<flow_mat class="FORMATTED_TEXT_BLOCK">
<material>
<mat_extension>
<mat_formattedtext type="SMART_TEXT"
>Green</mat_formattedtext>
</mat_extension>
</material>
</flow_mat>
</response_label>
</flow_label>
<flow_label class="Block">
<response_label ident="s10">
<flow_mat class="FORMATTED_TEXT_BLOCK">
<material>
<mat_extension>
<mat_formattedtext type="SMART_TEXT"
>Orange</mat_formattedtext>
</mat_extension>
</material>
</flow_mat>
</response_label>
</flow_label>
</render_choice>
</response_lid>
</flow>
</flow>
</presentation>
<respcondition title="correct">
<conditionvar>
<and>
<not>
<!-- Instead of an empty varequal within <not> I want <varequal respident="#s5">s8</varequal> -->
<varequal respident=""/>
</not>
<varequal respident="#s5">s7</varequal>
<varequal respident="#s5">s9</varequal>
<varequal respident="#s5">s10</varequal>
</and>
</conditionvar>
</respcondition>
</item>
I think key() is the way to go, but I'm unsure how to get it working. Thanks in advance.
Upvotes: 1
Views: 201
Reputation: 167471
Your key (I would simplify it to
<xsl:key name="answers"
match="div[@class='response-processing']/div[@class='condition']/div[@class='correct']/div[@class='response']/p/a[@class='answer-ref']"
use="."/>
)
allows you to check whether an answer li
has corresponding response so you check whether the key
call key('answers', @id)
gives an empty node set or sequence and then you can treat your answer as that:
<xsl:template match="div[@class = 'answer']//li[not(key('answers', @id))]">
<incorrect id="{@id}"></incorrect>
</xsl:template>
Upvotes: 1