Comparison with XML/XSLT

Comparison with XML/XSLT

by David Jeske <david@neotonic.com>

ClearSilver is similar to XML/XSLT in many ways. They both help separate presentation from application logic, they both can be used independently to render templated static pages, or with a programming language to render dynamic pages. They are both available when programming in a variety of different languages, including C,C++,Perl, Python, and Java.

However, while ClearSilver has many of XML/XSLT's capabilities, it provides them through extremely simple mechanisms, instead of XML/XSLT's overwhelming complexity. One of the biggest benefits of ClearSilver is that unlike XSLT, its template syntax is a true HTML superset like PHP,ASP and other template systems. XSLT requires your XSL and HTML to be well-formed XML, which makes some HTML pages impossible to render correctly, and others extremely hard to express.

Example 1: Hello Users

However, that's enough talk, lets dive into some examples. First, we'll demonstrate how ClearSilver can be used to render a simple table of users. In both cases, the table will look like this:
Name Email
David Jeske david@neotonic.com
Brandon Long brandon@neotonic.com

Example 1: ClearSilver

ClearSilver has a dataset format, conceptually similar to XML, known as HDF (Hierarchial Data Format). When this dataset format is stored in files it has the extension .hdf. Here is an example dataset file with information about our users:
ex1.hdf
# This is our 'ex1.hdf' dataset file
Users {
  0 {
     Name  = David Jeske
     Email = david@neotonic.com
  }
  1 {
     Name  = Brandon Long
     Email = brandon@neotonic.com
  }
}

ClearSilver also has template files which are a superset of HTML, and generally have the extension .cst.

ex1.cst
<HTML>
<BODY>
<TABLE BORDER=1>
  <TR><TH>Name</TH>
      <TH>Email</TH>
  </TR>
<?cs each:user = Users ?>
  <TR><TD><?cs var:user.Name ?></TD>
      <TD><?cs var:user.Email ?></TD>
  </TR>
<?cs /each ?>
</TABLE>
</BODY>
</HTML>

This template example has one simple each loop which is iterating over all the elements of Users in the source dataset. For each iteration of the loop, it renders a table row with user.Name and user.Email each in a table cell.

Example 1: XML/XSLT

Even if you are not at all familiar with XML, if you are famiar with HTML the following XML dataset should make some sense to you. It is nice and simple if a little verbose because of the end tags.
ex1.xml
<?xml version="1.0" ?>
<PAGE>
 <USER>
  <NAME>David Jeske</NAME>
  <EMAIL>david@neotonic.com</EMAIL>
 </USER>

 <USER>
  <NAME>Brandon Long</NAME>
  <EMAIL>brandon@neotonic.com</EMAIL>
 </USER>
</PAGE>

The next step is to create an XSL template which transforms this XML into HTML output. When XSLT was designed, it was intended to allow a block oriented separation of the templates which render data from the template file which those elements are embedded in. The XSLT template must be a valid XML file. Since HTML tags are XML tags, this means that all HTML is forced into X-HTML, the more rigid standard for HTML.

ex1.xsl
<xsl:stylesheet version="1.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8"/>
<xsl:template match="/">
<html>
<body>
<table border=1>
  <tr><th>Name</th>
      <th>Email</th>
  </tr>
  <xsl:for-each select="PAGE/USER">
    <tr><td><xsl:value-of select="NAME"></td>
        <td><xsl:value-of select="EMAIL"></td>
    </tr>
  </xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Example 1: Comparisons

By simple inspection it is clear that ex1.cst is mostly just an HTML file, except for commands which are embedded in the ClearSilver tag wrapper <?cs ?>, while ex1.xsl is an XSL file, with snippts of HTML embedded within it.

Example 2: Two Column Table

In this example, we're going to render a simple two column table which alternates row color for each column.
Name Name
David Brandon
Rob Judy

Example 2: ClearSilver

The HDF version of the dataset is pretty simple.
two-column.hdf
Page.Users {
 0 = David
 1 = Brandon
 2 = Rob
 3 = Judy
}

The ClearSilver template for this example iterates through each name, and every other iteration through the loop it ends the row and starts a new row. If you are a programmer, even though it has some commands and identifiers which are unfamiliar, the structure of this should be straightforward.

two-column.cst
<TABLE BORDER=1>
<?cs set:COLUMN_COUNT = #2 ?>
<?cs set:cur_col = #0 ?>
<TR>
<?cs each:user = Page.Users ?>
  <?cs if:cur_col > #COLUMN_COUNT ?>
    <?cs set:cur_col = #0 ?>
    </TR><TR>
  <?cs /if ?>
  <TD> <?cs var:user.name ?> </TD>
  <?cs set:cur_col = cur_col + #1 ?>
<?cs /each ?>
</TR>
</TABLE>

Example 2: XML/XSLT

The XML dataset is also very simple.
two-column.xml
<page>
  <user>David</user>
  <user>Brandon</user>
  <user>Rob</user>
  <user>Judy</user>
</page>

The XSL template for this example is starting to show signs of XSLT's complication. Remember that every valid XSL template must be a valid XML file. Our little </TR><TR> trick won't work here, because all tags in XML much match and be strictly nested. Therefore, what we're really doing is iterating through the columns of the table, and pulling values out of the XML dataset for each row. However, there is no numeric loop in XSL, so we are iterating over the values in the dataset, essentially as a "dummy loop", and then we pull out the proper elements from the XML dataset.

two-column.xsl
<table border=1>
<xsl:variable name="COLUMN_COUNT">2</xsl:variable>
<xsl:variable name="height" 
              select="round(count(USER) div $COLUMN_COUNT)" />
<xsl:for-each select="USER">
  <xsl:variable name="p" select="position()" />
  <xsl:if test="$p < $height">
    <tr>
     <td> <xsl:value-of 
             select="./USER[position()=$p*$COLUMN_COUNT]"> </td>
     <td> <xsl:value-of 
             select="./USER[position()=$p*$COLUMN_COUNT+1]"> </td>
    </tr>
  </xsl:if>
</xsl:for-each> 
</table>

Example 3: Dynamic Hello Users

The above examples do not involve any program code to render data. However, Both ClearSilver and XML/XSLT can be used to render dynamic pages as well. This is an area where ClearSilver's simplicity shines. In this example, we will render the same table, and using the same .cst or .xsl template. However, instead of reading the user information from a static file, we will provide it programatically from a Java fragment.

Example 3: Clearsilver

ex2-clearsilver.java
hdf.setValue("Users.0.Name","David Jeske")
hdf.setValue("Users.0.Email","jeske@neotonic.com")
hdf.setValue("Users.1.Name","Brandon Long")
hdf.setValue("Users.1.Email","brandon@neotonic.com")

Example 3: XML/XSLT

There are several interfaces to read and write XML data. However, there is one dom standard which is maintained by the W3C and which is mostly the same from different programming languages. We'll only concern ourselves with this standard, because like in ClearSilver, we want the API to be the same from different programming languages. Below is an example of creating the ex1.xml data using the DOM API from Java.

ex2-xslt.java

Element page    = doc.createElement("PAGE")
doc.appendChild(page)

Element user1   = doc.createElement("USER")
Element name    = doc.createElement("NAME")
name.appendChild(doc.createTextNode("David Jeske"))
user1.appendChild(name)

Element email   = doc.createElement("EMAIL")
email.appendChild(doc.createTextNode("david@neotonic.com")
user1.appendChild(email)
page.appendChild(user1)

Element user2   = doc.createElement("USER")
Element name    = doc.createElement("NAME")
name.appendChild(doc.createTextNode("Brandon Long"))
user2.appendChild(name)
Element email   = doc.createElement("EMAIL")
email.appendChild(doc.createTextNode("brandon@neotonic.com")
user2.appendChild(email)
page.appendChild(user2)
 

Example 3: Comparisons

The first obvious comparison is that the XML data-population is 18 lines when compared with Clearsilver's 4 lines. In fact, in this XML example, we avoided complicated XML features such as namespaces and attributes to keep things simple and HDF is still far simpler to use.

The simplicity of HDF comes from its absolute namespace. Just like a filesystem, or a URL, the HDF hierarchy only allows one element of a given name at a given level. In order to get a list of elements you must name each element differently. In the example above, the two users are named "0" and "1", or "Users.0" and "Users.1".

XML allows you to have any number of elements with the same name at the same level. This results in the complexity you see above in creating the DOM tree. However, that complexity is multiplied when you attempt to read nodes out of the tree. Because there is no way to directly name a node, accessing elements directly involves a complex standard known as XPath.

Conclusions

These examples have shown how ClearSilver shares many of the features of XML/XSLT while being significantly easier to work with. Because ClearSilver is designed specifically for dynamic web-programming, it has many other advantages over XML/XSLT, including its own CGI kit, mapping of environment variables and HTML form data into HDF, and other advantages which you can read about in the CGI Kit documentation.

Other Links