前面介绍了题库的实现。有了题库就可以出题考试了。下面介绍试卷部分的设计和实现。首先看下这部分的详细类图 4-13 。
图4-13 试卷模块类图
首先 Question 对象包含分值、得分、学生答案和一个表示是否批改过的 IsScored 属性。并有一个对 QuestionContent 的引用。 Questioin 对象代表了某学生一张试卷上的一道题。 QuestionContainer 类表示的是试卷上的一个大题。比如单选题。一个大题也就是一个 QuestionContainer 对象,它是就是小题 Question 对象的集合。以此类推一张试卷也就是一个 Paper 对象,就是大题 QuestionContainer 对象的集合。注意 QuesitonContainer 和 Paper 的 Score( 得分 ) 和 ScoreValue( 分值 ) 都是通过对他们所有子对象 Question 对象的得分和分值计算得来的。 Paper 类的 AutoScore 方法是用来自动改卷的。该方法检查客观题学生答案和标准答案。根据分值给每个 Question 子对象自动打分。另外注意每个试卷相关的类都有一个 GetxxxXML 的方法。该方法就是返回对象自身的 XML 格式字符串。如 QuestionContent 类的 GetContentXML 实现如下{
StringBuilder builder = new StringBuilder(500);
builder.Append("<" + QuestionType+ ">");
builder.Append("<Content><![CDATA[" + Content + "]]></Content>");
builder.Append("<Answer><![CDATA[" + Answer + "]]></Answer>");
builder.Append(GetChoicesXML());
builder.Append("</" + QuestionType + ">");
return builder.ToString();
}
下面是Question类的GetQuestionXML方法实现
public string GetQuestionXML()
{
StringBuilder builder = new StringBuilder(500);
builder.Append("<Question ID=\"" + ID + "\" ScoreValue=\"" + ScoreValue + "\" Score=\"" + Score + "\" IsScored=\""+IsScored+"\">");
builder.Append("<StudentAnswer><![CDATA[" + StudentAnswer + "]]></StudentAnswer>");
builder.Append(Content.GetContentXML());
//Content为QuestionContent对象
builder.Append("</Question>");
return builder.ToString();
}
注意注释部分说明了对象生成自己的XML格式时候,又是如何包含子对象XML内容。与此类似,QuestionContainer的XML生成方法包含了对Question对象的XML的添加。Paper则包含了QuestionContainer的XML内容的添加。最后看看调用Paper对象的GetPaperXML方法返回的整张试卷的XML格式内容。为了简化这里假设Paper对象只有一个大题QuestionContainer,单选题。该大题只有一个Question对象。Question对象引用的是一个JianDaContent对象。由此可以看出一个Paper对象是怎样生成自己的XML格式的。先来看看这个Paper对象和其相关子对象的对象图4-14。
图4-14 试卷对象图
下面是这些对象调用paper对象的GetPaperXML方法返回的XML。注意格式是经过修改过的。这里加上了换行。如果没加的话就会只有一行。
< QuestionContainer Title ="单选题" ScoreValue ="1" Score ="0" Count ="1" >
< Question ID ="61" ScoreValue ="1" Score ="0" IsScored ="True" >
< StudentAnswer > <![CDATA[ 3 ]]> </ StudentAnswer >
< JianDa >
< Content > <![CDATA[ 1+1=? ]]> </ Content >
< Answer > <![CDATA[ 2 ]]> </ Answer >
</ JianDa >
</ Question >
</ QuestionContainer >
</ Paper >
为什么要让Paper生成XML格式呢?就是为了下一个目标生成html。在线考试系统是基于web的,最终的试卷要在学生的浏览器中显示。而浏览器能够只能显示html。所以我们必须将Paper对象格式化成html才能显示给学生。当然也可以利用Asp.net的控件绑定来实现,或者直接用Response.Write来向浏览器输出html格式的Paper内容。但是这两种方法都十分的繁琐和难于调试。利用xslt这门专门用来转换文档格式的语言处理这种转换将更简洁灵活。因为XML是平台独立的。所以我们可以先将我们的Paper对象转换成XML格式。下一步就可以利用xlst将XML转换成想要的其他的格式。这里对xlst的基础知识不做过多的介绍。来看一个简单的例子来说明试卷是如何从XML到html的。还是利用上面已经有的一小段XML。下面给出用到的xslt的实现代码Paper.xsl。
< xsl:stylesheet version ="1.0"
xmlns:xsl ="http://www.w3.org/1999/XSL/Transform" >
< xsl:output method ="html" />
< xsl:template match ="Paper" >
< html >
< body >
< xsl:apply-templates select ="QuestionContainer" />
</ body >
</ html >
</ xsl:template >
< xsl:template match ="QuestionContainer" >
< div class ="Title" >
< xsl:value-of select ="@Title" />
</ div >
< xsl:number format ="1." />
< xsl:apply-templates select ="Question" />
</ xsl:template >
< xsl:template match ="Question" >
< div class ="Question" >
< xsl:apply-templates select ="JianDa" />
</ div >
</ xsl:template >
< xsl:template match ="JianDa" >
< div class ="JianDa" >
< div class ="Content" >
< pre >
< xsl:value-of select ="Content" />
</ pre >
</ div >
< div class ="Input" >
< textarea name ="{@ID}" rows ="8" style ="width:100%" >
< xsl:value-of select ="Answer" />
</ textarea >
</ div >
</ div >
</ xsl:template >
</ xsl:stylesheet >
public static string TranslateXMLToHtmlString( string XML, string xslFilePath)
{
XMLReader reader = XMLReader.Create(new StringReader(XML));
XPathDocument xpathDoc = new XPathDocument(reader);
XslCompiledTransform xslt = new XslCompiledTransform();
XsltSettings setting = new XsltSettings();
setting.EnableScript = true;
xslt.Load(HttpContext.Current.Server.MapPath(xslFilePath), setting, new XMLUrlResolver());
StringWriter writer = new StringWriter();
xslt.Transform(xpathDoc, null, writer);
return writer.ToString();
}
该方法有两个参数一个是XML片段字符串。另一个是xsl文件的站点路径。将上面出现的Paper对象的XML片段和Paper.xsl文件的路径作为参数就可以获得生成的html字符串。
下面是生成的html代码
< body >
< div class ="Title" > 单选题 </ div > 1. < div class ="Question" >
< div class ="JianDa" >
< div class ="Content" >
< pre > 1+1=? </ pre >
</ div >
< div class ="Input" >< textarea name ="" rows ="8" style ="width:100%" > 2 </ textarea ></ div >
</ div >
</ div >
</ body >
</ html >