Dan Rigsby – Coding Up Style

Developer.Speaker.Blogger

Archive for the 'xslt' Category

Preserving line breaks in xml while transforming to html with xslt

Posted by Dan Rigsby on 3rd January 2008

When using xslt to transform xml into html, sometimes it may be desired to keep the line breaks in the text of the xml and convert them into <br/> tags.  For instance, we pipe the output of build and database scripts to xml.  When rendering into html, we want to keep the line breaks or the text looks garbled.  There are many other situations where you might want to preserve line breaks, but bare with me.

Below I have some text copied from an execution of msbuild.  This text is stored directly into a <text> node in the xml:

<text>Microsoft (R) Build Engine Version 3.5.21022.8
[Microsoft .NET Framework, Version 2.0.50727.1433]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 1/2/2008 6:34:50 PM.
Done Building Project "D:\builds\ININ.BCF.sln" (default targets).

Build succeeded.
    0 Warning(s)
    0 Error(s)</text>

A simple xslt to convert this into html might look like this:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://www.w3.org/TR/xhtml1/strict">
    <xsl:output method="html"/>

    <xsl:template match="/">
        <xsl:value-of select="/text"/>
    </xsl:template>
</xsl:stylesheet>

The problem with the xslt is that the line breaks aren’t preserved and as a result, the text looks garbled:

Microsoft (R) Build Engine Version 3.5.21022.8 [Microsoft .NET Framework, Version 2.0.50727.1433] Copyright (C) Microsoft Corporation 2007. All rights reserved. Build started 1/2/2008 6:34:50 PM. Done Building Project "D:\builds\ININ.BCF.sln" (default targets). Build succeeded. 0 Warning(s) 0 Error(s)

Here is the same xslt with the inclusion of a PreserveLineBreaks template:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://www.w3.org/TR/xhtml1/strict">
    <xsl:output method="html"/>

    <xsl:template match="/">
        <xsl:call-template name="PreserveLineBreaks">
            <xsl:with-param name="text" select="/text"/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="PreserveLineBreaks">
        <xsl:param name="text"/>
        <xsl:choose>
            <xsl:when test="contains($text,'&#xA;')">
                <xsl:value-of select="substring-before($text,'&#xA;')"/>
                <br/>
                <xsl:call-template name="PreserveLineBreaks">
                    <xsl:with-param name="text">
                        <xsl:value-of select="substring-after($text,'&#xA;')"/>
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$text"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

The template preserves line breaks by writing out the text before the line break, adding a <br/> tag, then recursively calling the template again to look for addition line breaks in the remaining text.  This process continues until every line break is essentially replaced with a <br/>.

Microsoft (R) Build Engine Version 3.5.21022.8

[Microsoft .NET Framework, Version 2.0.50727.1433]

Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 1/2/2008 6:34:50 PM.

Done Building Project "D:\builds\ININ.BCF.sln" (default targets).

Build succeeded.

0 Warning(s)

0 Error(s)

This template could even be modified to replace other characters such as tabs depending on your needs.

Posted in xslt | 11 Comments »

XsltTransformer: Transforming an xml string with an xslt string

Posted by Dan Rigsby on 21st December 2007

There are many times when I have xml and xslt as strings and need the transformed data as a string.  I put together a utility class to take care of this.

/// <summary>
/// Transforms the specified xml string using the specified xslt string.
/// </summary>
/// <param name="xml">The xml string to transform.</param>
/// <param name="xslt">The xslt string to transform with.</param>
/// <returns>The transformed xml string.</returns>
public static string Transform(
    string xml,
    string xslt
    )
{
    string output = String.Empty;
    StringWriter stringWriter = null;
    XmlTextWriter xmlWriter = null;
    XmlDocument inputDocument = new XmlDocument();
    XmlDocument xsltDocument = new XmlDocument();
    System.Xml.Xsl.XslCompiledTransform xslCompiledTransformer = null;

    try
    {
        // Create Xml Document
        inputDocument.LoadXml(xml);

        // Create XsltDocument
        xsltDocument.LoadXml(xslt);

        stringWriter = new StringWriter();
        xmlWriter = new XmlTextWriter(stringWriter);

        // Apply the generated text stylesheet. 
    #if DEBUG
        xslCompiledTransformer =
            new System.Xml.Xsl.XslCompiledTransform(true);
    #else
        xslCompiledTransformer =
            new System.Xml.Xsl.XslCompiledTransform(false);
    #endif
        xslCompiledTransformer.Load(xsltDocument);
        xslCompiledTransformer.Transform(inputDocument, xmlWriter);

        // Generate the output Xml
        output = stringWriter.GetStringBuilder().ToString();
    }
    finally
    {
        if (xmlWriter != null)
        {
            xmlWriter.Close();
        }

        if (stringWriter != null)
        {
            stringWriter.Dispose();
        }

        if (xslCompiledTransformer != null)
        {
            xslCompiledTransformer.TemporaryFiles.Delete();
        }
    }

    return output;
}

 

A couple of things of note:

  1. When I first started using this, I had this running in memory and it would transform over 100 times an hour.  We had a variety of stylesheets, and the xml was almost always different.  If we didn’t make a call to TemporaryFiles.Delete(), then these temporary files would be kept and overtime this resulted in a lot of useless files.  This seems like a bug to me that this files aren’t cleaned up by default, but I am sure there is some clever reason for this.  It may depend on how you are implementing it.  I do like having the flexibility of deciding whether or not to keep them though.  You can read more about these options on the MSDN page for XsltCompiledTransform.
  2. I had thought of making the XsltCompiledTransform a static variable, but depending on how this method is used, I didn’t want to always have this loaded in memory.  If you make use of this code, you may decide to do this.

Posted in .Net, xslt | No Comments »