Saturday, 4 January 2014

Word Counting in XSLT

A word count is easily determined within either a string or within the text nodes of an xml document.

To count the words within a string supplied in a variable $string:

 <xsl:sequence select="count(tokenize($string, '\W+')[. != ''])"/>

To do the same for the whole XML document use:

<xsl:template match="/">
 <xsl:sequence select="count(//text()/tokenize(., ’\W+’)[.!=’’])"/>
</xsl:template>

To count the words within the text nodes of an xml document and sort them based upon the word frequency, use this:

<xsl:template match="/">
<xsl:for-each-group group-by="." select="for $word in //text()/tokenize(., ’\W+’)[.!=’’] return lower-case($word)">
 <xsl:sort select="count(current-group())" order="descending"/>
 <word word="{current-grouping-key()}"
  frequency="{count(current-group())}"/>
</xsl:for-each-group>
</xsl:template>

Monday, 2 December 2013

Accessing network drives in XSLT

Issue:

I have an xml document that lists several folders:

<folders>
 <folder>foo1</folder>
 <folder>foo2</folder>
 <folder>foo3</folder>
</folders>

and these need to be used to create three result documents on a shared folder on the network using Saxon 9 as the transformation engine.

Resolution:

Using the simple template:

<xsl:template match="folder">
  <xsl:result-document href="{.}/new.xml" method="xml">
  <new>
   <folder/>
  </new>
  </xsl:result-document>
</xsl:template> 

we can specify the absolute path in the href attribute.

Either

Map the shared folder on the network drive to a local drive letter and use the following:

file:///w:/share/{.}/new.xml

Write directly to the shared folder on the drive (note the four forward slashes)

file:////networkPC1234/share/{.}/new.xml

Thursday, 31 October 2013

XSD schema: Sequence of Optional Elements where one Element is allowed in any Position

A requirement recently arose where I needed to have a sequence of optional elements foo1, foo2, foo3, foo4, f005 where an optional element bar was permitted between any one of the foo{n} elements.

The following definition demonstrates a definition for just such a case, where foo2 can have multiple occurrences.

<xsd:element name="container">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:choice minOccurs="0" >
        <xsd:sequence>
          <xsd:element ref="foo1"/>
          <xsd:element ref="bar"  minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence> 
      </xsd:choice>
      <xsd:choice minOccurs="0"  maxOccurs="unbounded">
        <xsd:sequence>
          <xsd:element ref="foo2"/>
          <xsd:element ref="bar"  minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence> 
      </xsd:choice>
      <xsd:choice minOccurs="0" >
        <xsd:sequence>
          <xsd:element ref="foo3"/>
          <xsd:element ref="bar"  minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence> 
      </xsd:choice>
      <xsd:choice minOccurs="0" >
        <xsd:sequence>
          <xsd:element ref="foo4"/>
          <xsd:element ref="bar"  minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence> 
      </xsd:choice>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element> 

Wednesday, 30 October 2013

XSLT - return filename of the input document

On occasion one needs to use the input filename of the document that is being transformed. This can be supplied as a parameter, but it can also be determined within the XSLT by using either the base-uri() or document-uri() functions. Use global variables to return the uri of the document and then using the forward slash to tokenize the variable. The last token will be the filename.



<xsl:variable name="base-uri" select="base-uri(.)"/>
<xsl:variable name="document-uri" select="document-uri(.)"/>
 
<xsl:variable name="filename" select="(tokenize($document-uri,'/'))[last()]"/>
 
<xsl:template match="/">
 <docs>
  <uri><xsl:value-of select="$document-uri"/></uri>
  <uri><xsl:value-of select="$base-uri"/></uri>
  <filename><xsl:value-of select="$filename"/></filename>
  <xsl:apply-templates/>
 </docs>
</xsl:template> 

Thursday, 24 October 2013

XSLT: Quotation marks and apostrophes

XSLT 1 - Single quotes and apostrophes

An intriguing issue came up when trying to remove an apostrophe from a text string using XSLT 1. After a little research I found this method of getting around it:

It was a simple case of transposing the double quotes and single quotes in the element and function

<xsl:value-of select='translate($textstring, "&apos;", "_")'/>

This worked for both Saxon6 and MS transformation engines

XSLT 1 - Double quotes

For double quotes use:

<xsl:value-of select="translate(regex-group(2),'&quot;', '')"/>

XSLT 2 - Single quotes and apostrophes

When using XSLT 2 the same result can be achieved using:

<xsl:value-of select="translate($textstring, '''', '_')"/>

or:

<xsl:value-of select="replace($textstring, '''', '_')"/>

XSLT 2 - Double quotes

A similar mechanism can be used with XSLT for double quotes, either:

<xsl:value-of select='translate($textstring, """", "_")'/>

or:

<xsl:value-of select='replace($textstring, """", "_")'/>

Monday, 21 October 2013

Namespace Identity Transformation

A simple identity transformation for switching namespaces

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="oldNS" select="'http://foo/1.0'"/>
 <xsl:param name="newNS" select="'http://bar/1.0'"/>

 <xsl:template match="/">
  <xsl:apply-templates/>
 </xsl:template> 
 
 <xsl:template match="*">
  <xsl:element name="{name()}" namespace="{$newNS}">
   <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="@*">
  <xsl:choose>
   <xsl:when test="namespace-uri()=$oldNS">
    <xsl:attribute name="{name()}" namespace="{$newNS}">
     <xsl:value-of select="."/>
    </xsl:attribute>
   </xsl:when>
   <xsl:otherwise>
    <xsl:copy-of select="."/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

</xsl:stylesheet>

Thursday, 8 August 2013

Conversion of W3C schema to RelaxNG

There appears to be a scant lack of tools for such conversions. Two I have found are:

 RNG Convert


rngconvert which can be found at: http://java.net/downloads/msv/nightly/ 

This is a java based utility that can convert RELAX Core, RELAX namespace, TREX,
or W3C XML Schema or DTD  to RELAX NG. It is run with the following command line (which automatically detects the schema type):

java -jar rngconv.jar myschema.xsd > result.rng

Or for an XML DTD the -dtd option is needed:

java -jar rngconv.jar -dtd myschema.dtd > result.rng

XSD to RNG Converter


This project was initially hosted by google code (http://code.google.com/p/xsdtorngconverter/) but is now on github - https://github.com/epiasini/XSDtoRNG

This is a straight forward common or garden XSLT v1.0

I have not tried either of these tools as yet but will add to this posting as and when I do.