Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Sep 15, 2010 @ 16:48
    Sebastiaan Janssen
    0

    Creating a RSS feed with CDATA element

    So I'm creating an RSS feed and I need some elements to be enclosed in CDATA tag, description and content:encoded:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xsl:stylesheet [
      <!ENTITY nbsp "&#x00A0;">
    ]>
    <xsl:stylesheet
        version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:msxml="urn:schemas-microsoft-com:xslt" xmlns:content="http://purl.org/rss/1.0/modules/content/"
        xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:Ext.PageCount="urn:Ext.PageCount" xmlns:Ext.Tags="urn:Ext.Tags" xmlns:Ext.Strings="urn:Ext.Strings" xmlns:umbraco.contour="urn:umbraco.contour"
        exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets Ext.PageCount Ext.Tags Ext.Strings umbraco.contour content">
    
    
      <xsl:output method="xml" omit-xml-declaration="yes" cdata-section-elements="description content:encoded"/>
    
      <xsl:param name="currentPage"/>
      <xsl:variable name="urlPrefix">
        <xsl:text>http://</xsl:text>;
        <xsl:value-of select="umbraco.library:RequestServerVariables('HTTP_HOST')" />
        <xsl:text>/</xsl:text>
      </xsl:variable>
    
      <xsl:variable name="teaserLength" select="300" />
    
      <xsl:variable name="currentSiteRoot" select="$currentPage/ancestor-or-self::node [@level=1]" />
    
      <xsl:template match="/">
        <!-- change the mimetype for the current page to xml -->
        <xsl:value-of select="umbraco.library:ChangeContentType('text/xml')"/>
        <xsl:text disable-output-escaping="yes"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>]]></xsl:text>
        <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
          <channel>
            <title>
              Test
            </title>
            <link>
              http://www.domain.com
            </link>
            <pubDate>
              Sat, 17 Jul 2010 14:51:35 GMT
            </pubDate>
            <generator>umbraco</generator>
            <language>nl</language>
            <description>
              Test
            </description>
    
            <atom:link rel="self" type="application/rss+xml" href="{$urlPrefix}" />
    
            <item>
              <description>
                <xsl:for-each select="$currentSiteRoot//node [@nodeTypeAlias = 'Newsletter']">
                  <xsl:sort select="./@createDate"  order="descending" />
                  <xsl:if test="position() = 1">
                    <xsl:call-template name="newsletterContent">
                      <xsl:with-param name="currentNode" select="." />
                    </xsl:call-template>
                  </xsl:if>
                </xsl:for-each>
              </description>
              <content:encoded>
                <xsl:for-each select="$currentSiteRoot//node [@nodeTypeAlias = 'Newsletter']">
                  <xsl:sort select="./@createDate"  order="descending" />
                  <xsl:if test="position() = 1">
                    <xsl:call-template name="newsletterContent">
                      <xsl:with-param name="currentNode" select="." />
                    </xsl:call-template>
                  </xsl:if>
                </xsl:for-each>
              </content:encoded>
            </item>
          </channel>
        </rss>
    
      </xsl:template>

    As you can see, I've changed the xsl:output to make description and content:encoded into cdata section elements, but the funny thing is: only the FIRST <description> outputs properly, and that's not the one I wanted:

    <description><![CDATA[
    
              Test
    
            ]]></description>

    What am I doing wrong?

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Sep 15, 2010 @ 21:13
    Chriztian Steinmeier
    1

    Hi Sebastiaan,

    A CDATA section can only contain text, so the processor silently ignores your attempt to generate elements inside an element flagged for CDATA output. 

    You need to generate text-versions of the elements inside - take a look at this stylesheet - try to run it on any XML input document (even itself) to see the result:

     <?xml version="1.0" encoding="utf-8" ?>
    <xsl:stylesheet
           
    version="1.0"
           
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
           
    xmlns:mydata="#not-used-at-all"
    >
           
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"
                   
    cdata-section-elements="mydata:free description"
           
    />

           
    <xsl:template match="/">
                   
    <output>
                           
    <description>
                                    This is the first description element
                           
    </description>
                           
                           
    <mydata:free>
                                   
    <p>
                                            This is not being output as a CDATA Section
                                   
    </p>
                           
    </mydata:free>
                           
                           
    <mydata:free>
                                    &lt;p&gt;
                                            This should actually come out as CDATA
                                    &lt;/p&gt;
                           
    </mydata:free>
                           
                           
    <mydata:free><![CDATA[
                                   
    <p>
                                            This should actually come out as CDATA being no different from the above
                                   
    </p>
                            ]]>
    </mydata:free>
                   
    </output>
                   
           
    </xsl:template>

    </xsl:stylesheet>

    Now, I don't know what your newsletterContent template looks like, but let's say I had a chunk of XML I needed to create as text for CDATA output, I'd create a template for it and set the mode attribute to 'text' or similar, and then do like this (include 'code' in the cdata-section-elements attribute):

    <code>
            <xsl:apply-templates select="*" mode="text" />
    </code>
    ...

    <!-- Template for text-mode output of elements -->
    <xsl:template match="*" mode="text">
           
    <xsl:value-of select="concat('&lt;', name(), '&gt;')" />
                   
    <xsl:apply-templates select="*" mode="text" />
           
    <xsl:value-of select="concat('&lt;/', name(), '&gt;')" />
    </xsl:template>
    - of course, I'd be glad to help you with the newsletterContent template if you need it, just let me know.

    /Chriztian

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Sep 16, 2010 @ 09:57
    Sebastiaan Janssen
    0

    Thanks for that, I am probably doing it wrong but the newsletterContent template looks a bit like this now:

    <xsl:template match="*" mode="text" name="newsletterContent">
     <table>
      <tr>
       <td>Some data</td>
      </tr>
     </table>
    </xsl:template>

    It also calls a few other templates, do I need to put them into text mode as well?

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Sep 16, 2010 @ 09:58
    Sebastiaan Janssen
    0

    Ps. I'm still having a really hard time understanding how apply-template works as opposed to simply calling a template which is almost like calling a function.

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Sep 16, 2010 @ 15:15
    Sebastiaan Janssen
    0

    Ah, well, something happened when I changed it into this:

    <xsl:template match="*" mode="text" name="newsletterContent">
    <![CDATA[
     <table>
      <tr>
       <td>Some data</td>
      </tr>
     </table>
    ]]>
    </xsl:template>

    But, of course, <xsl:value-of select="$bla" /> in there will also be turned into plain text, so that doesn't help either..

    So. Back to the hack, you mentioned something about disable-output-escaping... where and how would I use it?

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Sep 17, 2010 @ 09:11
    Chriztian Steinmeier
    0

    Hi Sebastiaan,

    You can always break out of a CDATA Section if you need to (kinda like what you'd do in the good old days with ASP or PHP):

    <xsl:template name="newsletterContent" mode="text">
    <![CDATA[
    <table>
        <tr>
            <td>
    ]]><xsl:value-of select="$blah" /><![CDATA[
            </td>
        </tr>
    </table>
    ]]>
    </xsl:template>

    - but I'd only do that if I had like two or three things to output - it's a mess in XSLT just as it is in ASP/PHP...  

    Using the match="*" attribute only makes sense if you're otherwise using match templates and apply-templates, which I get the vibe you're not, so you should probably use the approach above (unless you really want to start pulling things apart). It really depends on the complexity of your newsletterContent template. 

    /Chriztian

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Sep 17, 2010 @ 10:32
    Sebastiaan Janssen
    2

    Thanks Criztian, was afraid of that. For now I got another tip to simply solve it: Use a template and only get the description content through an XSLT macro. Then you can enclose the whole description element in a CDATA block. This won't work for everybody, but it's fine for my purposes.

    <%@ Master Language="C#" MasterPageFile="/umbraco/masterpages/default.master" AutoEventWireup="true" %>
    
    <asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server"><?xml version="1.0" encoding="UTF-8"?>
        <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
          <channel>
            <title>Test</title>
            <link>http://www.domain.com</link>;
            <pubDate>Sat, 17 Jul 2010 14:51:35 GMT</pubDate>
            <generator>umbraco</generator>
            <language>nl</language>
            <description>Test</description>
    
            <atom:link rel="self" type="application/rss+xml" href="http://www.domain.com" />
    
            <item>
              <title>Title</title>
              <author>Me</author>
              <link>http://www.domain.com</link>;
              <pubDate>Wed, 07 Apr 2010 17:43:39 GMT</pubDate>
              <guid>http://www.domain.com</guid>;
              <description>
            <![CDATA[
                  <umbraco:Macro Alias="MailingRss" runat="server"></umbraco:Macro>
            ]]>
              </description>
            </item>
          </channel>
        </rss>
    </asp:Content>
  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 8x admin c-trib
    Sep 17, 2010 @ 12:13
    Chriztian Steinmeier
    0

     

    That's actually a neat solution - I often forget to think outside of the box because I develop 95% of my XSLT locally without server-side processing, so I wouldn't have thought of this.

    It's great because you can (probably) leave the original XSLT that generates the content untouched.

    /Chriztian 

Please Sign in or register to post replies

Write your reply to:

Draft