Monday, May 4, 2015

Publishing Category keyword hierarchy as XML

Tridion Bite : Fetch taxonomies in RDF/OWL-Lite format from cms to improve performance.

Many a times, we need category keywords hierarchy as xml file on presentation server for different purposes. The common way to implement it in templating is to iterate through the categories and keywords structure to produce a xml document and add it as output.

This works great but this doesn't result in a good publishing performance. Especially if the publishing threads are limited and so other items keep waiting for publish in the queue.

So here is an alternative approach, which quite faster as compared to the previous one:
  • Use Repository.GetTaxonomiesOwl() method to get the taxonomies in a repository in RDF/OWL-Lite format.
  • Write a xslt to apply on the taxonomies results and add it as “Embedded Resource” in your templating solution.
  • Transform the xml (RDF/OWL-Lite) using xslt and add the result to the output.


1. Use Repository.GetTaxonomiesOwl() method to get the taxonomies in this Repository in RDF/OWL-Lite format:

We get the results like below:



































from the cms structure:










2. Write xslt  and add it as “Embedded Resource” in your templating solution:

The xslt code:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:owl="http://www.w3.org/2002/07/owl#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:tcmt="http://www.tridion.com/ContentManager/5.2/Taxonomies#" xmlns:tcmc="tcm:0-9-1/Categories#">

  <xsl:template match="/rdf:RDF">
    <publication>
    <xsl:apply-templates select="tcmt:Taxonomy"/>
    </publication>
  </xsl:template>

  <xsl:template match="tcmt:Taxonomy">
    <category>
      <xsl:attribute name="id" >
        <xsl:value-of select="@rdf:about" />
      </xsl:attribute>
      <xsl:for-each select="tcmt:rootKeyword">
        <xsl:call-template name="keyword">
          <xsl:with-param name="tcmId" select="@rdf:resource"/>
         </xsl:call-template>
      </xsl:for-each>
    </category>
  </xsl:template>

  <xsl:template name="keyword">
    <xsl:param name = "tcmId" />
        <keyword>
          <xsl:attribute name="id" >
            <xsl:value-of select="$tcmId" />
          </xsl:attribute>
          <xsl:attribute name="title" >
            <xsl:value-of select="/rdf:RDF/*[@rdf:about = $tcmId]/rdfs:label" />
          </xsl:attribute>
          <xsl:for-each select="/rdf:RDF/*[@rdf:about = $tcmId]/tcmt:childKeyword">
            <xsl:call-template name="keyword">
              <xsl:with-param name="tcmId" select="@rdf:resource"/>
            </xsl:call-template>
          </xsl:for-each>
        </keyword>
  </xsl:template>
</xsl:stylesheet>

Add it as "OwlTaxonomies.xml" to the solution as below:













Make the file a embedded resource by  selecting build action to "Embedded Resource" in file properties:




















3. Transform the xml (RDF/OWL-Lite) using xslt and add the result to the output (The Example Code):


[TcmTemplateTitle("Generate Taxonomy Xml")]
    class GenerateTaxonomyXml : TemplateBase
    {

        /// <summary>
        /// Main function for TBB. Starting point of TBB code.
        /// </summary>
        public override void Transform(Engine engine, Package package)
        {
            // Call the base function to initialize the engine and package objects.
            Initialize(engine, package);
            Publication pub = GetPublication();
            var repo = GetPublication() as Repository;
            TaxonomiesOwlFilter fltr = new TaxonomiesOwlFilter(repo.Session);
            var taxonomies = repo.GetTaxonomiesOwl();

            // Load the style sheet.
            XslCompiledTransform xslTransformer = new XslCompiledTransform();

            //load the Xsl from the assembly
            Stream xslStream = LoadResourceAsStream("MyTest.TemplateBuildingBlocks.Resources.OwlTaxonomies.xslt");
            xslTransformer.Load(XmlReader.Create(xslStream));

            string strXmlListItems = taxonomies.OuterXml;
            StringReader srXml = new StringReader(strXmlListItems);
            XmlReader readerXml = new XmlTextReader(srXml);

            // Execute the transform and output the results to a file.
            StringWriter sw = new StringWriter();
            XmlWriter writer = new XmlTextWriter(sw);
            xslTransformer.Transform(readerXml, writer);

            package.PushItem("Output", package.CreateStringItem(ContentType.Xml, sw.ToString()));
        }

        private Stream LoadResourceAsStream(string resourceName)
        {
            var assembly = Assembly.GetExecutingAssembly();
            return assembly.GetManifestResourceStream(resourceName);
        }

    }

The Output:

<?xml version="1.0" encoding="utf-8"?>
<publication xmlns:owl="http://www.w3.org/2002/07/owl#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:tcmt="http://www.tridion.com/ContentManager/5.2/Taxonomies#" xmlns:tcmc="tcm:0-9-1/Categories#">
  <category id="tcm:9-5039-512">
    <keyword id="tcm:9-5256-1024" title="Level1 - Test Keyword1">
      <keyword id="tcm:9-5257-1024" title="Level2 - Test Keyword1" />
    </keyword>
    <keyword id="tcm:9-5258-1024" title="Level1 - Test Keyword2 - (localized)">
      <keyword id="tcm:9-5259-1024" title="Level2 - Test Keyword2" />
    </keyword>
    <keyword id="tcm:9-5260-1024" title="Local Keyword" />
  </category>
  <category id="tcm:9-5040-512" />
  <category id="tcm:9-5041-512" />
</publication>