{"id":40,"date":"2012-12-14T14:12:00","date_gmt":"2012-12-14T14:12:00","guid":{"rendered":"\/lisa\/post\/2012\/12\/14\/Back-in-the-XSLT-saddle-again.aspx"},"modified":"2012-12-14T14:12:00","modified_gmt":"2012-12-14T14:12:00","slug":"back-in-the-xslt-saddle-again","status":"publish","type":"post","link":"https:\/\/spacefold.com\/lisa\/2012\/12\/14\/back-in-the-xslt-saddle-again\/","title":{"rendered":"Back in the XSLT saddle again"},"content":{"rendered":"<p>In my new job, I won&#8217;t be playing very much with SSRS, so the focus of this blog will change.&nbsp;<\/p>\n<p>We do use SQL Server on the back-end, so there&#8217;s almost always some&nbsp;T-SQL work in the mix &#8212;&nbsp;that&#8217;s always fun for me &#8212; but we don&#8217;t use Reporting Services.&nbsp; I will miss SSRS being part of my day job.&nbsp; (I can still&nbsp;use the blog to answer SSRS questions from readers, and I don&#8217;t mind researching them on my own time. That&#8217;s what I was doing before, anyway.)&nbsp;<\/p>\n<p>I&#8217;m spending a lot more time on enterprise integration now, which sometimes means quite a lot of SSIS, sometimes not.&nbsp;<\/p>\n<p>But &#8212; and this is really fun for me &#8212; I&#8217;m back to using XSLT&nbsp;deeply and constantly again &#8212; &nbsp;not just occasionally, or because I happen to like using it.&nbsp;&nbsp;XSLT is&nbsp;part and parcel of how we do things in&nbsp;our products.&nbsp;Eminently sensible, IMHO.<\/p>\n<p>I thought I&#8217;d give you a little example from my current work, combining XSLT and SQL.<\/p>\n<h2>&nbsp;<\/h2>\n<h2>SQL-Delimited Strings&nbsp;from XSLT<\/h2>\n<p>&nbsp;<\/p>\n<p>Recently I was working on a piece of XSLT that generates SQL statements from a lot of XML instructions that specified the values to be inserted.&nbsp;<\/p>\n<p>Previous iterations of this code included many INSERT statements but all the VALUES to be inserted were types like numeric or bit.&nbsp; In&nbsp;my revision of&nbsp;this code, I wanted to add some string values.&nbsp;<\/p>\n<p>These&nbsp;strings are perfectly fine in the XML document, but they&nbsp;would need to be surrounded by string delimiters to be listed as VALUES in the INSERT statement.<\/p>\n<p>The problem here, as you realize, is that the string values themselves may include the delimiter character, so these delimiter characters need to be doubled within the string.&nbsp;<\/p>\n<p>For example, if you want to insert the value <span style=\"font-size: small;\"><strong>this isn&#8217;t a test<\/strong><\/span>, you need to&nbsp;delimit this string as<span style=\"color: #ff0000; font-size: small;\"> <strong>&#8216;this isn&#8221;t a test&#8217;<\/strong>&nbsp;<\/span>to get it into the SQL statement properly.<\/p>\n<p>Right?<\/p>\n<p>&nbsp;<\/p>\n<h2>So what&#8217;s the problem?<\/h2>\n<p>&nbsp;<\/p>\n<p>XSLT 1.0 doesn&#8217;t have a &#8220;replace&#8221; function, like SQL or .NET languages, or lots of others.&nbsp; You can&#8217;t just say replace($theString, $someString, $someOtherString) and have it done for you all at once.&nbsp; There&#8217;s a &#8220;translate&#8221; function, but it is strictly a character-for-character replacement.&nbsp; You can&#8217;t use &#8220;translate&#8221; to replace one character by two characters.<\/p>\n<p>While XSLT 2 does have this handy capability, many people who are using MS processors don&#8217;t have XSLT 2 available to them.&nbsp; (Don&#8217;t get me started on what a short-sighted attitude MS has with respect to XSLT&#8230;)<\/p>\n<p>String replacing is not&nbsp;actually difficult to do, even in XSLT 1.0, if you remember to use recursion. I have a feeling that Michael Kay and the others involved in creating the XSLT standard&nbsp;thought this was so obvious that they&nbsp;didn&#8217;t sweat the small stuff&nbsp;like &#8220;replace&#8221; functionality in the original XSLT standard.&nbsp;<\/p>\n<p>Unfortunately,&nbsp;it turned out that many people found the idea of recursion counter-intuitive, and they whined quite a lot about it.&nbsp;<\/p>\n<p class=\"NB\">IMHO, they were a lot more justified in whining about grouping, but that&#8217;s water under the bridge. (Don&#8217;t get me started on Muenchian grouping, either.)<\/p>\n<p>Since recursion is an extremely&nbsp;useful XSLT technique to have under your belt, in any version, and since this bit of code is easily re-used in other situations where you have to massage a string, and&nbsp;since you may even find yourself building SQL in XSLT some day&#8230; and since this is also a fairly classic use of XSLT&#8217;s string-parsing functions&#8230; I thought I&#8217;d give it to you.&nbsp;<\/p>\n<h2>&nbsp;<\/h2>\n<h2>Ready?<\/h2>\n<p>&nbsp;<\/p>\n<p>The &nbsp;simple templates that do the heavy lifting assume you have these variables declared:<\/p>\n<p class=\"code\"><span style=\"color: #0000ff;\">&lt;xsl:variable<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">Apos<\/span><span style=\"color: #0000ff;\">&#8220;&gt;<\/span><span style=\"color: black; font-weight: bold;\">&#8216;<\/span><span style=\"color: #0000ff;\">&lt;\/xsl:variable&gt;<br \/><\/span><span style=\"color: #0000ff;\">&lt;xsl:variable<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">SqlApos<\/span><span style=\"color: #0000ff;\">&#8220;<\/span><span style=\"color: #be3232;\"> select<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">concat($Apos,$Apos)<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<\/span><\/p>\n<p>&#8230; and you call them like this (assume this snippet is going to fill a variable that will form part of a SQL statement containing a string, so it needs to have delimiters added to it):<\/p>\n<p class=\"code\"><span style=\"color: #0000ff;\">&lt;xsl:call-template<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">DelimitSqlString<\/span><span style=\"color: #0000ff;\">&#8220;&gt;<br \/>&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:with-param<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">theString<\/span><span style=\"color: #0000ff;\">&#8220;<\/span><span style=\"color: #be3232;\"> <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; select<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">[your XPath or variable goes here]<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/><\/span><span style=\"color: #0000ff;\">&lt;\/xsl:call-template&gt;<\/span><\/p>\n<p>&#8230; and here they are:<\/p>\n<p class=\"code\"><span style=\"color: #0000ff;\">&lt;xsl:template<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">DelimitSqlString<\/span><span style=\"color: #0000ff;\">&#8220;&gt;<br \/>&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:param<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">theString<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/>&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:variable<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">resultString<\/span><span style=\"color: #0000ff;\">&#8220;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:choose&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:when<\/span><span style=\"color: #be3232;\"> test<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">contains($theString,$Apos)<\/span><span style=\"color: #0000ff;\">&#8220;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:call-template<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">FixSqlString<\/span><span style=\"color: #0000ff;\">&#8220;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:with-param<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">currString<\/span><span style=\"color: #0000ff;\">&#8220;<\/span><span style=\"color: #be3232;\">&nbsp;select<\/span><span style=\"color: #0000ff;\">=<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><span style=\"color: black; font-weight: bold;\">concat(substring-before($theString,$Apos),$SqlApos)<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:with-param<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">restString<\/span><span style=\"color: #0000ff;\">&#8221;&nbsp;<\/span><span style=\"color: #be3232;\">select<\/span><span style=\"color: #0000ff;\">=<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><span style=\"color: black; font-weight: bold;\">substring-after($theString,$Apos)<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;\/xsl:call-template&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;\/xsl:when&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:otherwise&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:value-of<\/span><span style=\"color: #be3232;\"> select<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">$theString<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;\/xsl:otherwise&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;\/xsl:choose&gt;<br \/>&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;\/xsl:variable&gt;<br \/>&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:value-of<\/span><span style=\"color: #be3232;\"> select<\/span><span style=\"color: #0000ff;\">=<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><span style=\"color: black; font-weight: bold;\">concat($Apos, normalize-space($resultString) ,$Apos)<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/><\/span><span style=\"color: #0000ff;\">&lt;\/xsl:template&gt;<\/p>\n<p><\/span><span style=\"color: #0000ff;\">&lt;xsl:template<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">FixSqlString<\/span><span style=\"color: #0000ff;\">&#8220;&gt;<br \/>&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:param<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">currString<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/>&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:param<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">restString<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/>&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:choose&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:when<\/span><span style=\"color: #be3232;\"> test<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">contains($restString,$Apos)<\/span><span style=\"color: #0000ff;\">&#8220;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:call-template<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">FixSqlString<\/span><span style=\"color: #0000ff;\">&#8220;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:with-param<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">currString<\/span><span style=\"color: #0000ff;\">&#8221;&nbsp;<\/span><span style=\"color: #be3232;\">select<\/span><span style=\"color: #0000ff;\">=<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><span style=\"color: black; font-weight: bold;\">concat($currString,substring-before($restString,$Apos),$SqlApos)<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:with-param<\/span><span style=\"color: #be3232;\"> name<\/span><span style=\"color: #0000ff;\">=&#8221;<\/span><span style=\"color: black; font-weight: bold;\">restString<\/span><span style=\"color: #0000ff;\">&#8220;<\/span><span style=\"color: #be3232;\">&nbsp;select<\/span><span style=\"color: #0000ff;\">=<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><span style=\"color: black; font-weight: bold;\">substring-after($restString,$Apos)<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;\/xsl:call-template&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;\/xsl:when&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:otherwise&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;xsl:value-of<\/span><span style=\"color: #be3232;\"> select <\/span><span style=\"color: #0000ff;\">=<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><span style=\"color: black; font-weight: bold;\">concat($currString,$restString)<\/span><span style=\"color: #0000ff;\">&#8220;\/&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;\/xsl:otherwise&gt;<br \/>&nbsp;&nbsp; <\/span><span style=\"color: #0000ff;\">&lt;\/xsl:choose&gt;<br \/><\/span><span style=\"color: #0000ff;\">&lt;\/xsl:template&gt;<\/span><\/p>\n<p>I&#8217;ve put a little extra&nbsp;work in there with the &#8220;contains&#8221; test in the first template.&nbsp; This just avoids calling out to the second template at all, when there isn&#8217;t any extra work that needs to be done.<\/p>\n<p>The second template is the one that does the recursion; it finds and&nbsp;doubles delimiter characters one at a time, calling itself to&nbsp;fix the remainder of the string as it goes.&nbsp;&nbsp;&nbsp;When&nbsp;the second template&#8217;s&nbsp;all done fixing, the first template adds the outside SQL delimiters.<\/p>\n<p>Got it?<\/p>\n<p>Your version may or may not need the normalize-space function (depending on what your source is like).<\/p>\n<p>Your version might not need the external&nbsp;variables, either; I need them in other places so I found it convenient to declare them and make them readable up-top.<\/p>\n<p>With those caveats and potential simplifications, you can drop these two templates in pretty much anywhere you needed to drive SQL from XSLT.&nbsp; With a few changes, you could use them to drive javascript generation or other types of&nbsp;XSLT work that may face a&nbsp;problem with&nbsp;delimiters embedded in strings.&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<h2>&nbsp;<\/h2>\n<h2>Better than QUOTENAME at what it does.<\/h2>\n<p>&nbsp;<\/p>\n<p>Think of this recursive technique as the XSLT equivalent of the T-SQL QUOTENAME function &#8212; without QUOTENAME&#8217;s limitations. The T-SQL function&nbsp;doesn&#8217;t handle long strings.&nbsp;&nbsp;<\/p>\n<p>XSLT processors do this type of recursive string handling pretty fast.&nbsp; Even with <strong>really<\/strong> long strings. Even the MS XSLT processors.<\/p>\n<p>Enjoy.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my new job, I won&#8217;t be playing very much with SSRS, so the focus of this blog will change.&nbsp; We do use SQL Server on the back-end, so there&#8217;s almost always some&nbsp;T-SQL work in the mix &#8212;&nbsp;that&#8217;s always fun for me &#8212; but we don&#8217;t use Reporting Services.&nbsp; I will miss SSRS being part<a class=\"more-link\" href=\"https:\/\/spacefold.com\/lisa\/2012\/12\/14\/back-in-the-xslt-saddle-again\/\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,10],"tags":[],"class_list":["post-40","post","type-post","status-publish","format-standard","hentry","category-sql-server","category-xml-xslt"],"_links":{"self":[{"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/posts\/40","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/comments?post=40"}],"version-history":[{"count":0,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/posts\/40\/revisions"}],"wp:attachment":[{"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/media?parent=40"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/categories?post=40"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/tags?post=40"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}