xsl-list
[Top] [All Lists]

pretty-printing XML into HTML

2003-12-17 13:49:37
(Apologies if you got this twice. I didn't see any responses
to this message since I sent it yesterday so I wanted to make sure it
got out.)

Hi all,

Yes, I know pretty-printing XML is an FAQ. I've looked at
http://www.dpawson.co.uk/xsl/sect2/pretty.html,
but this all seems to refer to copying an XML tree to XML
with indentation.

My requirement is this.
I have an XML source document that includes certain fragments
(subtrees) that are generated XML, each to be written to a file on the
file system. Each fragment is contained in a <source:fragment> element,
for processing by Cocoon's SourceWritingTransformer.
This all works fine.

However, we want to have an alternative view of this XML source document,
which lets the user view the collection of generated files in
one HTML browser view. E.g. this view (generated by a stylesheet)
puts the name of each output filename in an <h2> element,
then "serializes" (i.e. pretty-prints) the corresponding XML fragment
into HTML. E.g. the data
 <source:write>
  <source:source>context:/mount/gem/enterprise/index.xsp</source:source>
  <source:fragment>
   <xsp:page>
    <index-page>
     <page-set role="focal objects">
      <page-ref name="select_Ethnologue_Country" label="Ethnologue Country" />
      ...
     </page-set>
    </index-page>
   </xsp:page>
  </source:fragment>
 </source:write>

is transformed to:

<h2>enterprise/index.xsp</h2>

<pre>  & lt;xsp:page& gt;
    & lt;index-page& gt;
      & lt;page-set role="focal objects"& gt;
        & lt;page-ref name="select_Ethnologue_Country" label="Ethnologue 
Country" /& gt;
        ...
      & lt;/page-set& gt;
    & lt;/index-page& gt;
  & lt;/xsp:page& gt;
</pre>

(I put spaces after ampersands above, hoping this would avoid
mailer munging.)
(Notice I haven't yet handled namespace declarations, e.g. for xsp:.)

First of all, am I doing something fundamentally wrong here design-
wise? Does it not make sense to have XML data that you'd want
to both treat as XML data, and make visible to a browser user?
Am I wrongly mixing metaphors of text and markup?
If I should be taking a different approach, advice would be
appreciated!

Assuming that I'm on the right track... this pretty-printing XML to
HTML is a fair bit of work (especially getting the namespace
declarations in optimal places). I know Tidy is supposed to do
a good job at this, but don't think you can integrate Tidy
into the operation of a stylesheet, i.e. process subtrees of your
source document with Tidy and graft the results into your result
tree. (If you can do this in Cocoon, without reducing portability,
I'd be interested to hear about it.)

So... if this is a "normal" thing to do, hopefully somebody has
already written a pretty good XML-to-HTML pretty-printer in XSL?
It seemed to me there was an extension function in Saxon or in
XSLT 2.0 that would serialize a source tree fragment, hopefully
with indentation, but I can't find a reference to it right now.
Any ideas?

Below is the stylesheet I import for writing out the XML as
HTML (template with mode="escape-xml").

Thanks,
Lars

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <xsl:output method="html" indent="yes" />

  <!-- escape-xml mode: serialize XML tree to text, with indent
    Based on templates by Wendell Piez -->

  <xsl:variable name="nl"><xsl:text>&#10;</xsl:text></xsl:variable>
  <xsl:variable name="indent-increment" select="'  '" />

  <xsl:template name="write-starttag">
    <xsl:text>&lt;</xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:for-each select="@*">
     <xsl:call-template name="write-attribute"/>
    </xsl:for-each>
    <xsl:if test="not(*|text()|comment()|processing-instruction())"> /</xsl:if>
    <xsl:text>></xsl:text>
  </xsl:template>

  <xsl:template name="write-endtag">
     <xsl:text>&lt;/</xsl:text>
     <xsl:value-of select="name()"/>
     <xsl:text>></xsl:text>
  </xsl:template>

  <xsl:template name="write-attribute">
     <xsl:text> </xsl:text>
     <xsl:value-of select="name()"/>
     <xsl:text>="</xsl:text>
     <xsl:value-of select="."/>
     <xsl:text>"</xsl:text>
  </xsl:template>

  <xsl:template match="*" mode="escape-xml">
    <xsl:param name="indent-string" select="$indent-increment" />
    <xsl:value-of select="$indent-string" />
    <xsl:call-template name="write-starttag" />
    <xsl:if test="*"><xsl:value-of select="$nl" /></xsl:if>
    <xsl:apply-templates mode="escape-xml">
      <xsl:with-param name="indent-string" select="concat($indent-string, 
$indent-increment)" />
    </xsl:apply-templates>
    <xsl:if test="*"><xsl:value-of select="$indent-string" /></xsl:if>
     <xsl:if 
test="*|text()|comment()|processing-instruction()"><xsl:call-template
name="write-endtag" /></xsl:if>
    <xsl:value-of select="$nl" />
  </xsl:template>

</xsl:stylesheet>


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list