Sample ASP.NET and XSLT code to server both XHTML web pages and XCRI CAP 1.2 XML from a single course discovery page

We can expand the functionality of the ASP.NET and XSLT course discovery page from Sample XSLT and ASP.NET code to create web pages from your course feed to serve subsets of your XCRI CAP 1.2 feed as well as HTML pages. This will mean adding a new XSLT stylesheet to produce a subset of the feed using the same parameters as before (filtered by qualification, study mode and subject) and optionally restricting the amount of course information passed through using a new profile parameter. The Visual Basic.NET code is updated to support the two paths.


  1. Directory structure
  2. The ASP.NET page, save as default2.aspx
  3. The ASP.NET Visual Basic code behind file, save as default2.aspx.vb
  4. The XCRI CAP subsetting XSLT stylesheet, save as xcricap1p2subset.xslt
  5. Sample XCRI CAP 1.2 output
  6. Notes

Directory structure

The sample website has this structure. The enhanced course discovery page lives in the /courses directory, and the course details page lives in the /courses/course subdirectory. The XSLT stylesheets are in /xml/xsl/t/courses.

  • courses
    • default2.aspx
    • default2.aspx.vb
  • xml
    • xsl
      • t
        • courses
          • xcricap1p2toxhtmlcoursediscovery.xslt
          • xcricap1p2subset.xslt

The ASP.NET page, save as default2.aspx

This page is virtually the same as before, although note that the code behind file is now default2.aspx.vb, it inherits "courses_default2, and there is an ASP.NET Label control to write useful feedback to for testing.

<%@ Page Title="Kelpie College Courses" Language="VB" MasterPageFile="~/kelpiecollege.master" AutoEventWireup="false" CodeFile="default2.aspx.vb" Inherits="courses_default2" debug="true" %> <asp:Content ID="Content1" ContentPlaceHolderID="BreadcrumbPlaceHolder" runat="Server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="NavigationPlaceHolder" runat="Server"> <asp:Label ID="lblRequest" runat="server" /> </asp:Content> <asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder" runat="Server"> <asp:Xml ID="xmlCourseList" runat="server" /> </asp:Content>

The ASP.NET Visual Basic code behind file, save as default2.aspx.vb

The code now has conditional branching for two sets of HTTP ACCEPT values, one requesting a HTML web page, another requesting an XCRI CAP document.

Imports System.IO Imports System.Xml Imports System.Xml.XPath Imports System.Xml.Xsl Partial Class courses_default2 Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Const chrSemiColon As Char = ";"c Const chrComma As Char = ","c Dim strRequestAccepts As String = Request.ServerVariables("HTTP_ACCEPT") If Not strRequestAccepts.IndexOf(chrSemiColon) = -1 Then strRequestAccepts = strRequestAccepts.Substring(0, strRequestAccepts.IndexOf(chrSemiColon)) End If Dim chrDelimiters() As Char = {chrSemiColon, chrComma} lblRequest.Text = "<h2>Request accepts</h2>" Dim strRequestAcceptArray As String() = strRequestAccepts.Split(chrDelimiters) Dim strRequestAccept For Each strRequestAccept In strRequestAcceptArray lblRequest.Text &= "<br />" & strRequestAccept Next strRequestAccept ' Get parameters from query string. Dim strQualification As String = Request.QueryString("qualification") Dim strStudyMode As String = Request.QueryString("studymode") Dim strSubject As String = Request.QueryString("subject") Dim strPage As String = Request.QueryString("page") Dim strPageSize As String = Request.QueryString("pagesize") ' Compile the style sheet. Dim xctTransform1 As New XslCompiledTransform() ' Choose the transform stylesheet Dim strStylesheetUri1 As String If strRequestAcceptArray.Contains("text/html") Or strRequestAcceptArray.Contains("application/xhtml+xml") Then strStylesheetUri1 = "" Else If strRequestAcceptArray.Contains("application/xcri-cap+xml") strStylesheetUri1 = "" End If ' Choose the source XML document. Dim strDocumentUri1 As String = "" xctTransform1.Load(strStylesheetUri1) ' Create the XsltArgumentList. Dim argList1 As XsltArgumentList = New XsltArgumentList() ' Add parameters If Not strQualification = "" Then argList1.AddParam("qualification", "", strQualification) End if If Not strStudyMode = "" Then argList1.AddParam("studyMode", "", strStudyMode) End if If Not strSubject = "" Then argList1.AddParam("subject", "", strSubject) End if If Not strPage = "" Then argList1.AddParam("page", "", strPage) End if If Not strPageSize = "" Then argList1.AddParam("pageSize", "", strPageSize) End if ' Create a memory stream to carry the output of the transformation. Dim ms1 As MemoryStream = New MemoryStream() ' Transform the XCRI CAP 1.2 xctTransform1.Transform(strDocumentUri1, argList1, ms1) ' Load the results into an XPathDocument object. ms1.Seek(0, SeekOrigin.Begin) If strRequestAcceptArray.Contains("text/html") Or strRequestAcceptArray.Contains("application/xhtml+xml") Then 'add the course XHTML to the web page. Dim xpnDoc1 As XPathNavigator Dim xpdDoc1 As XPathDocument = New XPathDocument(ms1) xpnDoc1 = xpdDoc1.CreateNavigator xmlCourseList.XPathNavigator = xpnDoc1 Else If strRequestAcceptArray.Contains("application/xcri-cap+xml") 'replace the web page with an XCRI CAP 1.2 document. Dim data As Byte() data = ms1.ToArray() ms1.Flush() ms1.Close() HttpContext.Current.Response.Clear() HttpContext.Current.Response.ContentType = "application/xcri-cap+xml" 'For testing purposes, you can use "application/xml" below, it will render in web browsers. 'HttpContext.Current.Response.ContentType = "application/xml" HttpContext.Current.Response.AddHeader("Content-Length", data.Length.ToString()) HttpContext.Current.Response.BinaryWrite(data) HttpContext.Current.Response.[End]() End If End Sub End Class

The XCRI CAP subsetting XSLT stylesheet, save as xcricap1p2subset.xslt

This XSLT stylesheet uses a similar approach to the xcricap1p2toxhtmlcoursediscovery.xslt one listed in a previous example. This time, though, it returns a subset of your XCRI CAP 1.2 course feed.

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="" xmlns="" xmlns:xs="" xmlns:xd="" xmlns:dc="" xmlns:xhtml="" xmlns:xcri="" xmlns:fn="" xmlns:xsi="" xmlns:mlo="" xmlns:dcterms="" xmlns:credit="" xmlns:xcriTerms="" exclude-result-prefixes="xs xd" version="2.0"> <xsl:output method="xml" version="1.0" encoding="UTF-8" omit-xml-declaration="no" indent="yes"/> <xd:doc scope="stylesheet"> <xd:desc> <xd:p><xd:b>Created on:</xd:b> 2012-04-17</xd:p> <xd:p><xd:b>Author:</xd:b> Tavis Reddick</xd:p> <xd:p>Takes an XCRI CAP 1.2 document and filters it on parameters, and returns either a full course profile or some subset like title, URL and abstract only.</xd:p> </xd:desc> </xd:doc> <xsl:param name="qualification" /> <xsl:param name="studyMode" /> <xsl:param name="subject" /> <xsl:param name="page" select="1" /> <xsl:param name="pageSize" select="20" /> <xsl:param name="profile" select="'abstract'" /><!-- 'abstract' or 'full' --> <xsl:variable name="courses" select="/xcri:catalog/xcri:provider/xcri:course[($qualification = '' or $qualification = mlo:qualification/dc:title) and ($studyMode = '' or $studyMode = xcri:presentation/xcri:studyMode/@identifier) and ($subject = '' or $subject = dc:subject)]" /> <xsl:template match="/xcri:catalog"> <xsl:copy> <xsl:copy-of select="@xsi:schemaLocation" /> <xsl:copy-of select="@generated" /> <xsl:apply-templates /> </xsl:copy> </xsl:template> <xsl:template match="xcri:provider"> <xsl:copy> <xsl:copy-of select="dcterms:title" /> <xsl:for-each select="$courses"> <xsl:sort select="dcterms:title" /> <xsl:choose> <xsl:when test="$profile = 'full'"> <xsl:copy-of select="." /> </xsl:when> <xsl:when test="$profile = 'abstract'"> <xsl:copy> <xsl:copy-of select="dcterms:title" /> <xsl:copy-of select="mlo:url" /> <xsl:copy-of select="xcri:abstract" /> </xsl:copy> </xsl:when> </xsl:choose> </xsl:for-each> </xsl:copy> </xsl:template> </xsl:stylesheet>

Sample XCRI CAP 1.2 output

If you request the URL with a web browser, you will see a web page, because the browser tells the page it will accept "text/html" or "application/xhtml+xml" content types.

For example, will list all the Full time courses as before. However, if you request a special "application/xcri-cap+xml" content type (from a custom web application that is designed to consume XCRI CAP, or a REST-aware debugging tool) using the same URL, you will get something like this:

<?xml version="1.0" encoding="utf-8"?> <catalog xsi:schemaLocation="" generated="2012-04-18T01:29:29.417" xmlns="" xmlns:xsi="" xmlns:xhtml="" xmlns:mlo="" xmlns:dcterms="" xmlns:dc="" xmlns:credit="" xmlns:xcriTerms=""> <provider> <dcterms:title>Kelpie College</dcterms:title> <course> <dcterms:title>Conversational Cetacean Harbour Dolphin Dialect</dcterms:title> <mlo:url></mlo:url> <abstract>Learn to speak the Harbour Dolphin dialect of Cetacean and converse, question and barter with these friendly and helpful creatures.</abstract> </course> <course> <dcterms:title>Hive Mind Speak level 1</dcterms:title> <mlo:url></mlo:url> <abstract>Learn the telepathic Mind Speak of the Hive, and commune humbly with our near-neighbours from the Tenth Planet!</abstract> </course> </provider> </catalog>

Note that we are using the xcricap1p2subset.xslt style sheet's default 'abstract' profile here, which only returns course title, URL and abstract (although it is a valid XCRI CAP 1.2 document, and your application should check this). This would make a nice linked list on a remote site, for example.


This sample should be treated with the same kind of caution as the other samples here:

  • Important! In all this code, replace any references to "" with your own site domain name, or better still fix the code to use a relative address.
  • This is not optimized, production-ready code. In fact, I think Microsoft are bringing out a Web API framework which should make this kind of requirement easier to code.
  • There is no error handling.

A working demonstration of the web pages built from this code scan be found on the fictional Kelpie College website courses section.

For more information on the standard, see the XCRI Knowledge Base.

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.

Back to Code and Examples for XCRI CAP 1.2.

Last updated: .