this is linked from Attaching ancestor attributes to child nodes
I'm extracting names from a large xml dataset, I need to extract displayname, and the other name types (currently I am only picking out Synonyms and SystematicNames). Right now with the help of an awesome person I've gotten so far, but it only extracts the first of each type...
Sample XML
<Chemical id="0000103902" displayFormula="C8-H9-N-O2" displayName="Acetaminophen [USP:JAN]">
<NameList>
<DescriptorName>Acetaminophen<SourceList><Source>MeSH</Source></SourceList></DescriptorName>
<NameOfSubstance>Acetaminophen<SourceList><Source>HSDB</Source><Source>MeSH</Source></SourceList></NameOfSubstance>
<NameOfSubstance>Acetaminophen [USP:JAN]<SourceList><Source>NLM</Source></SourceList></NameOfSubstance>
<MixtureName>Actifed Plus<SourceList><Source>MeSH</Source></SourceList></MixtureName>
<MixtureName>Jin Gang<SourceList><Source>NLM</Source></SourceList></MixtureName>
<MixtureName>Talacen<SourceList><Source>NLM</Source></SourceList></MixtureName>
<SystematicName>Acetamide, N-(4-hydroxyphenyl)-<SourceList><Source>EPA SRS</Source><Source>MeSH</Source><Source>TSCAINV</Source></SourceList></SystematicName>
<SystematicName>Acetaminophen<SourceList><Source>CCRIS</Source></SourceList></SystematicName>
<SystematicName>Acetanilide, 4'-hydroxy-<SourceList><Source>RTECS</Source></SourceList></SystematicName>
<SystematicName>Paracetamol<SourceList><Source>ECHA</Source><Source>EINECS</Source></SourceList></SystematicName>
<Synonyms>4-13-00-01091 (Beilstein Handbook Reference)<SourceList><Source>RTECS</Source></SourceList></Synonyms>
<Synonyms>Abensanil<SourceList><Source>HSDB</Source><Source>RTECS</Source></SourceList></Synonyms>
<Synonyms>Acetagesic<SourceList><Source>HSDB</Source><Source>RTECS</Source></SourceList></Synonyms>
<Synonyms>Acetamide, N-(p-hydroxyphenyl)-<SourceList><Source>RTECS</Source></SourceList></Synonyms>
</NameList>
</Chemical>
Current code
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:variable name="FS">
<!-- Field seperator -->
<xsl:text>;</xsl:text>
</xsl:variable>
<xsl:variable name="LT">
<!-- Line terminator -->
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:for-each select="//Chemical[#displayName != '' and #displayName != 'INDEX NAME NOT YET ASSIGNED']">
<xsl:call-template name="printValues">
<xsl:with-param name="val1" select="#id" />
<xsl:with-param name="val2" select="#displayName" />
</xsl:call-template>
<xsl:if test="normalize-space(NameList/SystematicName/text()) != ''">
<xsl:call-template name="printValues">
<xsl:with-param name="val1" select="#id" />
<xsl:with-param name="val2" select="normalize-space(NameList/SystematicName/text())" />
</xsl:call-template>
</xsl:if>
<xsl:if test="normalize-space(NameList/Synonyms/text()) != ''">
<xsl:call-template name="printValues">
<xsl:with-param name="val1" select="#id" />
<xsl:with-param name="val2" select="normalize-space(NameList/Synonyms/text())" />
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="printValues">
<xsl:param name="val1" />
<xsl:param name="val2" />
<!-- constants -->
<xsl:variable name="url" select="'https://chem.nlm.nih.gov/chemidplus/sid/startswith/'" />
<xsl:variable name="src" select="'nlm'" />
<xsl:text>"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="paramStr" select="$val2" />
</xsl:call-template>
<xsl:text>"</xsl:text>
<xsl:text>,</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="concat($url, $val1)" />
<xsl:text>"</xsl:text>
<xsl:text>,</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="$src" />
<xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template name="escapeQuote">
<xsl:param name="paramStr" />
<xsl:if test="string-length($paramStr) > 0">
<xsl:value-of select="substring-before(concat($paramStr, '"'), '"')" />
<xsl:if test="contains($paramStr, '"')">
<xsl:text>\"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="paramStr" select="substring-after($paramStr, '"')" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
However this only gives:-
"Acetaminophen [USP:JAN]","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"Acetamide, N-(4-hydroxyphenyl)-","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"4-13-00-01091 (Beilstein Handbook Reference)","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
How do I go about extracting all the children in the same manner?
Printing of the text values in the <SystematicName> and <Synonyms> nodes can be achieved by adding <xsl:for-each> loop for those elements. The <xsl:if> condition can also be handled in the <xsl:for-each> selection.
Please modify the <xsl:template match="/"> as shown below.
<xsl:template match="Chemical[#displayName != '' and #displayName != 'INDEX NAME NOT YET ASSIGNED']">
<xsl:variable name="idValue" select="#id" />
<xsl:call-template name="printValues">
<xsl:with-param name="val1" select="$idValue" />
<xsl:with-param name="val2" select="#displayName" />
</xsl:call-template>
<xsl:for-each select="NameList/SystematicName[text() != '']">
<xsl:call-template name="printValues">
<xsl:with-param name="val1" select="$idValue" />
<xsl:with-param name="val2" select="normalize-space(text())" />
</xsl:call-template>
</xsl:for-each>
<xsl:for-each select="NameList/Synonyms[text() != '']">
<xsl:call-template name="printValues">
<xsl:with-param name="val1" select="$idValue" />
<xsl:with-param name="val2" select="normalize-space(text())" />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
Output
"Acetaminophen [USP:JAN]","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"Acetamide, N-(4-hydroxyphenyl)-","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"Acetaminophen","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"Acetanilide, 4'-hydroxy-","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"Paracetamol","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"4-13-00-01091 (Beilstein Handbook Reference)","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"Abensanil","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"Acetagesic","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
"Acetamide, N-(p-hydroxyphenyl)-","https://chem.nlm.nih.gov/chemidplus/sid/startswith/0000103902","nlm"
Related
(This question is a less simplified version of my problem. The more simplified version which was already answered can be found here. I'm posting this more complicated question due to a comment by michael.hor257k who suggested that there may be an alternative approach that could solve it - possibly using select in a loop, or possibly a completely different approach.)
I'd like to process an XML file, over whose format I have no control, to generate C++ code. I need to process functions defined in XML in several different ways to produce different parts of the code. As part of this I need to select a subset of function parameters that match a complicated criteria and pass this selection to several named templates; the named templates need to be able to access the original document.
This example creates a complex selection of C++ function parameters that do not have constant values (ie the same min and max), where the min and max may be decimal or hexadecimal, using the "GenerateNonFixedParameters" template. The parameters refer to enumerations which are located elsewhere in the document, and these definitions are referenced by the named template call "ListParameterValues".
There are two problems.
The creation of the variable "nonFixedParameters" does not use select. I cannot work out how to use select for such a complicated case (XSL 1.0), but maybe there is a way.
A copy of the nodes does not suffice, as the "ListParameterValues" template as it currently stands needs to operate on an original set of nodes from the document.
Example XSL with the locations of these two problems marked:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" encoding="iso-8859-1" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:for-each select="//function">
<!-- 1. This does not use 'select' therefore it does not work. This is XSL 1.0 so as="node()*" cannot be used. -->
<xsl:variable name="nonFixedParameters">
<xsl:call-template name="GenerateNonFixedParameters"/>
</xsl:variable>
<xsl:call-template name="ListParameterValues">
<xsl:with-param name="parameters" select="$nonFixedParameters"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="ListParameterValues">
<xsl:param name="parameters"/>
<xsl:for-each select="$parameters">
<xsl:value-of select="#name"/>
<xsl:text>[</xsl:text>
<xsl:variable name="min">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#min" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="max">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#max" />
</xsl:call-template>
</xsl:variable>
<!-- 2. This must be executed in the context of a document node, therefore this does not work. -->
<xsl:for-each select="//enum[#name=current()/#enum]/value">
<xsl:if test="#val >= $min and #val <= $max">
<xsl:value-of select="#name"/>
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>] </xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="GenerateNonFixedParameters">
<xsl:for-each select="parameter">
<xsl:variable name="min">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#min" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="max">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#max" />
</xsl:call-template>
</xsl:variable>
<xsl:if test="$min != $max">
<!-- Here a copy is clearly the wrong approach! -->
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="HexToNum">
<xsl:param name="hex" />
<xsl:param name="num" select="0"/>
<xsl:param name="msb" select="translate(substring($hex, 1, 1), 'abcdef', 'ABCDEF')"/>
<xsl:param name="value" select="string-length(substring-before('0123456789ABCDEF', $msb))"/>
<xsl:param name="result" select="16 * $num + $value"/>
<xsl:if test="string-length($hex) > 1">
<xsl:call-template name="HexToNum">
<xsl:with-param name="hex" select="substring($hex, 2)"/>
<xsl:with-param name="num" select="$result"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="string-length($hex) <= 1">
<xsl:value-of select="$result"/>
</xsl:if>
</xsl:template>
<xsl:template name="ToNum">
<xsl:param name="hexOrNum" />
<xsl:if test="starts-with($hexOrNum, '0x')">
<xsl:call-template name="HexToNum">
<xsl:with-param name="hex" select="substring-after($hexOrNum, '0x')"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(starts-with($hexOrNum, '0x'))">
<xsl:value-of select="$hexOrNum"/>
</xsl:if>
</xsl:template>
</xsl:transform>
Simple XML to feed the above:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<dictionary>
<enum name="EnumName">
<value name="firstValue" val="1" />
<value name="secondValue" val="2" />
<value name="thirdValue" val="3" />
<value name="forthValue" val="4" />
<value name="fifthValue" val="5" />
</enum>
</dictionary>
<function name="FunctionOne">
<parameter name="p1" type="enum" enum="EnumName" min="2" max="0x4"/>
<parameter name="p2" type="enum" enum="EnumName" min="0x03" max="3"/>
</function>
</body>
Wanted output. Note that p1 has all names within [min..max] listed, but p2 has none listed because min and max have the same value.
p1[secondValue thirdValue forthValue ] p2[]
I think your stylesheet can be made to work with XSLT 1.0 if you use an extension function like exsl:node-set to convert your result tree fragment into a node-set and if you store the root node of the primary input tree into a global variable or parameter as then you will be able to compare nodes in your primary input document to nodes of the newly constructed, temporary tree.
Based on these suggestions the code would look like
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:exsl="http://exslt.org/common">
<xsl:output method="text" encoding="iso-8859-1" omit-xml-declaration="yes" />
<xsl:variable name="main-root" select="/"/>
<xsl:template match="/">
<xsl:for-each select="//function">
<!-- 1. Using exsl:node-set or similar you can convert that result tree fragment into a node set to process it further -->
<xsl:variable name="nonFixedParameters">
<xsl:call-template name="GenerateNonFixedParameters"/>
</xsl:variable>
<xsl:call-template name="ListParameterValues">
<xsl:with-param name="parameters" select="$nonFixedParameters"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="ListParameterValues">
<xsl:param name="parameters"/>
<!-- <xsl:for-each xmlns:ms="urn:schemas-microsoft-com:xslt" select="ms:node-set($parameters)/parameter"> for MSXML or XslTransform -->
<xsl:for-each select="exsl:node-set($parameters)/parameter">
<xsl:value-of select="#name"/>
<xsl:text>[</xsl:text>
<xsl:variable name="min">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#min" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="max">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#max" />
</xsl:call-template>
</xsl:variable>
<!-- 2. This must be executed in the context of a document node, therefore using the global variable works. -->
<xsl:for-each select="$main-root//enum[#name=current()/#enum]/value">
<xsl:if test="#val >= $min and #val <= $max">
<xsl:value-of select="#name"/>
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>] </xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="GenerateNonFixedParameters">
<xsl:for-each select="parameter">
<xsl:variable name="min">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#min" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="max">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#max" />
</xsl:call-template>
</xsl:variable>
<xsl:if test="$min != $max">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="HexToNum">
<xsl:param name="hex" />
<xsl:param name="num" select="0"/>
<xsl:param name="msb" select="translate(substring($hex, 1, 1), 'abcdef', 'ABCDEF')"/>
<xsl:param name="value" select="string-length(substring-before('0123456789ABCDEF', $msb))"/>
<xsl:param name="result" select="16 * $num + $value"/>
<xsl:if test="string-length($hex) > 1">
<xsl:call-template name="HexToNum">
<xsl:with-param name="hex" select="substring($hex, 2)"/>
<xsl:with-param name="num" select="$result"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="string-length($hex) <= 1">
<xsl:value-of select="$result"/>
</xsl:if>
</xsl:template>
<xsl:template name="ToNum">
<xsl:param name="hexOrNum" />
<xsl:if test="starts-with($hexOrNum, '0x')">
<xsl:call-template name="HexToNum">
<xsl:with-param name="hex" select="substring-after($hexOrNum, '0x')"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(starts-with($hexOrNum, '0x'))">
<xsl:value-of select="$hexOrNum"/>
</xsl:if>
</xsl:template>
</xsl:transform>
The example is online at http://xsltransform.net/94hvTzi/1.
Let me show a different approach that actually selects and processes the original nodes, in their original context - as was discussed in the previous thread. Consider:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="text" encoding="utf-8"/>
<xsl:template match="/">
<xsl:for-each select="body/function">
<xsl:call-template name="select-parameters">
<xsl:with-param name="input-set" select="parameter"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="select-parameters">
<xsl:param name="input-set"/>
<xsl:param name="output-set" select="dummy-node"/>
<xsl:variable name="current-node" select="$input-set[1]" />
<xsl:choose>
<xsl:when test="$current-node">
<xsl:variable name="min">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="$current-node/#min" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="max">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="$current-node/#max" />
</xsl:call-template>
</xsl:variable>
<!-- recursive call -->
<xsl:call-template name="select-parameters">
<xsl:with-param name="input-set" select="$input-set[position() > 1]"/>
<xsl:with-param name="output-set" select="$output-set | $current-node[$min != $max]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- call a template to process the currently selected node-set -->
<xsl:call-template name="process-parameters">
<xsl:with-param name="input-set" select="$output-set"/>
</xsl:call-template>
<!-- call more templates here, if required -->
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:key name="enum-by-name" match="enum" use="#name" />
<xsl:template name="process-parameters">
<xsl:param name="input-set"/>
<xsl:for-each select="$input-set">
<xsl:variable name="min">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#min" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="max">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="#max" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat(#name, '[')"/>
<xsl:for-each select="key('enum-by-name', #enum)/value[#val >= $min and #val <= $max]">
<xsl:value-of select="#name"/>
<xsl:text> </xsl:text>
</xsl:for-each>
<xsl:text>] </xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="HexToNum">
<xsl:param name="hex" />
<xsl:param name="num" select="0"/>
<xsl:param name="msb" select="translate(substring($hex, 1, 1), 'abcdef', 'ABCDEF')"/>
<xsl:param name="value" select="string-length(substring-before('0123456789ABCDEF', $msb))"/>
<xsl:param name="result" select="16 * $num + $value"/>
<xsl:if test="string-length($hex) > 1">
<xsl:call-template name="HexToNum">
<xsl:with-param name="hex" select="substring($hex, 2)"/>
<xsl:with-param name="num" select="$result"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="string-length($hex) <= 1">
<xsl:value-of select="$result"/>
</xsl:if>
</xsl:template>
<xsl:template name="ToNum">
<xsl:param name="hexOrNum" />
<xsl:if test="starts-with($hexOrNum, '0x')">
<xsl:call-template name="HexToNum">
<xsl:with-param name="hex" select="substring-after($hexOrNum, '0x')"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(starts-with($hexOrNum, '0x'))">
<xsl:value-of select="$hexOrNum"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The problem with this approach is that it works exactly as advertised; the nodes selected at the end of the selecting processes are the original, unmodified parameters. As a result, they still carry the mixture of decimal and hexadecimal values, and you must convert these again when processing the selected set.
So it might well be more worthwhile to pre-process the parameters by normalizing the values to a common base, then use the result (converted to a node-set) for the rest of the processing. I wouldn't spend so much effort at selecting those that meet the criteria - because once the values are consistent, the selection becomes trivial. If you like, I will post a demo showing that.
I am quite new to XSLT,
I have a source XML message, which in its simplified version looks something like this:
<?xml version='1.0' encoding='iso-8859-1'?>
<Message>
<Invalid>
<InvalidBody>
<SynchError>
<ErrorText>The value of %1 should not be %2.</ErrorText>
<ErrorParameter>
<!-- Error Parameter is %1 identifier -->
<ErrorParameterType>value</ErrorParameterType>
<ErrorParameterValue>someField</ErrorParameterValue>
</ErrorParameter>
<ErrorParameter>
<!-- Error Parameter is %2 identifier -->
<ErrorParameterType>value</ErrorParameterType>
<ErrorParameterValue>someValue</ErrorParameterValue>
</ErrorParameter>
</SynchError>
</InvalidBody>
</Invalid>
</Message>
Now, I would like to use XSLT 1.0 to extract the ErrorText string and substitute the parameters %1 and %2 with the corresponding ErrorParameter/ErrorParameterValue values. The number of parameters %1, %2, %3... cannot be known in advance, so the solution should be flexible enough to accommodate a variable number of parameters.
Is there any elegant way to do this?
So, after quite a lot of googling around and a healthy dose of headache, I came up with the following solution, which seems to work like a charm:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:variable name="err-text" select="/Message/Invalid/InvalidBody/SynchError/ErrorText" />
<xsl:variable name="param-count" select="count(/Message/Invalid/InvalidBody/SynchError/ErrorParameter)" />
<xsl:call-template name="replace-params">
<xsl:with-param name="position" select="$param-count"/>
<xsl:with-param name="source-text" select="$err-text" />
</xsl:call-template>
</xsl:template>
<xsl:template name="replace-params">
<xsl:param name="position"/>
<xsl:param name="source-text"/>
<xsl:choose>
<xsl:when test="$position = 0">
<xsl:value-of select="$source-text"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="replace-params">
<xsl:with-param name="position" select="$position - 1"/>
<xsl:with-param name="source-text">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$source-text" />
<xsl:with-param name="replace" select="concat('%', $position)" />
<xsl:with-param name="by" select="/Message/Invalid/InvalidBody/SynchError/ErrorParameter[$position]/ErrorParameterValue" />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)" />
<xsl:value-of select="$by" />
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text"
select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="by" select="$by" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I use the "string-replace-all" template as a substitute for the XSLT 2.0 replace() function, since I cannot exclude multiple occurrences of a single parameter.
The "replace-params" template is applied recursively on the original text, iterating backwards on the index of the set of ErrorParameters.
The way I have tackled similar problems is to create a named template that recurses through the string (text element of) <ErrorText> with each cycle picking out the first n% item, then dereferences the <ErrorParameter> to access the contents of that and store in a result, then snip off the n% item just process and calls itself to grab the next one. When there are no more %n items left, return the result.
Here's an example of that, this template basically counts comma-separated items in a parameter passed in on the first cycle called List, and returns a $Count when it's finished.
<xsl:template name="CountList">
<xsl:param name="List"/>
<xsl:param name="Count" select="0"/>
<xsl:choose>
<xsl:when test="contains($List,',') = false()">
<xsl:value-of select="$Count"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="CountList">
<xsl:with-param name="List">
<xsl:value-of select="substring-after($List,',')"/>
</xsl:with-param>
<xsl:with-param name="Count">
<xsl:value-of select="$Count + 1"/>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
You can try something like this. You can use a replace instead of 'substring-before' and 'after' if this function is supported.
<xsl:template match="SynchError">
<xsl:apply-templates select="ErrorParameter[1]">
<xsl:with-param name="text"><xsl:value-of select="ErrorText"/></xsl:with-param>
<xsl:with-param name="position">1</xsl:with-param>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="ErrorParameter">
<xsl:param name="text"/>
<xsl:param name="position"/>
<xsl:apply-templates select="following::ErrorParameter">
<xsl:with-param name="position"><xsl:value-of select="number($position)+1"/></xsl:with-param>
<xsl:with-param name="text"><xsl:value-of select="concat(substring-before($text,concat('%',$position)),ErrorParameterValue,substring-after($text,concat('%',$position)))"/></xsl:with-param>
</xsl:apply-templates>
<xsl:if test="not(following::ErrorParameter)">
<xsl:value-of select="concat(substring-before($text,concat('%',$position)),ErrorParameterValue,substring-after($text,concat('%',$position)))"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Here's another way you could look at it:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<output>
<xsl:call-template name="merge">
<xsl:with-param name="string" select="Message/Invalid/InvalidBody/SynchError/ErrorText"/>
<xsl:with-param name="parameters" select="Message/Invalid/InvalidBody/SynchError/ErrorParameter"/>
</xsl:call-template>
</output>
</xsl:template>
<xsl:template name="merge">
<xsl:param name="string"/>
<xsl:param name="parameters"/>
<xsl:param name="flag" select="'%'"/>
<xsl:choose>
<xsl:when test="contains($string, $flag)">
<xsl:variable name="subsequent-char"
select="substring(translate(substring-after($string, $flag), '0123456789', ''), 1, 1)"/>
<xsl:variable name="i"
select="substring-before(substring-after($string, $flag), $subsequent-char)" />
<xsl:value-of select="substring-before($string, $flag)"/>
<xsl:value-of select="$parameters[number($i)]/ErrorParameterValue"/>
<!-- recursive call -->
<xsl:call-template name="merge">
<xsl:with-param name="string" select="substring-after($string, concat($flag, $i))"/>
<xsl:with-param name="parameters" select="$parameters"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I am using Docbook-XSL to process an XML file and produce HTML and XML output files. These files will be used for the online-help in an Eclipse RCP application.
The standard XSL does not produce a contexts.xml file needed to enable F1 context help so I have made use of an XSL I found online here http://blogs.itemis.de/leipzig/archives/691.
This works fine until I add German umlauts into the source text. These characters get converted into named entities such as ä and ß. This causes errors in the outputted XML as these entities cannot be found.
Is there a way to tell the XSL processor to used the numbered entities such as ß?
(I could perform a search and replace on the output file but this seems a bit clumsy...)
The top-level XSL:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ng="http://docbook.org/docbook-ng" xmlns:db="http://docbook.org/ns/docbook"
xmlns:exsl="http://exslt.org/common" version="1.0"
exclude-result-prefixes="exsl db ng">
<!-- make sure the path to the standard eclipse.xsl is correct -->
<xsl:import href="docbook-xsl-1.78.1/eclipse/eclipse3.xsl" />
<xsl:param name="manifest">
1
</xsl:param>
<xsl:param name="create.plugin.xml">
1
</xsl:param>
<xsl:param name="create.context.xml">
1
</xsl:param>
<xsl:param name="context.constants.package">
org.example.help
</xsl:param>
<xsl:template name="plugin.xml">
<xsl:if test="$create.context.xml = '1'">
<xsl:call-template name="context.xml" />
<xsl:call-template name="contextconstants">
<xsl:with-param name="package">
<xsl:value-of select="$context.constants.package" />
</xsl:with-param>
</xsl:call-template>
</xsl:if>
<xsl:if test="$create.plugin.xml = '1'">
<xsl:call-template name="write.chunk">
<xsl:with-param name="filename">
<xsl:if test="$manifest.in.base.dir != 0">
<xsl:value-of select="$base.dir" />
</xsl:if>
<xsl:value-of select="'plugin.xml'" />
</xsl:with-param>
<xsl:with-param name="method" select="'xml'" />
<xsl:with-param name="encoding" select="'utf-8'" />
<xsl:with-param name="indent" select="'yes'" />
<xsl:with-param name="content">
<xsl:choose>
<xsl:when test="$manifest = '1'">
<plugin>
<extension point="org.eclipse.help.toc">
<toc file="toc.xml" primary="true" />
</extension>
<extension point="org.eclipse.help.contexts">
<contexts file="contexts.xml"></contexts>
</extension>
</plugin>
</xsl:when>
<xsl:otherwise>
<plugin name="{$eclipse.plugin.name}" id="{$eclipse.plugin.id}"
version="1.0" provider-name="{$eclipse.plugin.provider}">
<extension point="org.eclipse.help.toc">
<toc file="toc.xml" primary="true" />
</extension>
</plugin>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="context.xml">
<xsl:call-template name="write.chunk">
<xsl:with-param name="filename" select="'contexts.xml'" />
<xsl:with-param name="method" select="'xml'" />
<xsl:with-param name="encoding" select="'utf-8'" />
<xsl:with-param name="indent" select="'yes'" />
<xsl:with-param name="quiet" select="$chunk.quietly" />
<xsl:with-param name="content">
<contexts>
<xsl:apply-templates select="/*" mode="context.xml" />
</contexts>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template
match="book|part|reference|preface|chapter|bibliography|appendix|article|glossary|section|sect1|sect2|sect3|sect4|sect5|refentry|colophon|bibliodiv|index"
mode="context.xml">
<xsl:variable name="title">
<xsl:if test="$eclipse.autolabel=1">
<xsl:variable name="label.markup">
<xsl:apply-templates select="." mode="label.markup" />
</xsl:variable>
<xsl:if test="normalize-space($label.markup)">
<xsl:value-of select="concat($label.markup,$autotoc.label.separator)" />
</xsl:if>
</xsl:if>
<xsl:apply-templates select="." mode="title.markup" />
</xsl:variable>
<xsl:variable name="href">
<xsl:call-template name="href.target.with.base.dir">
<xsl:with-param name="context" select="/" />
</xsl:call-template>
</xsl:variable>
<!-- <xsl:message>Node=<xsl:value-of select="name(.)"></xsl:value-of>,
ID=<xsl:call-template name="object.id"> <xsl:with-param name="object" select="."/>
</xsl:call-template></xsl:message> -->
<xsl:call-template name="acontext">
<xsl:with-param name="id">
<xsl:call-template name="object.id">
<xsl:with-param name="object" select="." />
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="label" select="$title" />
<xsl:with-param name="href" select="$href" />
</xsl:call-template>
<xsl:apply-templates
select="part|reference|preface|chapter|bibliography|appendix|article|glossary|section|sect1|sect2|sect3|sect4|sect5|refentry|colophon|bibliodiv|index"
mode="context.xml" />
</xsl:template>
<xsl:template name="acontext">
<xsl:param name="id" />
<xsl:param name="label" />
<xsl:param name="href" />
<xsl:message><xsl:value-of select="$id"/>, <xsl:value-of select="$href"/>
</xsl:message>
<context id="{normalize-space($id)}" title="">
<description />
<topic href="{$href}" label="{normalize-space($label)}" />
</context>
</xsl:template>
<xsl:template name="contextconstants">
<xsl:param name="package" />
<xsl:call-template name="write.chunk">
<xsl:with-param name="filename" select="'ContextConstants.java'" />
<xsl:with-param name="method" select="'java'" />
<xsl:with-param name="encoding" select="'utf-8'" />
<xsl:with-param name="indent" select="'yes'" />
<xsl:with-param name="quiet" select="$chunk.quietly" />
<xsl:with-param name="content">
package
<xsl:value-of select="$package" />
;
public class ContextConstants{
<xsl:apply-templates select="/*" mode="contextconstants" />
}
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template
match="book|part|reference|preface|chapter|bibliography|appendix|article|glossary|section|sect1|sect2|sect3|sect4|sect5|refentry|colophon|bibliodiv|index"
mode="contextconstants">
<xsl:variable name="title">
<xsl:if test="$eclipse.autolabel=1">
<xsl:variable name="label.markup">
<xsl:apply-templates select="." mode="label.markup" />
</xsl:variable>
<xsl:if test="normalize-space($label.markup)">
<xsl:value-of select="concat($label.markup,$autotoc.label.separator)" />
</xsl:if>
</xsl:if>
<xsl:apply-templates select="." mode="title.markup" />
</xsl:variable>
<xsl:variable name="href">
<xsl:call-template name="href.target.with.base.dir">
<xsl:with-param name="context" select="/" />
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="acontextconstant">
<xsl:with-param name="id">
<xsl:call-template name="object.id">
<xsl:with-param name="object" select="." />
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="label" select="$title" />
<xsl:with-param name="href" select="$href" />
</xsl:call-template>
<xsl:apply-templates
select="part|reference|preface|chapter|bibliography|appendix|article|glossary|section|sect1|sect2|sect3|sect4|sect5|refentry|colophon|bibliodiv|index"
mode="contextconstants" />
</xsl:template>
<xsl:template name="acontextconstant">
<xsl:param name="id" />
<xsl:param name="label" />
<xsl:param name="href" />
public static final String C_
<xsl:value-of select="translate($id,'-','_')" />
="
<xsl:value-of select="$id" />
";
</xsl:template>
</xsl:stylesheet>
If you're using Saxon then you could try saxon:character-representation: see
http://www.saxonica.com/documentation/#!extensions/output-extras/character-representation
Needs Saxon-PE or higher.
However, I'm confused about your requirements. If you want XML output, use method="xml". In that case you won't get a tag generated. If you want a tag but don't want HTML character references, you seem to want some kind of hybrid, and I'm not sure why. Perhaps method="xhtml" will fit your needs?
I have implemented the superfish navigation menu into an umbraco installation. Superfish simply takes a UL element and turns it into a hierarchical menu that shows child items when you hover over the parent (you remember when they were cool back in 1999 right?).
I cannot figure out why, on certain pages (usually ones without children), the menu does not show child items for any page. My exposure to XSLT is minimal, so i must be overlooking some logic.
You can see the actual site here Hover over 'personal training' to see the menu work, now click on 'weight management' and hey-presto the magic stops happening.
The XSLT that creates the UL structure is below, and the HTML page source tells me that its simply not generating any LI elements for child pages when the issue occurs.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library"
exclude-result-prefixes="msxml umbraco.library">
<xsl:output method="html" omit-xml-declaration="yes"/>
<xsl:param name="currentPage" />
<!--This sets the level that the nav starts at and tells us if we should recurse through child elements-->
<xsl:variable name="startDepth" select="/macro/startingLevel" />
<xsl:variable name="recurse" select="/macro/recurse" />
<xsl:variable name="selectBranches" select="/macro/selectBranches"></xsl:variable>
<xsl:variable name="maxMenuDepth" select="/macro/maxMenuDepth"></xsl:variable>
<xsl:variable name="forceNode" select="/macro/forceNode"></xsl:variable>
<xsl:variable name="walkChildren" select="/macro/expandChildren"></xsl:variable>
<xsl:variable name="forceHome" select="/macro/forceHome"></xsl:variable>
<xsl:variable name="securityTrimming" select="/macro/securityTrimming"></xsl:variable>
<!--Alternate page title variable in here-->
<!--Styles for the navigation-->
<xsl:variable name="ulBaseClass" select="/macro/ulBaseClass"></xsl:variable>
<xsl:variable name="branchClass" select="/macro/branchClass"></xsl:variable>
<xsl:variable name="selectedClass" select="/macro/selectedClass"></xsl:variable>
<xsl:variable name="startLevel">
<xsl:choose>
<xsl:when test="$startDepth >= 0">
<xsl:value-of select="$startDepth"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$currentPage/#level"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!--This calls first iteration of the navigation, sending the first node at the correct depth found in the ancestors of the current page-->
<xsl:template match="/">
<xsl:choose>
<xsl:when test="$forceNode">
<xsl:variable name="currentNode" select="umbraco.library:GetXmlNodeById($forceNode)"></xsl:variable>
<xsl:call-template name="nodeIterator">
<xsl:with-param name="parentNode" select="$currentNode/ancestor-or-self::*[#isDoc][#level=$startLevel]
[
string(umbracoNaviHide) != '1'
and ($securityTrimming != '1'
or umbraco.library:IsProtected(#id, #path) = false()
or umbraco.library:HasAccess(#id, #path) = true())
]" />
<xsl:with-param name="pseudoCurrentPage" select="$currentNode" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="currentNode" select="$currentPage"></xsl:variable>
<xsl:call-template name="nodeIterator">
<xsl:with-param name="parentNode" select="$currentNode/ancestor-or-self::*[#isDoc][#level=$startLevel]
[
string(umbracoNaviHide) != '1'
and ($securityTrimming != '1'
or umbraco.library:IsProtected(#id, #path) = false()
or umbraco.library:HasAccess(#id, #path) = true())
]" />
<xsl:with-param name="pseudoCurrentPage" select="$currentNode" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="nodeIterator">
<xsl:param name="parentNode" />
<xsl:param name="pseudoCurrentPage" />
<!-- do not show info doc node types-->
<xsl:variable name="calculatedMenuDepth" select="($parentNode/#level - $startLevel)+1" />
<xsl:if test="$parentNode/*[#isDoc] or ($calculatedMenuDepth = 1 and $forceHome)">
<ul>
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$calculatedMenuDepth = 1">
<xsl:value-of select="$ulBaseClass" />
</xsl:when>
<!--<xsl:when test="$calculatedMenuDepth = 1">
<xsl:value-of select="concat($ulBaseClass, ' lv', $calculatedMenuDepth)" />
</xsl:when>
<xsl:when test="$calculatedMenuDepth > 1">
<xsl:value-of select="concat('lv', $calculatedMenuDepth)" />
</xsl:when>-->
</xsl:choose>
</xsl:attribute>
<xsl:if test="$forceHome = 1 and $calculatedMenuDepth = 1">
<!-- Create the class for the li element-->
<li>
<xsl:variable name="isHomeSelected">
<xsl:choose>
<xsl:when test="$currentPage/ancestor-or-self::*[#isDoc][#level=1]/#id = $currentPage/#id">1</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="cssClassConstructor">
<xsl:with-param name="isSelected" select="$isHomeSelected" />
<xsl:with-param name="isSelectedBranch" select="0" />
<xsl:with-param name="hasChildren" select="1" />
<xsl:with-param name="selectedClass" select="$selectedClass" />
<xsl:with-param name="branchClass" select="$branchClass" />
</xsl:call-template>
<a href="{umbraco.library:NiceUrl($currentPage/ancestor-or-self::*[#isDoc][#level=1]/#id)}">
<xsl:call-template name="cssClassConstructor">
<xsl:with-param name="isSelected" select="$isHomeSelected" />
<xsl:with-param name="isSelectedBranch" select="0" />
<xsl:with-param name="hasChildren" select="0" />
<xsl:with-param name="selectedClass" select="$selectedClass" />
<xsl:with-param name="branchClass" select="$branchClass" />
</xsl:call-template>
<!--set the innerText for the a element-->
<xsl:value-of select="$currentPage/ancestor-or-self::*[#isDoc][#level=1]/text()"/>
<xsl:if test="string($currentPage/ancestor-or-self::*[#isDoc][#level=1]/text()) = ''">
<xsl:value-of select="$currentPage/ancestor-or-self::*[#isDoc][#level=1]/#nodeName"/>
</xsl:if>
</a>
</li>
</xsl:if>
<!--End force home-->
<!--for each node in the parent node that is not hidden by Umbraco-->
<xsl:for-each select="$parentNode/*[#isDoc][
string(umbracoNaviHide) != '1'
and ($securityTrimming != '1'
or umbraco.library:IsProtected(#id, #path) = false()
or umbraco.library:HasAccess(#id, #path) = true())
]">
<!--Set the current node id i.e. the node we have looped to not the current page-->
<xsl:variable name="currentNodeID" select="#id" />
<!--Is the node a branch? i.e. are there children and is it in the colletion of ancestor nodes -->
<xsl:variable name="isBranch">
<xsl:choose>
<xsl:when test="$currentPage/ancestor-or-self::*[#isDoc][#id = $currentNodeID]/child::*[#isDoc]">1</xsl:when>
</xsl:choose>
</xsl:variable>
<!--Is the node selected? i.e. is it the same as the currentPage node-->
<xsl:variable name="isSelected">
<xsl:choose>
<xsl:when test="$currentPage/#id = $currentNodeID">1</xsl:when>
<!-- parent selected -->
<xsl:when test="$pseudoCurrentPage/#id = $currentNodeID">1</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="isSelectedBranch">
<xsl:choose>
<xsl:when test="$isBranch = 1 and $selectBranches = 1">1</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="hasChildren">
<xsl:choose>
<xsl:when test="./*[#isDoc]">1</xsl:when>
</xsl:choose>
</xsl:variable>
<li>
<!-- Create the class attribute for the element-->
<xsl:call-template name="cssClassConstructor">
<xsl:with-param name="isSelected" select="$isSelected" />
<xsl:with-param name="isSelectedBranch" select="$isSelectedBranch" />
<xsl:with-param name="hasChildren" select="$hasChildren" />
<xsl:with-param name="selectedClass" select="$selectedClass" />
<xsl:with-param name="branchClass" select="$branchClass" />
</xsl:call-template>
<a href="{umbraco.library:NiceUrl(#id)}">
<xsl:call-template name="cssClassConstructor">
<xsl:with-param name="isSelected" select="$isSelected" />
<xsl:with-param name="isSelectedBranch" select="$isSelectedBranch" />
<xsl:with-param name="hasChildren" select="0" />
<xsl:with-param name="selectedClass" select="$selectedClass" />
<xsl:with-param name="branchClass" select="$branchClass" />
</xsl:call-template>
<!--set the innerText for the a element-->
<xsl:value-of select="./pageTitle/text()"/>
<xsl:if test="string(./pageTitle/text()) = ''">
<xsl:value-of select="#nodeName"/>
</xsl:if>
</a>
<!-- if it's a branch recurse through it's children-->
<xsl:if test="((($isBranch = 1 and $recurse = 1) or ($walkChildren = 1 and $pseudoCurrentPage/descendant-or-self::*[#isDoc][#id = $currentNodeID]/child::*[#isDoc])) and $maxMenuDepth > $calculatedMenuDepth)">
<xsl:call-template name="nodeIterator">
<xsl:with-param name="parentNode" select="." />
<xsl:with-param name="pseudoCurrentPage" select="$pseudoCurrentPage" />
</xsl:call-template>
</xsl:if>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</xsl:template>
<xsl:template name="cssClassConstructor">
<xsl:param name="isSelected"></xsl:param>
<xsl:param name="isSelectedBranch"></xsl:param>
<xsl:param name="hasChildren"></xsl:param>
<xsl:param name="selectedClass"></xsl:param>
<xsl:param name="branchClass"></xsl:param>
<xsl:variable name="class">
<xsl:if test="$isSelected = 1">
<xsl:value-of select="concat($selectedClass,' ')"/>
</xsl:if>
<xsl:if test="$isSelectedBranch = 1">
<xsl:value-of select="concat($branchClass,' ')"/>
</xsl:if>
<xsl:if test="$hasChildren = 1">
<xsl:value-of select="'hasChildren '"/>
</xsl:if>
</xsl:variable>
<xsl:if test="string-length($class) > 0">
<xsl:attribute name="class">
<xsl:value-of select="normalize-space($class)"/>
</xsl:attribute>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The issue you describe above isn't replicating for me (I've checked your source code using Notepad++ and a diff viewer). I see from your code that you're using the CogWorks' Flexible Navigation package. Can you ensure you're using the latest code from here?
Many thanks,
Benjamin
i am converting an html form to xml sequence, i am using a recursive function to achieve this
so the input to param "list" will be of the form
name=value&name=value&name=value
The template below does this fine and returns an xml sequence as follows
<name>value</name><name>value</name><name>value</name>
Ok so the problem
some of the name value pairs are special and i would like to add a attribute to them so the output would be
<name>value</name><name attr="special">value</name><name>value</name>
so to do this i have an external xml file with a list of special names as follows
<settings><google><option from="color"/><option from="size"/></google></settings>
So if we assume i have a xsl variable $SETTINGS connected to this external document above
<xsl:for-each select="$SETTINGS/google/option"></xsl:for-each>
Should be node with 2 children 1 called color and the other size
wot i want to do is if 1 of these children names = $name add the attribute
something like <xsl:if test="$name = $SETTINGS/google/option/ckild name">
<xsl:template name="tokenize">
<xsl:param name="list"/>
<xsl:variable name="seperator" select="'&'"/>
<xsl:variable name="first" select="substring-before(concat($list, $seperator), $seperator)"/>
<xsl:variable name="butfirst" select="substring-after($list, $seperator)"/>
<xsl:variable name="name" select="normalize-space(substring-before($first, '='))"/>
<xsl:variable name="value" select="normalize-space(substring-after($first, '='))"/>
<xsl:if test="string-length($name)>0 and string-length($value)>0">
<xsl:element name="{$name}">
<xsl:for-each select="$SETTINGS/google/option">
-----> <xsl:if test="$name = $SETTINGS/google/option/ckild name">
<xsl:attribute name="option"/>
</xsl:if>
</xsl:for-each>
<xsl:value-of select="$value"/>
</xsl:element>
</xsl:if>
<xsl:if test="$butfirst">
<xsl:call-template name="tokenize">
<xsl:with-param name="list" select="$butfirst"/>
<xsl:with-param name="seperator" select="$seperator"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Many Thanks
Tim Dodgson
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="SETTINGS" select="/settings"/>
<xsl:template match="/">
<xsl:call-template name="tokenize">
<xsl:with-param name="pString"
select="'color=blue&name=value&size=big'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="pString"/>
<xsl:param name="pSeperator" select="'&'"/>
<xsl:choose>
<xsl:when test="not($pString)"/>
<xsl:when test="contains($pString,$pSeperator)">
<xsl:call-template name="tokenize">
<xsl:with-param name="pString"
select="substring-before($pString, $pSeperator)"/>
<xsl:with-param name="pSeperator" select="$pSeperator"/>
</xsl:call-template>
<xsl:call-template name="tokenize">
<xsl:with-param name="pString"
select="substring-after($pString, $pSeperator)"/>
<xsl:with-param name="pSeperator" select="$pSeperator"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vName"
select="normalize-space(substring-before($pString,'='))"/>
<xsl:variable name="vValue"
select="normalize-space(substring-after($pString,'='))"/>
<xsl:if test="$vName and $vValue">
<xsl:element name="{$vName}">
<xsl:if test="$vName = $SETTINGS/google/option/#from">
<xsl:attribute name="attr">special</xsl:attribute>
</xsl:if>
<xsl:value-of select="$vValue"/>
</xsl:element>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
With this input:
<settings>
<google>
<option from="color"/>
<option from="size"/>
</google>
</settings>
Output:
<color attr="special">blue</color>
<name>value</name>
<size attr="special">big</size>
Note: Node set comparison