CSCI E-153, Web Development Using XML

XSLT and XPath Wrap-up; Designing and Defining XML Markup

September 21, 2010

Harvard University
Division of Continuing Education
Extension School

Course Web Site: http://cscie153.dce.harvard.edu/

Instructor email: david_heitmeyer@harvard.edu
Course staff email: cscie153@dce.harvard.edu

Lecture Feedback

After you've seen the lecture, please fill out the Lecture Feedback Form for 9/21

  1. What were the most important points of the lecture?
  2. What was the "muddiest" part? (i.e. What is unclear and confusing?)
  3. What would you like to hear more about?

Multiple XSL documents: xsl:import and xsl:include

Why?

Multiple XSL documents: xsl:import and xsl:include

Precedence:
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0">
  <xsl:import href="../../common.xsl"/>
  <xsl:import href="../course.xsl"/>
  <!-- truncated -->
The "more specific" stylesheets should import the more general stylesheets. Remember that import'ed stylesheets can import other stylesheets.
See the XSLT Specification for a discussion on:

US Congress Example with xsl:import

XML

XSLT and Output

List:
congress import list
Grid:
congress import grid
Detail:
congress import detail

Cocoon sitemap:

Stylesheet Structure

list.xsl, grid.xsl, and detail.xsl all import common.xsl.

common.xsl has templates for the document root, header, footer, and navigation. The template matching on document root performs an <xsl:apply-templates />

detail.xsl, list.xsl and grid.xsl implement a template matching on people (called from template matching on document root in common.xsl).

NameRoleImportstemplate matchtemplate name
common.xslProvides XHTML document and page structure. Provides common templates for content. Defines common parameters (state, url).Imported by other templatesmatch="/"
match="person" mode="photo"
header
footer
navigation
viewmode
list.xslProvides specific templates to display members in table rows.common.xslmatch="person"main
grid.xslProvides specific templates to display members in grid form.common.xslmatch="person"main
detail.xslProvides specific templates to display detailed information about members.common.xslmatch="person"
match="current-committee-assignment"
main
viewmode

page components

Multiple XSL documents and Multiple Media Types

Take an example of a project that involves the creation of a course catalog that displays course information in a variety of output media types (XHTML, XHTML Mobile Profile, PDF (via XSL-FO)) in a couple of different view formats (List, Detail).

One might have a stylesheet structure like:

project
|-- common.xsl
|-- fo
|   |-- common.xsl
|   |-- detail.xsl
|   `-- list.xsl
|-- xhtml
|   |-- common.xsl
|   |-- detail.xsl
|   `-- list.xsl
`-- xhtml-mp
    |-- common.xsl
    |-- detail.xsl
    `-- list.xsl

For example, the xhtml/list.xsl would import the xhtml/common.xsl. The xhtml/common.xsl would import the common.xsl.

StylesheetPurposeImports
xhtml/detail.xslTemplates specific to presenting a course detail view format in XHTML. Would not include templates for XHTML output shared by list and detail views. Would not include templates needed by all output media types.xhtml/common.xsl
xhtml/common.xslTemplates common to XHTML output. Would not include templates specific to view formats of detail or list. Would not include templates needed by all output media types.common.xsl
common.xslCommon templates to deal with courses, stylesheet parameters, and other global functions regardless of view format or output format.None
etc.

Multiple Result Documents

Multiple Result Documents

<xsl:result-document> can be used to output multiple result documents.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  
  <xsl:key name="MemberByState" match="person" use="role/@state"/>
  
  <xsl:template match="/">
    <html>
      <head>
        <title>Members by State</title>
      </head>
      <body>
        <h1> Members by State </h1>
        <ul>
          <xsl:for-each-group select="/people/person"
            group-by="role/@state">
            <xsl:sort select="current-grouping-key()"/>
            <li><a href="{concat(current-grouping-key(),'.html')}">
              <xsl:value-of select="current-grouping-key()"/> </a>
              <xsl:call-template name="makefile">
                <xsl:with-param name="state">
                  <xsl:value-of select="current-grouping-key()"/>
                </xsl:with-param>
              </xsl:call-template>
            </li>
          </xsl:for-each-group>
        </ul>
      </body>
    </html>
  </xsl:template>
  <xsl:template name="makefile">
    <xsl:param name="state"/>
    <xsl:result-document href="{concat($state,'.html')}">
      <html>
        <head>
          <title>
            <xsl:value-of select="$state"/>
          </title>
        </head>
        <body>
          <h1>
            <xsl:value-of select="$state"/></h1>
          <ul>
            <xsl:for-each
              select="key('MemberByState', $state)">
              <xsl:sort select="role/@type" order="descending"/>
              <xsl:sort select="@lastname"/>
              <xsl:sort select="@firstname"/>
              <li>
                <xsl:value-of select="@name"/>
              </li>
            </xsl:for-each>
          </ul>
        </body>
      </html>
    </xsl:result-document>
  </xsl:template>
</xsl:stylesheet>

When you run this, you'll get an one file for each state:

[morpheus$ tree
.
|-- congress-multiple.xsl
|-- output
`-- people.xml

1 directory, 2 files
morpheus$ cd output
morpheus$ java -jar /usr/local/saxon/saxon.jar -o index.html ../people.xml ../congress-multiple.xsl
morpheus$ tree
.
|-- AK.html
|-- AL.html
|-- AR.html
|-- AS.html
|-- AZ.html
|-- CA.html
|-- CO.html
|-- CT.html
|-- DC.html
|-- DE.html
|-- FL.html
|-- GA.html
|-- GU.html
|-- HI.html
|-- IA.html
|-- ID.html
|-- IL.html
|-- IN.html
|-- KS.html
|-- KY.html
|-- LA.html
|-- MA.html
|-- MD.html
|-- ME.html
|-- MI.html
|-- MN.html
|-- MO.html
|-- MS.html
|-- MT.html
|-- NC.html
|-- ND.html
|-- NE.html
|-- NH.html
|-- NJ.html
|-- NM.html
|-- NV.html
|-- NY.html
|-- OH.html
|-- OK.html
|-- OR.html
|-- PA.html
|-- PR.html
|-- RI.html
|-- SC.html
|-- SD.html
|-- TN.html
|-- TX.html
|-- UT.html
|-- VA.html
|-- VI.html
|-- VT.html
|-- WA.html
|-- WI.html
|-- WV.html
|-- WY.html
`-- index.html

0 directories, 56 files

XSLT Cookbooks and Libraries

XSLT Standard Library

XSLT Standard Library

XSLT Standard Library: Math

To use the XSLT Standard Library:

  1. define namespace
  2. import the library
  3. call the template function

An example with ordinals: The "math:ordinal" template returns an ordinal, given a number (1 → 1st → first)


<xsl:template name="math:ordinal">
  <xsl:param name="number"/>
  <xsl:choose>
    <xsl:when test='$number &lt; 0'/>
    <xsl:otherwise>
      <xsl:value-of select='$number'/>
      <xsl:choose>
        <xsl:when test='$number = 11 or $number = 12 or $number = 13'>th</xsl:when>
        <xsl:when test='$number mod 10 = 1'>st</xsl:when>
        <xsl:when test='$number mod 10 = 2'>nd</xsl:when>
        <xsl:when test='$number mod 10 = 3'>rd</xsl:when>
        <xsl:otherwise>th</xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Ordinals, 0 to 99

ordinals

Three points here:

  1. Use of XSLT Standard Library for "ordinal" and "ordinal-as-word" functions.
  2. Use of a recursive template to go from 0 to 99
  3. Import of "xhtml.xsl", which provides XHTML document scaffolding and call to "main" template.

XSLT Standard Library

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:math="http://xsltsl.org/math"
    version="1.0">
  <xsl:import href="http://morpheus.dce.harvard.edu/xsltsl/stdlib.xsl"/>

Recursive Template

Template gets "max" and "number", and then calls itself with "number + 1" if "max" has not been reached.

  <xsl:template name="getordinal">
    <xsl:param name="max" select="100"/>
    <xsl:param name="number" select="0"/>
    <tr>
      <!-- number -->
      <td>
        <xsl:value-of select="$number"/>
      </td>
      <!-- ordinal -->
      <td>
        <xsl:call-template name="math:ordinal">
          <xsl:with-param name="number" select="$number"/>
        </xsl:call-template>
      </td>
      <!-- ordinal as word -->
      <td>
        <xsl:call-template name="math:ordinal-as-word">
          <xsl:with-param name="number" select="$number"/>
        </xsl:call-template>
      </td>
    </tr>
    <xsl:if test="$number + 1 &lt; $max">
      <xsl:call-template name="getordinal">
        <xsl:with-param name="number" select="$number + 1"/>
        <xsl:with-param name="max" select="$max"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

xsl:import

math.xsl

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:math="http://xsltsl.org/math"
    version="1.0">
  <xsl:import href="http://morpheus.dce.harvard.edu/xsltsl/stdlib.xsl"/>
  <xsl:import href="xhtml.xsl"/>
  <xsl:variable name="title">
    Demonstration of XSLT SL Math Ordinal Function
  </xsl:variable>

  <xsl:template name="main">
    <table cellspacing="0" cellpadding="0">
      <thead>
        <tr>
          <th>Number</th>
          <th>Ordinal</th>
          <th>Ordinal as Word</th>
        </tr>
      </thead>
      <tbody>
        <xsl:call-template name="getordinal">
          <xsl:with-param name="number" select="0"/>
          <xsl:with-param name="max" select="100"/>
        </xsl:call-template>
      </tbody>
    </table>
  </xsl:template>
  
  <xsl:template name="getordinal">
    <xsl:param name="max" select="100"/>
    <xsl:param name="number" select="0"/>
    <tr>
      <!-- number -->
      <td>
        <xsl:value-of select="$number"/>
      </td>
      <!-- ordinal -->
      <td>
        <xsl:call-template name="math:ordinal">
          <xsl:with-param name="number" select="$number"/>
        </xsl:call-template>
      </td>
      <!-- ordinal as word -->
      <td>
        <xsl:call-template name="math:ordinal-as-word">
          <xsl:with-param name="number" select="$number"/>
        </xsl:call-template>
      </td>
    </tr>
    <xsl:if test="$number + 1 &lt; $max">
      <xsl:call-template name="getordinal">
        <xsl:with-param name="number" select="$number + 1"/>
        <xsl:with-param name="max" select="$max"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

xhtml.xsl

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:variable name="title">Untitled </xsl:variable>
  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="$title"/>
        </title>
        <xsl:call-template name="style"/>
      </head>
      <body>
        <h1>
          <xsl:value-of select="$title"/>
        </h1>
        <xsl:call-template name="main"/>
      </body>
    </html>
  </xsl:template>
  <xsl:template name="style">
    <style type="text/css">
      body, th, td { font-family: Calibri,Verdana,Tahoma,helvetica,sans-serif;}
      table { border: thin solid black; }
      thead { background-color: #ffffcc; }
      th, td {border-top: thin solid black; padding: 2px;}
      td {border-collapse: collapse; margin: 0px;}
    </style>
  </xsl:template>
</xsl:stylesheet>

Ordinals: Loop using a sequence

XSLT 2 provides ways of creating sequences. Instead of using a recursive template, we can build a sequence from 0 to 99 and iterate through the sequence items.

Within "main" template:

<tbody>
  <xsl:for-each select="for $i in 0 to 99 return $i">
    <xsl:call-template name="make_ordinal_row"/>
  </xsl:for-each>
</tbody>

And then the "make_ordinal_row" simply creates the "tr" content -- it is not recursive.

  <xsl:template name="make_ordinal_row">
    <xsl:variable name="number">
      <xsl:value-of select="."/>
    </xsl:variable> 
    <tr>
      <!-- number -->
      <td>
        <xsl:value-of select="$number"/>
      </td>
      <!-- ordinal -->
      <td>
        <xsl:call-template name="math:ordinal">
          <xsl:with-param name="number" select="$number"/>
        </xsl:call-template>
      </td>
      <!-- ordinal as word -->
      <td>
        <xsl:call-template name="math:ordinal-as-word">
          <xsl:with-param name="number" select="$number"/>
        </xsl:call-template>
      </td>
    </tr>
  </xsl:template>

Controlling White-space

Top level elements in stylesheet. "elements" attribute has white-space separated list of elements which directive applies to:

xsl:strip-space

All elements

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="/">
    <xsl:copy-of select="*"/>
  </xsl:template>
</xsl:stylesheet>

xsl:strip-space on person

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>
  <xsl:strip-space elements="person"/>
  <xsl:template match="/">
    <xsl:copy-of select="*"/>
  </xsl:template>
</xsl:stylesheet>

xsl:preserve-space

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>
  <xsl:preserve-space elements="*"/>
  <xsl:template match="/">
    <xsl:copy-of select="*"/>
  </xsl:template>
</xsl:stylesheet>

Comparison

See: Seeking Equality by Bob Ducharme at XML.com

XPath 2.0 provides new value comparison operators, which are for single-value comparisons:

Date Comparison

People data from GovTrack.US contains entries for all people who have served in the US House or Senate (about 12,300). For my examples, I am interested in reducing this set to those people who are currently serving (539).

In XSLT 1.0, this was not straightforward. With XSLT 2.0, we can use the date comparison feataures.

Each person has one or more roles (child::role) associated with them, and each role has an end date (child::role/@enddate):

<?xml version="1.0" encoding="utf-8"?>
<people>
  <person id="400001" lastname="Abercrombie" firstname="Neil" birthday="1938-06-26" gender="M" url="http://www.house.gov/abercrombie" party="Democrat" osid="N00007665" bioguideid="A000014" title="Rep." state="HI" district="1" name="Rep. Neil Abercrombie [D, HI-1]">
    <role type="rep" startdate="2007-01-01" enddate="2008-12-31" party="Democrat" state="HI" district="1" url="http://www.house.gov/abercrombie" />
    <current-committee-assignment committee="House Armed Services" />
    <current-committee-assignment committee="House Armed Services" subcommittee="Air and Land Forces" role="Chair" />
    <current-committee-assignment committee="House Armed Services" subcommittee="Seapower and Expeditionary Forces" />
    <current-committee-assignment committee="House Natural Resources" />
    <current-committee-assignment committee="House Natural Resources" subcommittee="Fisheries, Wildlife, and Oceans" />
    <current-committee-assignment committee="House Natural Resources" subcommittee="National Parks, Forests, and Public Lands" />
  </person>

XSLT that copies only the current members and current roles:

<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"                
  version="2.0">
  <xsl:output method="xml" />
  <xsl:template match="person" />
  <xsl:template match="role" />
  <xsl:template match="person[role/xs:date(@enddate) > current-date()]">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="role[xs:date(@enddate) > current-date()]">
    <xsl:copy-of select="."/>
  </xsl:template>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

string-join

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0">
  <xsl:template match="/">
    <html>
      <head>
        <title>US Congress</title>
      </head>
      <body>
        <h1>First Names</h1>
        <xsl:value-of select="string-join(/people/person/@firstname, ', ')"/>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

string join

Regular Expressions with XSLT 2.0

matches

<xsl:template match="h:a[matches(@href,'^http://.*\.harvard\.edu')">
  <xsl:copy>
    <xsl:attribute name="class">
      <xsl:value-of select="'internal'"/>
    </xsl:attribute>
  </xsl:copy>
</xsl:template>

regular expressions

  <xsl:template match="number">
    <number>
      <xsl:analyze-string select="normalize-space(.)" regex="^(\d\d\d)(\d\d\d)(\d\d\d\d)$">
        <xsl:matching-substring>
          <area_code>
            <xsl:value-of select="regex-group(1)"/>
          </area_code>
          <prefix>
            <xsl:value-of select="regex-group(2)"/>
          </prefix>
          <last4>
            <xsl:value-of select="regex-group(3)"/>
          </last4>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
          <xsl:value-of select="."/>
        </xsl:non-matching-substring>
      </xsl:analyze-string>
    </number>
  </xsl:template>

phone.xml

<phone>
  <number>6174951000</number>
  <number>6174950000</number>
  <number>6174951215</number>
</phone>
<?xml version="1.0" encoding="ISO-8859-1"?>
<phone>
  <number>
    <area_code>617</area_code>
    <prefix>495</prefix>
    <last4>1000</last4>
  </number>
  <number>
    <area_code>617</area_code>
    <prefix>495</prefix>
    <last4>0000</last4>
  </number>
  <number>
    <area_code>617</area_code>
    <prefix>495</prefix>
    <last4>1215</last4>
  </number>
</phone>

Creating Sequences: tokenize

preamble

<xsl:for-each 
  select="tokenize(/preamble,'\W+')">
  <li><xsl:value-of select="."/></li>
</xsl:for-each>

preamble

Creating Sequences: for..in..return

    <xsl:value-of select="sum(
            for $word in tokenize(/preamble,'\W+') 
            return string-length($word)
            )"/>

preamble

Creating Sequences: Counting Words

<xsl:for-each-group group-by="."
  select="
    for $w in tokenize(string(.), '\W+')
      return lower-case($w)">
  <xsl:sort select="count(current-group())" order="descending"/>
    <tr>
      <td>
        <xsl:value-of select="current-grouping-key()"/>
      </td>
      <td>
        <xsl:value-of select="count(current-group())"/>
      </td>
    </tr>
</xsl:for-each-group>

preamble

Relative Links in Cocoon

Problem: have a common "XHTML template" but different relative locations

Your CSS URL is css/site.css

You want an easy way to refer to these areas, whether or not you are creating the link from a page 'one' level deep or 'three' levels deep (or any arbitrary level)

Input Modules

Cocoon has various Input Modules, which can be used to access data in a modular way. For example, various request modules will allow you to access properties relating to the request, the system, global variables, etc.

The BaseLinkModule provides access to a relative path to the root of the request. This lets you build a relative link easily by passing in this parameter to your stylesheet.

BaseLinkModule

You refer to properties from the input modules with the curly-brace syntax that you use for matches.

In sitemap:

<map:match pattern="**.html">
  <map:generate src="cocoon:{1}.xml"/>
  <map:transform src="template.xsl">
    <map:parameter name="relativepath"
		   value="{baselink:SitemapBaseLink}"/>
  </map:transform>
  <map:generate type="xhtml"
</map:match>

Your XSLT needs to accept the parameter, and then use it when linking to images, javascript, css:

Define parameter:

<xsl:param name="relativepath"/>

Use parameter to create relative link to CSS:

<link rel="stylesheet" 
    type="text/css"
    href="{$relativepath}css/site.css" />

Absolute Links

Absolute links are useful for image URLs in XSl-FO when they are serialized by Cocoon's fo2pdf serializer. Also, they are needed if you refer to a CSS stylesheet in an SVG processing-instruction (and Cocon will serialize the SVG to PNG or JPEG).

Here, the global input module is useful. In the sitemap.xmap, global variables are defined as a child of 'global-variables', which is a child of 'map:pipelines'

<map:component-configurations>
<map:pipelines>
  <map:component-configurations>
    <global-variables>
      <baseurl>http://localhost:8888/assignment3/</baseurl>
    </global-variables>
  </map:component-configurations>
  <!-- ...cut... -->

Then, you can pass the global variable to the XSLT via a map:parameter, define the parameter in the XSLT, and use it:

In sitemap:

<map:match pattern="**.html">
  <map:generate src="data.xml"/>
  <map:transform src="template.xsl">
    <map:parameter name="absolutebaseurl"
		   value="{global:baseurl}"/>
  </map:transform>
  <map:generate type="xhtml"
</map:match>

Of course, your XSLT needs to accept the parameter, and then use it when linking to images, javascript, css:

<xsl:param name="absolutebaseurl"/>
<link rel="stylesheet" 
    type="text/css"
    href="{$absolutebaseurl}css/site.css" />

Images referred to in CSS

What about images referred to in CSS?

project
|--xsl
|--css
| `--site.css
`--images
`--logo.png
#header { 	
      height: 100px; 
      background-image: url(../images/harvard_shield_72.png);
}
    

Designing XML Markup

Avoid:

Jeni Tennison on "Bad XML"

Bad XML by Jeni Tennison, O'Reilly Media's Information about XML (May 2008)

Good XML

Narrative Documents

From The Economist: Too Soon to Kiss and Make-Up (October 16, 2008):

RUSSIA announced this week that, just as it promised, it had pulled all its troops in Georgia back to the two disputed territories of Abkhazia and South Ossetia. Many European Union leaders were swift to praise the Kremlin for meeting the conditions it agreed with France's Nicolas Sarkozy some six weeks ago — and almost as quick to suggest a return to business as usual. A majority now want to start talks in November on a new "partnership and co-operation agreement". Their none-so-subtle message is: forget about the pesky Georgian president, Mikheil Saakashvili, who was anyway responsible for starting the war on August 7th, and attend instead to the more urgent task of repairing relations with our biggest energy supplier.

Adding meaning via markup

<article
  cite="http://www.economist.com/opinion/displaystory.cfm?story_id=12429514">
  <p><country code="RU">Russia</country> announced this week that, just as it promised, it had
  pulled all its troops in <country code="GE">Georgia</country> back to the two disputed territories of 
  <territory>Abkhazia</territory> and <territory>South Ossetia</territory>. Many 
  <organization code="EU">European Union</organization> leaders were swift to praise the 
  <organization>Kremlin</organization> for meeting the conditions it agreed with
  <country code="FR">France's</country> <person>Nicolas Sarkozy</person> some six weeks ago -- and 
  almost as quick to suggest a return to business as usual. A majority now want to start talks in 
  <date date="2008-11">November</date> on a new "partnership and co-operation agreement". 
  Their none-so-subtle message is: forget about the pesky Georgian president, 
  <person>Mikheil Saakashvili</person>, who was anyway responsible for
  starting the war on <date date="2008-08-07">August 7th</date>, and attend instead to the 
  more urgent task of repairing relations with our biggest energy supplier.
  </p>
</article>

Data-centric Documents

Data can be contained in elements or attributes.

Elements:

<quote>
  <company>YHOO</company>
  <date>2010-09-27</date>
  <close>14.279</close>
  <volume>5820000</volume>
</quote>

Attributes:

<quote company="YHOO" date="2010-09-27" close="14.279" volume="5820000"/>

"Mixed":

<quote>
  <company value="YHOO"/>
  <date value="2010-09-27"/>
  <close value="14.279"/>
  <volume value="5820000"/>
</quote>

Processing XML

Element based XML

<document>
<element>
<attribute1>attribute1 text</attribute1>
<attribute2>attribute2 text</attribute2>
<attribute3>attribute3 text</attribute3>
</element>
<element>
<attribute1>attribute1 text</attribute1>
<attribute2>attribute2 text</attribute2>
<attribute3>attribute3 text</attribute3>
</element>
</document>

Attribute based XML

<document>
  <element attribute1="attribute1 text" 
    attribute2="attribute2 text" attribute3="attribute3 text"/>
  <element attribute1="attribute1 text" 
    attribute2="attribute2 text" attribute3="attribute3 text"/>
</document>

Mixed model XML

<document>
<element>
<attribute1 value="attribute1 text"/>
<attribute2 value="attribute2 text"/>
<attribute3 value="attribute3 text"/>
</element>
<element>
<attribute1 value="attribute1 text"/>
<attribute2 value="attribute2 text"/>
<attribute3 value="attribute3 text"/>
</element>
</document>

processing by saxon9

Some Basic Rules

Example with Weather

Current Weather Conditions in XML from the National Weather Service
(http://www.weather.gov/xml/current_obs/KBOS.xml:

Observations on XML

<current_observation version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:noNamespaceSchemaLocation="http://www.weather.gov/view/current_observation.xsd">
    <credit>NOAA's National Weather Service</credit>
    <credit_URL>http://weather.gov/</credit_URL>
    <image>
        <url>http://weather.gov/images/xml_logo.gif</url>
        <title>NOAA's National Weather Service</title>
        <link>http://weather.gov</link>
    </image>
    <suggested_pickup>15 minutes after the hour</suggested_pickup>
    <suggested_pickup_period>60</suggested_pickup_period>
    <location>Boston, Logan International Airport, MA</location>
    <station_id>KBOS</station_id>
    <latitude>42.38</latitude>
    <longitude>-71.03</longitude>
    <observation_time>Last Updated on Sep 28 2010, 1:54 pm EDT</observation_time>
    <observation_time_rfc822>Tue, 28 Sep 2010 13:54:00 -0400</observation_time_rfc822>
    <weather>Mostly Cloudy</weather>
    <temperature_string>77.0 F (25.0 C)</temperature_string>
    <temp_f>77.0</temp_f>
    <temp_c>25.0</temp_c>
    <relative_humidity>77</relative_humidity>
    <wind_string>South at 17.3 MPH (15 KT)</wind_string>
    <wind_dir>South</wind_dir>
    <wind_degrees>200</wind_degrees>
    <wind_mph>17.3</wind_mph>
    <wind_kt>15</wind_kt>
    <pressure_string>1009.1 mb</pressure_string>
    <pressure_mb>1009.1</pressure_mb>
    <pressure_in>29.80</pressure_in>
    <dewpoint_string>69.1 F (20.6 C)</dewpoint_string>
    <dewpoint_f>69.1</dewpoint_f>
    <dewpoint_c>20.6</dewpoint_c>
    <visibility_mi>10.00</visibility_mi>
    <icon_url_base>http://weather.gov/weather/images/fcicons/</icon_url_base>
    <two_day_history_url>http://www.weather.gov/data/obhistory/KBOS.html</two_day_history_url>
    <icon_url_name>bkn.jpg</icon_url_name>
    <ob_url>http://www.nws.noaa.gov/data/METAR/KBOS.1.txt</ob_url>
    <disclaimer_url>http://weather.gov/disclaimer.html</disclaimer_url>
    <copyright_url>http://weather.gov/disclaimer.html</copyright_url>
    <privacy_policy_url>http://weather.gov/notice.html</privacy_policy_url>
</current_observation>

Possible Improvements: Time Standards

Possible Improvements: Namespaces and Geo tagging

Possible Improvements: Nesting

Possible Improvements: Metadata in Attributes

Bad XML - Generic Names

Dump from a database table in XML:

bad xml


Search Results:

bad xml

Bad XML - Escaped Markup

Post about article, "Escaped Markup Considered Harmful" by Norman Walsh

escaped markup

Escaped Markup

Coping with escaped markup

disable-output-escaping

Flexibility: Atom

"type" attribute allows for text, html, and xhtml. Note that the "xhtml" type allows XHTML markup to be part of Atom document!

From On RSS and Atom by DeWitt Clinton:
atom type attribute

DHS Threat Level Advisory

Improvements for:

<THREAT_ADVISORY CONDITION="ELEVATED" />

dhs 

threat level
http://www.dhs.gov/dhspublic/getAdvisoryCondition

Defining XML Documents

Valid XML Documents

  1. Well-formed and
  2. Conforming to rules of schema
Schemas for XML. Rules that define the elements, attributes, and structure of a particular markup language.

Schema Features

Why Bother with Definitions and Validation

Sometimes you won't bother.

Document Type Definition (DTD) by Example

XML Bookmark Exchange Language (XBEL)

Snippet of XBEL document of a "bookmark" element:

<!-- bookmark element snippet from XBEL -->
<bookmark href="http://www.dirtys.com/">
  <title>Dirty's Potato Chips</title>
  <desc>
      My very favorite potato chips. Their motto is, 
      "We don't wash out the natural potato flavor, 
      so our chips are crunchier and tastier."
  </desc>
</bookmark>

DOCTYPE Declaration

A breakdown of the DOCTYPE declaration

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

XML Bookmark Exchange Language (XBEL)

<?xml version="1.0"?>
<!DOCTYPE xbel PUBLIC 
  "+//IDN python.org//DTD XML Bookmark Exchange Language 1.0//EN//XML" 
  "http://www.python.org/topics/xml/dtds/xbel-1.0.dtd">

Operators in DTDs

noneExactly one.(c) = c must appear exactly once
,Required sequence (all are required, in order)(a,b,c) = a AND b AND c
|The OR operator (allow any one or more, at least one required)(a|b|c) = a OR b OR c
?Optional occurrencea? = a is OPTIONAL
+One or more (required and repeatable)a+ = one OR more a
*Zero or more (optional and repeatable)a* = zero OR more a
-Must not appear (not allowed)B- = B must not be present
& Required (in any order)(a & b & c) = a AND b AND c must appear

Attributes

Attribute TypesMeaning
CDATACharacter data - plain text that is not marked up
PCDATAParsed Character Data - text that is parsed for character entities or mark up.
EnumeratedA list of possible values from which exactly one will be chosen
IDA unique name not shared by any other ID type attribute in the document
IDREFThe value of an ID type attribute of an element in the document
IDREFSMultiple IDs of elements separated by whitespace
ENTITYThe name of an entity declared in the DTD
ENTITIESThe names of multiple entities declared in the DTD, separated by whitespace
NMTOKENAn XML name
NOTATIONThe name of a notation declared in a DTD
NMTOKENSMultiple XML names separated by whitespace

#REQUIRED -- the value must be provided or an error is return
#IMPLIED -- for values that are not required by the user nor by the author
#FIXED -- the document author includes a default value that does not change

XBEL DTD

<!-- This is the XML Bookmarks Exchange Language, version 1.0.  It
     should be used with the formal public identifier:

        +//IDN python.org//DTD XML Bookmark Exchange Language 1.0//EN//XML

     One valid system identifier at which this DTD will remain
     available is:

        http://pyxml.sourceforge.net/topics/dtds/xbel-1.0.dtd

     More information on the DTD, including reference
     documentation, is available at:

        http://www.python.org/topics/xml/xbel/

    Attributes which take date/time values should encode the value
    according to the W3C NOTE on date/time formats:

        http://www.w3.org/TR/NOTE-datetime
  -->


<!-- Customization entities.  Define these before "including" this DTD
     to create "subclassed" DTDs.
  -->
<!ENTITY % local.node.att  "">
<!ENTITY % local.url.att   "">
<!ENTITY % local.nodes.mix "">

<!ENTITY % node.att     "id       ID    #IMPLIED
                         added    CDATA #IMPLIED
                         %local.node.att;">

<!ENTITY % url.att      "href     CDATA #REQUIRED
                         visited  CDATA #IMPLIED
                         modified CDATA #IMPLIED
                         %local.url.att;">

<!ENTITY % nodes.mix    "bookmark|folder|alias|separator
                         %local.nodes.mix;">


<!ELEMENT xbel (title?, info?, desc?, (%nodes.mix;)*)>
<!ATTLIST xbel
            %node.att;
            version  CDATA      #FIXED "1.0"
>
<!ELEMENT title      (#PCDATA)>

<!--=================== Info ======================================-->

<!ELEMENT info (metadata+)>

<!ELEMENT metadata EMPTY>
<!ATTLIST metadata
            owner    CDATA      #REQUIRED
>

<!--=================== Folder ====================================-->

<!ELEMENT folder   (title?, info?, desc?, (%nodes.mix;)*)>
<!ATTLIST folder
            %node.att;
            folded   (yes|no)   'yes'   
>

<!--=================== Bookmark ==================================-->

<!ELEMENT bookmark (title?, info?, desc?)>
<!ATTLIST bookmark
            %node.att;
            %url.att;
>

<!ELEMENT desc       (#PCDATA)>

<!--=================== Separator =================================-->

<!ELEMENT separator EMPTY>

<!--=================== Alias =====================================-->

<!-- <alias> elements correspond to Netscape bookmark aliases.  The
     required "ref" attribute must refer to a <bookmark> or <folder>
     element.  Note that MSIE aliases can refer to folders, so that is
     supported in XBEL.  Applications must be careful about traversing
     aliases to folders to avoid improper recursion through circular
     data structures.
  -->

<!ELEMENT alias EMPTY>
<!ATTLIST alias
            ref       IDREF     #REQUIRED
>

Documentation from DTD

Several samples are at: http://morpheus.dce.harvard.edu/dtd/

RELAX NG

RELAX NG Home:

RELAX NG is a schema language for XML. The key features of RELAX NG are that it:

The RELAX NG specifications have been developed within OASIS by the RELAX NG Technical Committeee. RELAX NG is an International Standard (ISO/IEC 19757-2). RELAX NG was based on TREX designed by James Clark and RELAX designed by MURATA Makoto.


Recommended RNG Readings

A Look at Meerkat XML

Russian Nesting Dolls

Meerkat: Russian Doll

XML

<?xml version="1.0" encoding="utf-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
  <start>
    <element name="meerkat">
      <zeroOrMore>
        <choice>
          <element name="title">
            <text />
          </element>
          <element name="link">
            <text />
          </element>
          <element name="description">
            <text />
          </element>
          <element name="language">
            <text />
          </element>
          <optional>
            <element name="image">
              <zeroOrMore>
                <choice>
                  <element name="title">
                    <text />
                  </element>
                  <element name="url">
                    <text />
                  </element>
                  <ref name="link" />
                  <optional>
                    <element name="width">
                      <text />
                    </element>
                  </optional>
                  <optional>
                    <element name="height">
                      <text />
                    </element>
                  </optional>
                  <optional>
                    <element name="description">
                      <text />
                    </element>
                  </optional>
                </choice>
              </zeroOrMore>
            </element>
          </optional>
          <oneOrMore>
            <element name="story">
              <optional>
                <attribute name="id">
                  <data type="ID" />
                </attribute>
              </optional>
              <zeroOrMore>
                <choice>
                  <element name="title">
                    <text />
                  </element>
                  <element name="link">
                    <text />
                  </element>
                  <element name="description">
                    <text />
                  </element>
                  <element name="category">
                    <text />
                  </element>
                  <element name="channel">
                    <text />
                  </element>
                  <element name="timestamp">
                    <text />
                  </element>
                </choice>
              </zeroOrMore>
            </element>
          </oneOrMore>
        </choice>
      </zeroOrMore>
    </element>
  </start>
</grammar>

Compact Syntax

start =
  element meerkat {
    (element title { text }
     | element link { text }
     | element description { text }
     | element language { text }
     | element image {
         (element title { text }
          | element url { text }
          | link
          | element width { text }?
          | element height { text }?
          | element description { text }?)*
       }?
     | element story {
         attribute id { xsd:ID }?,
         (element title { text }
          | element link { text }
          | element description { text }
          | element category { text }
          | element channel { text }
          | element timestamp { text })*
       }+)*
  }

Meerkat: Flat Model

<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
  <define name="meerkat">
    <element name="meerkat">
      <ref name="attlist.meerkat"/>
      <zeroOrMore>
        <choice>
          <ref name="title"/>
          <ref name="link"/>
          <ref name="description"/>
          <ref name="language"/>
          <optional>
            <ref name="image"/>
          </optional>
          <oneOrMore>
            <ref name="story"/>
          </oneOrMore>
        </choice>
      </zeroOrMore>
    </element>
  </define>
  <define name="attlist.meerkat" combine="interleave">
    <empty/>
  </define>
  <define name="attlist.story" combine="interleave">
    <optional>
      <attribute name="id">
        <data type="ID"/>
      </attribute>
    </optional>
  </define>
  <define name="title">
    <element name="title">
      <ref name="attlist.title"/>
      <text/>
    </element>
  </define>
  <define name="attlist.title" combine="interleave">
    <empty/>
  </define>
  <define name="link">
    <element name="link">
      <ref name="attlist.link"/>
      <text/>
    </element>
  </define>
  <define name="attlist.link" combine="interleave">
    <empty/>
  </define>
  <define name="description">
    <element name="description">
      <ref name="attlist.description"/>
      <text/>
    </element>
  </define>
  <define name="attlist.description" combine="interleave">
    <empty/>
  </define>
  <define name="language">
    <element name="language">
      <ref name="attlist.language"/>
      <text/>
    </element>
  </define>
  <define name="attlist.language" combine="interleave">
    <empty/>
  </define>
  <define name="image">
    <element name="image">
      <ref name="attlist.image"/>
      <zeroOrMore>
        <choice>
          <ref name="title"/>
          <ref name="url"/>
          <ref name="link"/>
          <optional>
            <ref name="width"/>
          </optional>
          <optional>
            <ref name="height"/>
          </optional>
          <optional>
            <ref name="description"/>
          </optional>
        </choice>
      </zeroOrMore>
    </element>
  </define>
  <define name="attlist.image" combine="interleave">
    <empty/>
  </define>
  <define name="story">
    <element name="story">
      <ref name="attlist.story"/>
      <zeroOrMore>
        <choice>
          <ref name="title"/>
          <ref name="link"/>
          <ref name="description"/>
          <ref name="category"/>
          <ref name="channel"/>
          <ref name="timestamp"/>
        </choice>
      </zeroOrMore>
    </element>
  </define>
  <define name="url">
    <element name="url">
      <ref name="attlist.url"/>
      <text/>
    </element>
  </define>
  <define name="attlist.url" combine="interleave">
    <empty/>
  </define>
  <define name="rating">
    <element name="rating">
      <ref name="attlist.rating"/>
      <text/>
    </element>
  </define>
  <define name="attlist.rating" combine="interleave">
    <empty/>
  </define>
  <define name="width">
    <element name="width">
      <ref name="attlist.width"/>
      <text/>
    </element>
  </define>
  <define name="attlist.width" combine="interleave">
    <empty/>
  </define>
  <define name="height">
    <element name="height">
      <ref name="attlist.height"/>
      <text/>
    </element>
  </define>
  <define name="attlist.height" combine="interleave">
    <empty/>
  </define>
  <define name="category">
    <element name="category">
      <ref name="attlist.category"/>
      <text/>
    </element>
  </define>
  <define name="attlist.category" combine="interleave">
    <empty/>
  </define>
  <define name="channel">
    <element name="channel">
      <ref name="attlist.channel"/>
      <text/>
    </element>
  </define>
  <define name="attlist.channel" combine="interleave">
    <empty/>
  </define>
  <define name="timestamp">
    <element name="timestamp">
      <ref name="attlist.timestamp"/>
      <text/>
    </element>
  </define>
  <define name="attlist.timestamp" combine="interleave">
    <empty/>
  </define>
  <start>
    <choice>
      <ref name="meerkat"/>
      <ref name="rating"/>
    </choice>
  </start>
</grammar>

Compact Syntax

meerkat =
  element meerkat {
    attlist.meerkat,
    (title | link | description | language | image? | story+)*
  }
attlist.meerkat &= empty
attlist.story &= attribute id { xsd:ID }?
title = element title { attlist.title, text }
attlist.title &= empty
link = element link { attlist.link, text }
attlist.link &= empty
description = element description { attlist.description, text }
attlist.description &= empty
language = element language { attlist.language, text }
attlist.language &= empty
image =
  element image {
    attlist.image,
    (title | url | link | width? | height? | description?)*
  }
attlist.image &= empty
story =
  element story {
    attlist.story,
    (title | link | description | category | channel | timestamp)*
  }
url = element url { attlist.url, text }
attlist.url &= empty
rating = element rating { attlist.rating, text }
attlist.rating &= empty
width = element width { attlist.width, text }
attlist.width &= empty
height = element height { attlist.height, text }
attlist.height &= empty
category = element category { attlist.category, text }
attlist.category &= empty
channel = element channel { attlist.channel, text }
attlist.channel &= empty
timestamp = element timestamp { attlist.timestamp, text }
attlist.timestamp &= empty
start = meerkat | rating    

Validating RELAX NG

James Clark's Jing tool.
% java -jar /usr/local/jing/bin/jing.jar \
?  meerkat.rng meerkat.xml
% java -jar /usr/local/jing/bin/jing.jar -c \
?  meerkat.rnc meerkat.xml

Techquila offers stylesheets that produce DocBook and SVG documentation from RNG.

W3C XML Schema

W3C XML Schema Home

Recommendations:

Schema Types
Schema types image from http://www.w3.org/TR/xmlschema-2/

W3C XML Schema "Purchase Order" Example

Purchase Order Examples:

<?xml version="1.0"?>
<purchaseOrder orderDate="1999-10-20">
   <shipTo country="US">
      <name>Alice Smith</name>
      <street>123 Maple Street</street>
      <city>Mill Valley</city>
      <state>CA</state>
      <zip>90952</zip>
   </shipTo>
   <billTo country="US">
      <name>Robert Smith</name>
      <street>8 Oak Avenue</street>
      <city>Old Town</city>
      <state>PA</state>
      <zip>95819</zip>
   </billTo>
   <comment>Hurry, my lawn is going wild<!/comment>
   <items>
      <item partNum="872-AA">
         <productName>Lawnmower</productName>
         <quantity>1</quantity>
         <USPrice>148.95</USPrice>
         <comment>Confirm this is electric</comment>
      </item>
      <item partNum="926-AA">
         <productName>Baby Monitor</productName>
         <quantity>1</quantity>
         <USPrice>39.98</USPrice>
         <shipDate>1999-05-21</shipDate>
      </item>
   </items>
</purchaseOrder>

W3C XML Schema:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <xsd:annotation>
    <xsd:documentation xml:lang="en">
     Purchase order schema for Example.com.
     Copyright 2000 Example.com. All rights reserved.
    </xsd:documentation>
  </xsd:annotation>

  <xsd:element name="purchaseOrder" type="PurchaseOrderType"/>

  <xsd:element name="comment" type="xsd:string"/>

  <xsd:complexType name="PurchaseOrderType">
    <xsd:sequence>
      <xsd:element name="shipTo" type="USAddress"/>
      <xsd:element name="billTo" type="USAddress"/>
      <xsd:element ref="comment" minOccurs="0"/>
      <xsd:element name="items"  type="Items"/>
    </xsd:sequence>
    <xsd:attribute name="orderDate" type="xsd:date"/>
  </xsd:complexType>

  <xsd:complexType name="USAddress">
    <xsd:sequence>
      <xsd:element name="name"   type="xsd:string"/>
      <xsd:element name="street" type="xsd:string"/>
      <xsd:element name="city"   type="xsd:string"/>
      <xsd:element name="state"  type="xsd:string"/>
      <xsd:element name="zip"    type="xsd:decimal"/>
    </xsd:sequence>
    <xsd:attribute name="country" type="xsd:NMTOKEN"
                   fixed="US"/>
  </xsd:complexType>

  <xsd:complexType name="Items">
    <xsd:sequence>
      <xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="productName" type="xsd:string"/>
            <xsd:element name="quantity">
              <xsd:simpleType>
                <xsd:restriction base="xsd:positiveInteger">
                  <xsd:maxExclusive value="100"/>
                </xsd:restriction>
              </xsd:simpleType>
            </xsd:element>
            <xsd:element name="USPrice"  type="xsd:decimal"/>
            <xsd:element ref="comment"   minOccurs="0"/>
            <xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
          </xsd:sequence>
          <xsd:attribute name="partNum" type="SKU" use="required"/>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>

  <!-- Stock Keeping Unit, a code for identifying products -->
  <xsd:simpleType name="SKU">
    <xsd:restriction base="xsd:string">
      <xsd:pattern value="\d{3}-[A-Z]{2}"/>
    </xsd:restriction>
  </xsd:simpleType>

</xsd:schema>

NWS Current Observations

Visual depiction of Schema (produced by Stylus Studio):
xsd

Documentation for an XML Schema

HTML documentation

Utility to Produce Documentation from Schema

Online Visual Schema for some W3C XML Schemas

Nested or Flat Models (W3C XML Schema)

Russian Nesting Dolls

Trang: Multiformat schema converter based on RELAX NG

Trang can convert between different schemas,and even infer a schema from an XML document.

Trang can take an XML file as an input and infer a schema:

% java -jar trang.jar meerkat.xml meerkat-inferred.rng
% java -jar trang.jar meerkat.xml meerkat-inferred.rnc
% java -jar trang.jar meerkat.xml meerkat-inferred.xsd
% java -jar trang.jar meerkat.xml meerkat-inferred.dtd

And the results:

The Schematron

The Schematron: An XML Structure Validation Language using Patterns in Trees

It is rules-based, using XSLT and XPath expressions.

Examples of Schematron in Action

Web Accessibility Initiative (WAI)

The Web Accessibility Initiative (WAI) develops strategies, guidelines, and resources to help make the Web accessible to people with disabilities.

Web Content Accessibility Guidelines 1.0 has 14 guidelines that are general principles of accessible design. Each guideline has one or more checkpoints that explain how the guideline applies in a specific area.

Testing WAI Checkpoints with Schematron

Using Schematron, we can test for many of the checkpoints within each guideline.

Guideline 1 deals with providing alternatives to auditory or visual content. Some Schematron rules for the these checkpoints could be:

<pattern name="Web Content Accessibility Guidelines 1.0, Guideline 1" 
	 see="http://www.w3.org/TR/WAI-WEBCONTENT/#gl-provide-equivalents" 
	 fpi="+//IDN sinica.edu.tw/SGML Schema for WAI::Guideline 1//EN" 
	 id="g1">
  <rule context="img | IMG">
    <assert test="@alt or @ALT or @longdesc or @LONGDESC">
      (1.1) An image element should have some descriptive text: 
      an alt or longdesc attribute.
    </assert>
    <key name="imgkey" path="@alt" />
  </rule>
  <rule context="input | INPUT">
    <assert test="@alt or @ALT">
      (1.1) An input element should have some descriptive 
      text: an alt or longdesc attribute.
    </assert>
  </rule>
  <rule context="applet | APPLET">
    <assert test="@alt or @ALT">
      (1.1) An applet element should have some descriptive 
      text: an alt or longdesc attribute.
    </assert>
  </rule>
  <rule context="map | MAP">
    <assert test="area/@alt or a or A or AREA/@ALT">
      (1.1) A map element should have some descriptive 
      text: an alt attribute or a link.
    </assert>
    </rule>
    <rule context="object | OBJECT">
      <assert test="string-length(text()) &gt; 0">
	(1.1) An object element should contain 
	some descriptive text.
      </assert>
    </rule>
    <rule context="frame | FRAME">
      <assert test="@longdesc or @LONGDESC">
	(1.1) A frame element should have some descriptive 
	text: a longdesc attribute.
      </assert>
    </rule>
</pattern>

Guideline 2

  <pattern name="Web Content Accessibility Guidelines 1.0, Guideline 2" 
	   see="http://www.w3.org/TR/WAI-WEBCONTENT/#gl-color">
    <rule context="body | BODY">
      <report role="samecolor" 
	      test="string(@bgcolor) = string(@color)">
	(2.2) The background color and the foreground 
	color are the same
      </report>
      <!-- put specific color comparisons here -->
    </rule>
  </pattern>

Guideline 3

  <pattern name="Web Content Accessibility Guidelines 1.0, Guideline 3" 
	   see="http://www.w3.org/TR/WAI-WEBCONTENT/#gl-structure-presentation">
    <rule context="b | I | i | B">
      <report test="self::*">
	(3.3) Concerning element 
	<name />: B and I are not recommended. 
	Use strong and em, or stylesheets.
      </report>
    </rule>
    <rule context="ul | ol | UL | OL">
      <assert test="li or LI">
      (3.3) A list should not be used for formatting effects</assert>
    </rule>
  </pattern>

Guideline 4

  <pattern name="Web Content Accessibility Guidelines 1.0, Guideline 4" 
	   see="http://www.w3.org/TR/WAI-WEBCONTENT/#gl-abbreviated-and-foreign">
    <rule role="topdoc" context="html | HTML | body | BODY">
      <assert test="@lang or @xml:lang or @LANG">
	(4.3) The primary language of a document 
	should be identified.
      </assert>
    </rule>
  </pattern>

Copyright © 2002-2010 David P. Heitmeyer

Bookmark and Share