XSL: replacing certain characters with xml tags

It's a little complicated and I'm a little stuck on it. I want to make tags instead of brackets '[' (for example, for buttons, links, etc.), And instead of ']'

<section> <title>Buttons</title> <orderedlist> <listitem> <para>Clicking on [Save] will attempt to save changes, then it navigates to <xref linkend="saved" xrefstyle="select: title"/>.</para> </listitem> <listitem> <para>Clicking on [Cancel] navigates to <xref linkend="noSave" xrefstyle="select: title"/>.</para> </listitem> </orderedlist> </section> 

To:

 <section> <title>Buttons</title> <orderedlist> <listitem> <para>Clicking on <uicontrol>Save</uicontrol> will attempt to save changes, then it navigates to <xref linkend="saved" xrefstyle="select: title"/>.</para> </listitem> <listitem> <para>Clicking on <uicontrol>Cancel</uicontrol> navigates to <xref linkend="noSave" xrefstyle="select: title"/>.</para> </listitem> </orderedlist> </section> 

And '[' ']' is optionally always in the .listitem.para section

Edit: I only need the replacement [] when certain words are in brackets.

+6
xml replace xslt
source share
3 answers

This conversion is :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my" exclude-result-prefixes="my"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <my:uicontrols> <control>[Save]</control> <control>[Cancel]</control> </my:uicontrols> <xsl:key name="kHasControls" match="text()" use="boolean(document('')/*/my:uicontrols/*[contains(current(), .)])"/> <xsl:variable name="vControls" select="document('')/*/my:uicontrols/*"/> <xsl:template match="node()|@*" name="identity"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="text()[key('kHasControls', 'true')]"> <xsl:choose> <xsl:when test="not($vControls[contains(current(),.)])"> <xsl:copy-of select="."/> </xsl:when> <xsl:otherwise> <xsl:call-template name="createControl"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="createControl"> <xsl:param name="pText" select="."/> <xsl:choose> <xsl:when test="not(contains($pText, '['))"> <xsl:copy-of select="$pText"/> </xsl:when> <xsl:otherwise> <xsl:copy-of select="substring-before($pText, '[')"/> <xsl:variable name="vStartText" select= "concat('[', substring-after($pText, '['))"/> <xsl:variable name="vCtrl" select="$vControls[starts-with($vStartText,.)]"/> <xsl:choose> <xsl:when test="not($vCtrl)"> <xsl:text>[</xsl:text> <xsl:call-template name="createControl"> <xsl:with-param name="pText" select="substring($vStartText,1)"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <uicontrol> <xsl:value-of select="translate($vCtrl,'[]','')"/> </uicontrol> <xsl:call-template name="createControl"> <xsl:with-param name="pText" select="substring-after($vStartText, $vCtrl)"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> 

when applied to the provided XML document :

 <section> <title>Buttons</title> <orderedlist> <listitem> <para>Clicking on [Save] will attempt to save changes, then it navigates to <xref linkend="saved" xrefstyle="select: title"/>.</para> </listitem> <listitem> <para>Clicking on [Cancel] navigates to <xref linkend="noSave" xrefstyle="select: title"/>.</para> </listitem> </orderedlist> </section> 

creates the desired, correct result :

 <section> <title>Buttons</title> <orderedlist> <listitem> <para>Clicking on <uicontrol>Save</uicontrol><xref linkend="saved" xrefstyle="select: title"/>.</para> </listitem> <listitem> <para>Clicking on <uicontrol>Cancel</uicontrol><xref linkend="noSave" xrefstyle="select: title"/>.</para> </listitem> </orderedlist> </section> 

Please note the following :

  • This solution replaces only the controlled list of management names . Thus, we are protected from accidental errors, and we are also free to use strings like "[Anything]" without any problems (for example, we want to display the famous XPath expression - such an expression by definition has predicates :))

  • Using keys provides better performance than scanning each node text for "[" .

+2
source share

Without nested uicontrol for nested brackets (which require parsing for balanced brackets and without balanced brackets).

This style sheet:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="text()" name="replace" priority="1"> <xsl:param name="pString" select="."/> <xsl:variable name="vMask" select="translate($pString, translate($pString, '[]', ''), '')"/> <xsl:choose> <xsl:when test="contains($vMask,'[]')"> <xsl:call-template name="makeControl"> <xsl:with-param name="pString" select="$pString"/> <xsl:with-param name="pMask" select="substring-before($vMask,'[]')"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$pString"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="makeControl"> <xsl:param name="pString"/> <xsl:param name="pMask"/> <xsl:choose> <xsl:when test="$pMask"> <xsl:variable name="vMask" select="substring($pMask,1,1)"/> <xsl:value-of select="concat( substring-before( $pString, $vMask), $vMask)"/> <xsl:call-template name="makeControl"> <xsl:with-param name="pString" select="substring-after($pString,$vMask)"/> <xsl:with-param name="pMask" select="substring($pMask,2)"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="substring-before($pString,'[')"/> <uicontrol> <xsl:value-of select="substring-before( substring-after( $pString, '['), ']')"/> </uicontrol> <xsl:call-template name="replace"> <xsl:with-param name="pString" select="substring-after($pString,']')"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> 

Output:

 <section> <title>Buttons</title> <orderedlist> <listitem> <para>Clicking on <uicontrol>Save</uicontrol> will attempt to save changes, then it navigates to <xref linkend="saved" xrefstyle="select: title"></xref>.</para> </listitem> <listitem> <para>Clicking on <uicontrol>Cancel</uicontrol> navigates to <xref linkend="noSave" xrefstyle="select: title"></xref>.</para> </listitem> </orderedlist> </section> 

And with this input:

 <text> This is an opening bracket [ ? [Yes] [No] This is a closing bracket ] ? [Yes] [No] </text> 

Output:

 <text> This is an opening bracket [ ? <uicontrol>Yes</uicontrol> <uicontrol>No</uicontrol> This is a closing bracket ] ? <uicontrol>Yes</uicontrol> <uicontrol>No</uicontrol> </text> 

Note Any textual match \[[^\[\]]*\] will be split into uicontrol .

+3
source share

You can work with the contains , substring-before and substring-after functions to find the brackets, and then insert the elements you need instead of the brackets.

Edit - this should work:

 <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> <xsl:output method="xml" indent="yes"/> <xsl:template name="insertElements"> <xsl:param name="text" select="." /> <xsl:choose> <xsl:when test="contains($text, '[')"> <xsl:value-of select="substring-before($text, '[')"/> <xsl:variable name="after" select="substring-after($text, '[')" /> <uicontrol> <xsl:value-of select="substring-before($after, ']')"/> </uicontrol> <xsl:call-template name="insertElements"> <xsl:with-param name="text" select="substring-after($after, ']')" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$text"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:for-each select="node()"> <xsl:choose> <xsl:when test="self::text()"> <xsl:call-template name="insertElements" /> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="." /> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:copy> </xsl:template> </xsl:stylesheet> 
+2
source share

All Articles