Re: [xsl] is there a way to output XML with invalid qnames?

Subject: Re: [xsl] is there a way to output XML with invalid qnames?
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Date: Fri, 06 Nov 2009 21:07:57 -0500
At 2009-11-06 16:57 -0800, Jessica Britton wrote:
I'm trying to format XML files so that I can display them as examples in a wiki. I'd like the element names in the examples to link to other wiki pages, so I'm trying to figure out the best way to take source like this:

<ns:elementName1>
    <ns:elementName2>value</ns:elementName2>
</ns:elementName1>


And get output like this:


<[ns:elementName1|page 1]>
    <[ns:elementName2|page 2]>value</ns:elementName2>
</ns:elementName1>

This was an interesting challenge! Especially coming up with monotonically increasing page number references for unique element names across the instance.


Of course, "[ns:elementName|page]" isn't a valid qname so just trying to rename the elements in an XML to XML transform doesn't work.
...
Before I give myself a headache trying to figure it out, I was wondering if anyone could suggest a better approach I should look into.

I think the example below suits your needs, though I'm making guesses. As a more extensive test I ran the stylesheet through itself in order to have a larger test. This isn't a very rigourous test, and I haven't checked nearly all the edge cases but it should do a lot for you as it stands and may meet your needs.


I hope this helps.

. . . . . . . . . . . Ken


T:\ftemp>type jessica.xml <ns:elementName1 xmlns:ns="urn:X-Jessica"> <ns:elementName2>value</ns:elementName2> </ns:elementName1>

T:\ftemp>call xslt jessica.xml xml2wiki.xsl
<[ns:elementName1|page 1] xmlns:ns="urn:X-Jessica">
    <[ns:elementName2|page 2]>value</ns:elementName2>
</ns:elementName1>
T:\ftemp>type xml2wiki.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                version="1.0">

<xsl:output method="text"/>

<xsl:key name="names" match="*" use="name(.)"/>

<xsl:template match="*">
  <!--reconstitute the element start tag-->
  <xsl:text>&lt;[</xsl:text>
  <xsl:value-of select="name(.)"/>|page <xsl:text/>
  <!--find the first in document order-->
  <xsl:variable name="first"
                select="key('names',name(.))[1]"/>
  <!--determine its ordinal based on the uniqueness of element names-->
  <xsl:value-of select="count(
                        ($first/preceding::* | $first/ancestor::*)
                        [generate-id(.)=
                         generate-id(key('names',name(.))[1])]) + 1"/>
  <xsl:text>]</xsl:text>
  <!--reconstitute the namespaces-->
  <xsl:for-each select="namespace::*[not(.=../../namespace::*) and
                                     name(.)!='xml']">
    <xsl:value-of select="concat(' ','xmlns')"/>
    <xsl:if test="name(.)">:<xsl:value-of select="name(.)"/></xsl:if>
    <xsl:value-of select="concat('=&#x22;',.,'&#x22;')"/>
  </xsl:for-each>
  <!--reconstitute the attributes-->
  <xsl:for-each select="@*">
    <xsl:value-of select="concat(' ',name(.),'=&#x22;')"/>
    <xsl:call-template name="do-text"/>
    <xsl:text>"</xsl:text>
  </xsl:for-each>
  <xsl:choose>
    <xsl:when test="node()">
      <!--reconstitute the content-->
      <xsl:text>&gt;</xsl:text>
      <xsl:apply-templates select="node()"/>
      <xsl:text>&lt;/</xsl:text>
      <xsl:value-of select="name(.)"/>
      <xsl:text>&gt;</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <!--empty element-->
      <xsl:text>/&gt;</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="comment()">
  <xsl:text>&lt;!--</xsl:text>
  <xsl:value-of select="."/>
  <xsl:text>--&gt;</xsl:text>
</xsl:template>

<xsl:template match="processing-instruction()">
  <xsl:text>&lt;?</xsl:text>
  <xsl:value-of select="concat(name(.),' ',.)"/>
  <xsl:text>?&gt;</xsl:text>
</xsl:template>

<xsl:template match="text()" name="do-text">
  <xsl:param name="rest" select="."/>
  <xsl:choose>
    <xsl:when test="contains($rest,'&lt;')">
      <xsl:value-of select="substring-before($rest,'&lt;')"/>
      <xsl:text>&amp;lt;</xsl:text>
      <xsl:call-template name="do-text">
        <xsl:with-param name="rest" select="substring-after($rest,'&lt;')"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$rest"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

T:\ftemp>call xslt xml2wiki.xsl xml2wiki.xsl
<[xsl:stylesheet|page 1] xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="1.0">


<[xsl:output|page 2] method="text"/>

<[xsl:key|page 3] name="names" match="*" use="name(.)"/>

<[xsl:template|page 4] match="*">
<!--reconstitute the element start tag-->
<[xsl:text|page 5]>&lt;[</xsl:text>
<[xsl:value-of|page 6] select="name(.)"/>|page <[xsl:text|page 5]/>
<!--find the first in document order-->
<[xsl:variable|page 7] name="first" select="key('names',name(.))[1]"/>
<!--determine its ordinal based on the uniqueness of element names-->
<[xsl:value-of|page 6] select="count( ($first/preceding::* | $first/ancestor::*) [generate-id(.)= generate-id(key('names',name(.))[1])]) + 1"/>
<[xsl:text|page 5]>]</xsl:text>
<!--reconstitute the namespaces-->
<[xsl:for-each|page 8] select="namespace::*[not(.=../../namespace::*) and name(.)!='xml']">
<[xsl:value-of|page 6] select="concat(' ','xmlns')"/>
<[xsl:if|page 9] test="name(.)">:<[xsl:value-of|page 6] select="name(.)"/></xsl:if>
<[xsl:value-of|page 6] select="concat('="',.,'"')"/>
</xsl:for-each>
<!--reconstitute the attributes-->
<[xsl:for-each|page 8] select="@*">
<[xsl:value-of|page 6] select="concat(' ',name(.),'="')"/>
<[xsl:call-template|page 10] name="do-text"/>
<[xsl:text|page 5]>"</xsl:text>
</xsl:for-each>
<[xsl:choose|page 11]>
<[xsl:when|page 12] test="node()">
<!--reconstitute the content-->
<[xsl:text|page 5]>></xsl:text>
<[xsl:apply-templates|page 13] select="node()"/>
<[xsl:text|page 5]>&lt;/</xsl:text>
<[xsl:value-of|page 6] select="name(.)"/>
<[xsl:text|page 5]>></xsl:text>
</xsl:when>
<[xsl:otherwise|page 14]>
<!--empty element-->
<[xsl:text|page 5]>/></xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


<[xsl:template|page 4] match="comment()">
  <[xsl:text|page 5]>&lt;!--</xsl:text>
  <[xsl:value-of|page 6] select="."/>
  <[xsl:text|page 5]>--></xsl:text>
</xsl:template>

<[xsl:template|page 4] match="processing-instruction()">
  <[xsl:text|page 5]>&lt;?</xsl:text>
  <[xsl:value-of|page 6] select="concat(name(.),' ',.)"/>
  <[xsl:text|page 5]>?></xsl:text>
</xsl:template>

<[xsl:template|page 4] match="text()" name="do-text">
<[xsl:param|page 15] name="rest" select="."/>
<[xsl:choose|page 11]>
<[xsl:when|page 12] test="contains($rest,'&lt;')">
<[xsl:value-of|page 6] select="substring-before($rest,'&lt;')"/>
<[xsl:text|page 5]>&lt;</xsl:text>
<[xsl:call-template|page 10] name="do-text">
<[xsl:with-param|page 16] name="rest" select="substring-after($rest,'&lt;')"/>
</xsl:call-template>
</xsl:when>
<[xsl:otherwise|page 14]>
<[xsl:value-of|page 6] select="$rest"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


</xsl:stylesheet>
T:\ftemp>rem



--
Upcoming:  hands-on XSLT, XQuery and XSL-FO Washington DC Nov 2009
Interested in other classes?  http://www.CraneSoftwrights.com/s/i/
Crane Softwrights Ltd.          http://www.CraneSoftwrights.com/s/
Training tools: Comprehensive interactive XSLT/XPath 1.0/2.0 video
Video lesson:    http://www.youtube.com/watch?v=PrNjJCh7Ppg&fmt=18
Video overview:  http://www.youtube.com/watch?v=VTiodiij6gE&fmt=18
G. Ken Holman                 mailto:gkholman@xxxxxxxxxxxxxxxxxxxx
Male Cancer Awareness Nov'07  http://www.CraneSoftwrights.com/s/bc
Legal business disclaimers:  http://www.CraneSoftwrights.com/legal

Current Thread