Replce utf to unicode using xslt with help of database.xml - xslt

I want to replace all utf to unicode as below given example using xslt.
Here many utf entities are in my xml and just want to replace then to unicode using xslt with help of a database file which contains all utf and unicode values as well.
Please refer to below given example.
database.xml:-
<entities>
<entity utf8="°" unicode="x00B0" iso="deg" latin1="176"/>
<entity utf8="í" unicode="x00ED" iso="iacute" latin1="237"/>
<entity utf8="é" unicode="x00E9" iso="eacute" latin1="233"/>
<entity utf8="ó" unicode="x00F3" iso="oacute" latin1="243"/>
<entity utf8="â¢" unicode="x2062" iso="InvisibleTimes" latin1="Not Available"/>
</entities>
input:-
<article>
<documentinfo>
<title lang="eng">First report on the contribution of small-sized species to the copepod community structure of the southern Patagonian shelf (Argentina, 47-55°S)</title>
<author>
<lastname>Julieta</lastname>
<firstname>Carolina</firstname>
<middlename>Antacli</middlename>
<fullname>Carolina Antacli Julieta</fullname>
<corresponding>yes</corresponding>
<email>James#gmail.com</email>
<affiliation>Consejo Nacional de Investigaciones Científicas y Técnicas (CONICET). Av. Rivadavia 1917, C1033AAJ, Buenos Aires, Argentina,</affiliation>
<affiliation>Instituto Nacional de Investigación y Desarrollo Pesquero (INIDEP). Paseo Victoria Ocampo 1, B7602HSA, Mar del Plata, Argentina</affiliation>
<affiliation>Instituto de Investigaciones Marinas y Costeras (IIMYC), CONICET-Universidad Nacional de Mar del Plata, Argentina</affiliation>
</author>
Output:-
<article>
<documentinfo>
<title lang="eng">First report on the contribution of small-sized species to the copepod community structure of the southern Patagonian shelf (Argentina, 47-55°S)</title>
<author>
<lastname>Julieta</lastname>
<firstname>Carolina</firstname>
<middlename>Antacli</middlename>
<fullname>Carolina Antacli Julieta</fullname>
<corresponding>yes</corresponding>
<email>James#gmail.com</email>
<affiliation>Consejo Nacional de Investigaciones Científicas y Técnicas (CONICET). Av. Rivadavia 1917, C1033AAJ, Buenos Aires, Argentina,</affiliation>
<affiliation>Instituto Nacional de Investigación y Desarrollo Pesquero (INIDEP). Paseo Victoria Ocampo 1, B7602HSA, Mar del Plata, Argentina</affiliation>
<affiliation>Instituto de Investigaciones Marinas y Costeras (IIMYC), CONICET-Universidad Nacional de Mar del Plata, Argentina</affiliation>
</author>
</documentinfo>
</article>
You can download all files from:- http://www.stylusstudio.com/SSDN/upload/Entities-Replacement.zip also.

If you want to change the encoding of a file, you better use a tool like iconv. E.g.
iconv -f UTF-8 -t UCS-2LE input_UTF8.xml > output_UCS.xml
This info on internationalization and encoding might be useful as well; http://docstore.mik.ua/orelly/xml/jxslt/ch08_06.htm
That said, character-map does work with the input.xml you've uploaded;
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:character-map name="specialsigns">
<xsl:output-character character="°" string="x00B0"/>
<xsl:output-character character="í" string="x00ED"/>
<xsl:output-character character="é" string="x00E9"/>
<xsl:output-character character="ó" string="x00F3"/>
</xsl:character-map>
<xsl:output indent="yes" method="xml" use-character-maps="specialsigns"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

This is just a modified stylesheet from replace a string with a string with xslt.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vReps" select="document('entities.xml')/entities/*"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="*|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()" name="replace">
<xsl:param name="pText" select="."/>
<xsl:choose>
<xsl:when test="not($vReps/#utf8[contains($pText, .)])">
<xsl:value-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="multiReplace">
<xsl:with-param name="pText" select="$pText"/>
<xsl:with-param name="pReps"
select="$vReps[contains($pText, #utf8)]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="multiReplace">
<xsl:param name="pText"/>
<xsl:param name="pReps"/>
<xsl:choose>
<xsl:when test="$pReps">
<xsl:variable name="escaped">
<xsl:value-of select="concat('&#', $pReps[1]/#unicode, ';')" disable-output-escaping="yes"/>
</xsl:variable>
<xsl:variable name="vRepResult">
<xsl:call-template name="singleReplace">
<xsl:with-param name="pText" select="$pText"/>
<xsl:with-param name="pOld" select="$pReps[1]/#utf8"/>
<xsl:with-param name="pNew" select="$escaped"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="multiReplace">
<xsl:with-param name="pText" select="$vRepResult"/>
<xsl:with-param name="pReps" select="$pReps[position() >1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="singleReplace">
<xsl:param name="pText"/>
<xsl:param name="pOld"/>
<xsl:param name="pNew"/>
<xsl:if test="$pText">
<xsl:choose>
<xsl:when test="not(contains($pText, $pOld))">
<xsl:value-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($pText, $pOld)"/>
<xsl:value-of select="$pNew" disable-output-escaping="yes"/>
<xsl:call-template name="singleReplace">
<xsl:with-param name="pText" select="substring-after($pText, $pOld)"/>
<xsl:with-param name="pOld" select="$pOld"/>
<xsl:with-param name="pNew" select="$pNew"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied to your inputs, I get:
<article>
<documentinfo>
<title lang="eng">First report on the contribution of small-sized species to the copepod community structure of the southern Patagonian shelf (Argentina, 47-55&#x00B0;S)</title>
<author>
<lastname>Julieta</lastname>
<firstname>Carolina</firstname>
<middlename>Antacli</middlename>
<fullname>Carolina Antacli Julieta</fullname>
<corresponding>yes</corresponding>
<email>James#gmail.com</email>
<affiliation>Consejo Nacional de Investigaciones Cient&#x00ED;ficas y T&#x00E9;cnicas (CONICET). Av. Rivadavia 1917, C1033AAJ, Buenos Aires, Argentina,</affiliation>
<affiliation>Instituto Nacional de Investigaci&#x00F3;n y Desarrollo Pesquero (INIDEP). Paseo Victoria Ocampo 1, B7602HSA, Mar del Plata, Argentina</affiliation>
<affiliation>Instituto de Investigaciones Marinas y Costeras (IIMYC), CONICET-Universidad Nacional de Mar del Plata, Argentina</affiliation>
</author>
</documentinfo>
</article>
All credit goes to Dimitre Novatchev.

Related

How to remove duplicate entry - XSLT

I am try to remove duplicate entry after entity § and if contains the , in entry and after tokenize the start-with the ( round bracket then entry e.g (17200(b)(2), (4)–(6)) s/b e.g (<p>17200(b)(2)</p><p>17200(b)(4)–(6)</p>).
Input XML
<root>
<p>CC §1(a), (b), (c)</p>
<p>Civil Code §1(a), (b)</p>
<p>CC §§2(a)</p>
<p>Civil Code §3(a)</p>
<p>CC §1(c)</p>
<p>Civil Code §1(a), (b), (c)</p>
<p>Civil Code §17200(b)(2), (4)–(6), (8), (12), (16), (20), and (21)</p>
</root>
Expected Output
<root>
<sec specific-use="CC">
<title content-type="Sta_Head3">CIVIL CODE</title>
<p>1(a)</p>
<p>1(b)</p>
<p>1(c)</p>
<p>2(a)</p>
<p>3(a)</p>
<p>17200(b)(2)</p>
<p>17200(b)(4)–(6)</p>
<p>17200(b)(8)</p>
<p>17200(b)(12)</p>
<p>17200(b)(16)</p>
<p>17200(b)(20)</p>
<p>17200(b)(21)</p>
</sec>
</root>
XSLT Code
<xsl:template match="root">
<xsl:copy>
<xsl:for-each-group select="p[(starts-with(., 'CC ') or starts-with(., 'Civil Code'))]" group-by="replace(substring-before(., ' §'), 'Civil Code', 'CC')">
<xsl:text>
</xsl:text>
<sec specific-use="{current-grouping-key()}">
<xsl:text>
</xsl:text>
<title content-type="Sta_Head3">CIVIL CODE</title>
<xsl:for-each-group select="current-group()" group-by="replace(substring-after(., '§'), '§', '')">
<xsl:sort select="replace(current-grouping-key(), '[^0-9.].*$', '')" data-type="number" order="ascending"/>
<xsl:for-each
select="distinct-values(
current-grouping-key() !
(let $tokens := tokenize(current-grouping-key(), ', and |, | and ')
return (head($tokens), tail($tokens) ! (substring-before(head($tokens), '(') || .)))
)" expand-text="yes">
<p>{.}</p>
</xsl:for-each>
</xsl:for-each-group>
</sec>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
You could do it like this, in a two-step approach where you first compute the list of existing elements and then use a for-each-group to remove duplicates.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="listP">
<xsl:apply-templates select="root/p"/>
</xsl:variable>
<xsl:for-each-group select="$listP" group-by="p">
<p><xsl:value-of select="current-grouping-key()"/></p>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="p">
<xsl:variable name="input" select="replace(substring-after(.,'§'),'§','')"/>
<xsl:variable name="chapter" select="substring-before($input,'(')"/>
<xsl:for-each select="tokenize(substring-after($input, $chapter),',')">
<p><xsl:value-of select="concat($chapter,replace(replace(.,' ',''),'and',''))"/></p>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/gVrvcxQ

Better way to cycle xsl:for-each letter of the alphabet?

I have a long XML file from which I ned to pull out book titles and other information, then sort it alphabetically, with a separator for each letter. I also need a section for items that don't begin with a letter, say a number or symbol. Something like:
#
1494 - hardcover, $9.99
A
After the Sands - paperback, $24.95
Arctic Spirit - hardcover, $65.00
B
Back to the Front - paperback, $18.95
…
I also need to create a separate list of authors, created from the same data but showing different kinds of information.
How I'm currently doing it
This is simplified, but I basically have this same code twice, once for titles and once for authors. The author version of the template works with different elements and does different things with the data, so I can't use the same template.
<xsl:call-template name="BIP-letter">
<xsl:with-param name="letter" select="'#'" />
</xsl:call-template>
<xsl:call-template name="BIP-letter">
<xsl:with-param name="letter" select="'A'" />
</xsl:call-template>
…
<xsl:call-template name="BIP-letter">
<xsl:with-param name="letter" select="'Z'" />
</xsl:call-template>
<xsl:template name="BIP-letter">
<xsl:param name="letter" />
<xsl:choose>
<xsl:when test="$letter = '#'">
<xsl:text>#</xsl:text>
<xsl:for-each select="//Book[
not(substring(Title,1,1) = 'A') and
not(substring(Title,1,1) = 'B') and
…
not(substring(Title/,1,1) = 'Z')
]">
<xsl:sort select="Title" />
<xsl:appy-templates select="Title" />
<!-- Add other relevant data here -->
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$letter" />
<xsl:for-each select="//Book[substring(Title,1,1) = $letter]">
<xsl:sort select="Title" />
<xsl:appy-templates select="Title" />
<!-- Add other relevant data here -->
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
My questions
The code above works just fine, but:
Manually cycling through each letter gets very long, especially having to do it twice. Is there a way to simplify that? Something like a <xsl:for-each select="[A-Z]"> that I could use to set the parameter when calling the template?
Is there a simpler way to select all titles that don't begin with a letter? Something like //Book[not(substring(Title,1,1) = [A-Z])?
There may be cases where the title or author name starts with a lowercase letter. In the code above, they would get grouped with under the # heading, rather than with the actual letter. The only way I can think to accommodate that—doing it manually—would significantly bloat up the code.
This solution answers all questions asked:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vLowercase" select="'abcdefghijklmnopqrstuvuxyz'"/>
<xsl:variable name="vUppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="vDigits" select="'0123456789'"/>
<xsl:key name="kBookBy1stChar" match="Book"
use="translate(substring(Title, 1, 1),
'abcdefghijklmnopqrstuvuxyz0123456789',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ##########'
)"/>
<xsl:template match="/*">
<xsl:apply-templates mode="firstInGroup" select=
"Book[generate-id()
= generate-id(key('kBookBy1stChar',
translate(substring(Title, 1, 1),
concat($vLowercase, $vDigits),
concat($vUppercase, '##########')
)
)[1]
)
]">
<xsl:sort select="translate(substring(Title, 1, 1),
concat($vLowercase, $vDigits),
concat($vUppercase, '##########')
)"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Book" mode="firstInGroup">
<xsl:value-of select="'
'"/>
<xsl:value-of select="translate(substring(Title, 1, 1),
concat($vLowercase, $vDigits),
concat($vUppercase, '##########')
)"/>
<xsl:apply-templates select=
"key('kBookBy1stChar',
translate(substring(Title, 1, 1),
concat($vLowercase, $vDigits),
concat($vUppercase, '##########')
)
)">
<xsl:sort select="Title"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Book">
<xsl:value-of select="'
'"/>
<xsl:value-of select="concat(Title, ' - ', Binding, ', $', price)"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following xml document (none provided in the question!):
<Books>
<Book>
<Title>After the Sands</Title>
<Binding>paperback</Binding>
<price>24.95</price>
</Book>
<Book>
<Title>Cats Galore: A Compendium of Cultured Cats</Title>
<Binding>hardcover</Binding>
<price>5.00</price>
</Book>
<Book>
<Title>Arctic Spirit</Title>
<Binding>hardcover</Binding>
<price>65.00</price>
</Book>
<Book>
<Title>1494</Title>
<Binding>hardcover</Binding>
<price>9.99</price>
</Book>
<Book>
<Title>Back to the Front</Title>
<Binding>paperback</Binding>
<price>18.95</price>
</Book>
</Books>
the wanted, correct result is produced:
#
1494 - hardcover, $9.99
A
After the Sands - paperback, $24.95
Arctic Spirit - hardcover, $65.00
B
Back to the Front - paperback, $18.95
C
Cats Galore: A Compendium of Cultured Cats - hardcover, $5.00
Explanation:
Use of the Muenchian method for grouping
Use of the standard XPath translate() function
Using mode to process the first book in a group of books starting with the same (case-insensitive) character
Using <xsl:sort> to sort the books in alphabetical orser
The most problematic part is this:
I also need a section for items that don't begin with a letter, say a number or symbol.
If you have a list of all possible symbols that an item can begin with, then you can simply use translate() to convert them all to the # character. Otherwise it gets more complicated. I would try something like:
XSLT 1.0 (+ EXSLT node-set())
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:key name="book" match="Book" use="index" />
<xsl:template match="/Books">
<!-- first-pass: add index char -->
<xsl:variable name="books-rtf">
<xsl:for-each select="Book">
<xsl:copy>
<xsl:copy-of select="*"/>
<index>
<xsl:variable name="index" select="translate(substring(Title, 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:choose>
<xsl:when test="contains('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $index)">
<xsl:value-of select="$index"/>
</xsl:when>
<xsl:otherwise>#</xsl:otherwise>
</xsl:choose>
</index>
</xsl:copy>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="books" select="exsl:node-set($books-rtf)/Book" />
<!-- group by index char -->
<xsl:for-each select="$books[count(. | key('book', index)[1]) = 1]">
<xsl:sort select="index"/>
<xsl:value-of select="index"/>
<xsl:text>
</xsl:text>
<!-- list books -->
<xsl:for-each select="key('book', index)">
<xsl:sort select="Title"/>
<xsl:value-of select="Title"/>
<xsl:text> - </xsl:text>
<xsl:value-of select="Binding"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="Price"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
However, this still leaves the problem of items that begin with a diacritic, e.g. "Österreich" or say a Greek letter. Under this method they too will be clumped under #.
Unfortunately, the only good solution for this is to move to XSLT 2.0.
Demo: https://xsltfiddle.liberty-development.net/jyRYYjj/2

XSLT: Concatenate and select the element

XSLT:
Written my XSLT like this but I'm not getting required output as below that means I want to loop through each record and get frontimagefilename and rearimagefilename for each check.
<xsl:template name="ChequeDetailsTemplate">
<xsl:param name ="vItemcurrIndx" />
<xsl:variable name ="x" select ="$allRecs[$vItemcurrIndx]/Content/*" />
<xsl:call-template name="loop">
<xsl:with-param name="i" select="1"/>
<xsl:with-param name="limit" select="$x/NumberOfCheques"/>
<xsl:with-param name="vItemcurrIndx" select="position()"></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="loop">
<xsl:param name="i"/>
<xsl:param name="limit"/>
<xsl:param name ="vItemcurrIndx" />
<xsl:variable name ="y" select ="$allRecs[$vItemcurrIndx]/Content/*" />
<xsl:if test="$i <= $limit">
<Check>
<CheckAmount><xsl:value-of select="concat(./CourtesyAmount,$i)"/> </CheckAmount>
<FrontImage><xsl:value-of select="concat(./FrontImageFilename,$i)"/></FrontImage>
<RearImage><xsl:value-of select="concat(./RearImageFilename,$i)"/></RearImage>
</Check>
<xsl:call-template name="loop">
<xsl:with-param name="i" select="$i+1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Output looking for:
-<Check><CheckAmount>300</CheckAmount><Codeline><006474< :########## 12345678<</Codeline><FrontImage>C:\Jan292015\archive (1) - Copy\archive\BIN\TempChequeImages\CHQNCR-2VD8NP348TD20150129135318f001.tif</FrontImage><RearImage>C:\Jan292015\archive (1) - Copy\archive\BIN\TempChequeImages\CHQNCR-2VD8NP348TD20150129135318r001.tif</RearImage><CourtesyAmount>300</CourtesyAmount></Check>-<Check><CheckAmount>300</CheckAmount><Codeline><006474< :########## 12345678<</Codeline><FrontImage>C:\Jan292015\archive (1) - Copy\archive\BIN\TempChequeImages\CHQNCR-2VD8NP348TD20150129135320f002.tif</FrontImage><RearImage>C:\Jan292015\archive (1) - Copy\archive\BIN\TempChequeImages\CHQNCR-2VD8NP348TD20150129135320r002.tif</RearImage><CourtesyAmount>300</CourtesyAmount></Check>
But the output which im getting now is:
-<Check><CheckAmount>1</CheckAmount><FrontImage>1</FrontImage><RearImage>1</RearImage></Check>-<Check><CheckAmount>2</CheckAmount><FrontImage>2</FrontImage><RearImage>2</RearImage></Check>
If I am guessing (!) correctly, your input looks something like this:
<ChqDepAppSrvChequeScanComplete>
<CourtesyAmount1>100</CourtesyAmount1>
<FrontImageFilename1 t="String">R:\bin_simulated\Images\TempChequeImages\100f001.bmp</FrontImageFilename1>
<RearImageFilename1 t="String">R:\bin_simulated\Images\TempChequeImages\100r001.bmp</RearImageFilename1>
<RefuseReason1 t="String">None</RefuseReason1>
<CourtesyAmount2>200</CourtesyAmount2>
<FrontImageFilename2 t="String">R:\bin_simulated\Images\TempChequeImages\200f002.bmp</FrontImageFilename2>
<RearImageFilename2 t="String">R:\bin_simulated\Images\TempChequeImages\200r002.bmp</RearImageFilename2>
<RefuseReason2 t="String">None</RefuseReason2>
<NumberOfCheques t="Int32">2</NumberOfCheques>
</ChqDepAppSrvChequeScanComplete>
Your mistake is in the way you have built this expression:
<xsl:value-of select="concat(./CourtesyAmount,$i)"/>
There is no <CourtesyAmount> node in your input, so you are concatenating nothing and $i, resulting in the current value of $i.
What you really want to do here is get the value from a node whose name is the concatenation of the string "CourtesyAmount" and the current value of $i. Something like:
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="/ChqDepAppSrvChequeScanComplete">
<output>
<xsl:call-template name="loop">
<xsl:with-param name="limit" select="NumberOfCheques"/>
</xsl:call-template>
</output>
</xsl:template>
<xsl:template name="loop">
<xsl:param name="i" select="1"/>
<xsl:param name="limit"/>
<xsl:if test="$i <= $limit">
<Check>
<CheckAmount>
<xsl:value-of select="*[local-name()=concat('CourtesyAmount', $i)]"/>
</CheckAmount>
<FrontImage>
<xsl:value-of select="*[local-name()=concat('FrontImageFilename', $i)]"/>
</FrontImage>
<RearImage>
<xsl:value-of select="*[local-name()=concat('RearImageFilename', $i)]"/>
</RearImage>
</Check>
<xsl:call-template name="loop">
<xsl:with-param name="i" select="$i+1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
which when applied to the above input will result in:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<Check>
<CheckAmount>100</CheckAmount>
<FrontImage>R:\bin_simulated\Images\TempChequeImages\100f001.bmp</FrontImage>
<RearImage>R:\bin_simulated\Images\TempChequeImages\100r001.bmp</RearImage>
</Check>
<Check>
<CheckAmount>200</CheckAmount>
<FrontImage>R:\bin_simulated\Images\TempChequeImages\200f002.bmp</FrontImage>
<RearImage>R:\bin_simulated\Images\TempChequeImages\200r002.bmp</RearImage>
</Check>
</output>
Note:
The real problem here is the input. It should not be formatted this way. Things would be much easier if the person ahead of you did a proper job and supplied you with a well-structured XML, for example:
<ChqDepAppSrvChequeScanComplete>
<NumberOfCheques t="Int32">2</NumberOfCheques>
<Cheque>
<CourtesyAmount>100</CourtesyAmount>
<FrontImageFilename t="String">R:\bin_simulated\Images\TempChequeImages\100f001.bmp</FrontImageFilename>
<RearImageFilename t="String">R:\bin_simulated\Images\TempChequeImages\100r001.bmp</RearImageFilename>
<RefuseReason t="String">None</RefuseReason>
</Cheque>
<Cheque>
<CourtesyAmount>200</CourtesyAmount>
<FrontImageFilename t="String">R:\bin_simulated\Images\TempChequeImages\200f002.bmp</FrontImageFilename>
<RearImageFilename t="String">R:\bin_simulated\Images\TempChequeImages\200r002.bmp</RearImageFilename>
<RefuseReason t="String">None</RefuseReason>
</Cheque>
</ChqDepAppSrvChequeScanComplete>
with none of this numbered nodes nonsense.

XSLT - Modifying value of a XML node depending on other nodes

I am new to XSLT programming and struggling with the following issue:
XML:
<All_Results>
<Result>
<url>http://server/sites/sitecoll/library/Folder/NewFolder/test v1.0.docx</url>
<hithighlightedproperties>
<HHUrl>SomeValue1</HHUrl>
</hithighlightedproperties>
<isdocument>True</isdocument>
<serverredirectedurl>SomeValue</serverredirectedurl>
</Result>
<Result>
<url>http://server/sites/sitecoll/library/NewFolder1/test v2.0.docx</url>
<hithighlightedproperties>
<HHUrl>SomeValue1</HHUrl>
</hithighlightedproperties>
<isdocument>True</isdocument>
<serverredirectedurl>SomeValue</serverredirectedurl>
</Result>
<Result>
<url>http://server/sites/sitecoll/library/NewFolder/test v1.0.docx</url>
<hithighlightedproperties>
<HHUrl>SomeValue1</HHUrl>
</hithighlightedproperties>
<isdocument>False</isdocument>
<serverredirectedurl>SomeValue1</serverredirectedurl>
</Result>
......
......
Following is the requirement:
For each "Result" section,
if ("isdocument" node = True),
read the "url" node and get substring after 'library/' in it's value. From this output,
get the substring before the last occurrence of '/'. (Used separate a template to
achieve this) For example, for the first "Result", it will be "Folder/NewFolder".
Finally, concatenate hard-coded strings before and after this output and replace the
values of "HHUrl" and "ServerRedirectUrl" with this final output for every "Result"
under "Results".
Output
<All_Results>
<Result>
<url>http://server/sites/sitecoll/library/Folder/NewFolder/test v1.0.docx</url>
<hithighlightedproperties>
<HHUrl>http://SomeHardCodedString1/Folder/NewFolder/SomeHardCodedString2</HHUrl>
</hithighlightedproperties>
<isdocument>True</isdocument>
<serverredirectedurl>
http://SomeHardCodedString1/Folder/NewFolder/SomeHardCodedString2
</serverredirectedurl>
</Result>
<Result>
<url>http://server/sites/sitecoll/library/NewFolder1/test v2.0.docx</url>
<hithighlightedproperties>
<HHUrl>http://SomeHardCodedString1/NewFolder1/SomeHardCodedString2</HHUrl>
</hithighlightedproperties>
<isdocument>True</isdocument>
<serverredirectedurl>http://SomeHardCodedString1/NewFolder1/SomeHardCodedString2
</serverredirectedurl>
</Result>
<Result>
<url>http://server/sites/sitecoll/library/NewFolder/test v1.0.docx</url>
<hithighlightedproperties>
<HHUrl>SomeValue1</HHUrl>
</hithighlightedproperties>
<isdocument>False</isdocument>
<serverredirectedurl>SomeValue1</serverredirectedurl>
</Result>
......
......
I have trimmed the original XML output to simplify the requirement and have a long complex XLST associated with the original XML. The goal is to modify the "HHUrl" string on the fly before it is rendered as HTML. For this particular requirement, I have created and embedded the following code, which works partially:
<xsl:template name="stripLast">
<xsl:param name="pText"/>
<xsl:param name="pDelim" select="'/'"/>
<xsl:if test="contains($pText, $pDelim)">
<xsl:value-of select="substring-before($pText, $pDelim)"/>
<xsl:if test="contains(substring-after($pText, $pDelim), $pDelim)">
<xsl:value-of select="$pDelim"/>
</xsl:if>
<xsl:call-template name="stripLast">
<xsl:with-param name="pText" select=
"substring-after($pText, $pDelim)"/>
<xsl:with-param name="pDelim" select="$pDelim"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="All_Results/Result/hithighlightedproperties/HHUrl">
<xsl:param name="staticUrl" select=" 'https://SomeHardCodedString1/' "/>
<xsl:copy>
<xsl:variable name="urlValue" select="string(.)"/>
<xsl:variable name="s" select="substring-after($urlValue, 'Portal/')"/>
<xsl:variable name="qsValue">
<xsl:call-template name="stripLast">
<xsl:with-param name="pText" select="$s"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat($staticUrl, $qsValue, 'SomeHardCodedString2')"/>
</xsl:copy>
</xsl:template>
Any help will be highly appreciated.
Thanks,
SharePointDev.
I had a bit of a play around, the following idea might be useful. It will be a bit fragile though, if there are more than two levels in the bit you want to hard-code around (ie. "Folder/", "Folder/Folder1/", (and it breaks if), "Folder/Folder1/Folder2/"), but you could extend the idea:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//HHUrl[ancestor::Result[isdocument[.='True']]]">
<xsl:variable name="url" select="../../url"></xsl:variable>
<xsl:variable name="firsttoken" select="concat(substring-before(substring-after($url,'library/'),'/'),'/')"></xsl:variable>
<xsl:variable name="secondtoken" select="substring-before(substring-after($url,$firsttoken),'/')"></xsl:variable>
<xsl:variable name="thirdtoken" select="concat($firsttoken,$secondtoken)"></xsl:variable>
<HHUrl>http://SomeHardCodedString1/<xsl:if test="$secondtoken!=''"><xsl:value-of select="$thirdtoken"/></xsl:if><xsl:if test="$secondtoken=''"><xsl:value-of select="substring-before($firsttoken,'/')"/></xsl:if>/SomeHardCodedString2</HHUrl>
</xsl:template>
<xsl:template match="//serverredirectedurl[ancestor::Result[isdocument[.='True']]]">
<xsl:variable name="url" select="../url"></xsl:variable>
<xsl:variable name="firsttoken" select="concat(substring-before(substring-after($url,'library/'),'/'),'/')"></xsl:variable>
<xsl:variable name="secondtoken" select="substring-before(substring-after($url,$firsttoken),'/')"></xsl:variable>
<xsl:variable name="thirdtoken" select="concat($firsttoken,$secondtoken)"></xsl:variable>
<serverredirectedurl>http://SomeHardCodedString1/<xsl:if test="$secondtoken!=''"><xsl:value-of select="$thirdtoken"/></xsl:if><xsl:if test="$secondtoken=''"><xsl:value-of select="substring-before($firsttoken,'/')"/></xsl:if>/SomeHardCodedString2</serverredirectedurl>
</xsl:template>

Generate XPath using XPath

I have two separate types of XML documents (one is a.xml & the other is b.xml). Document a.xml, is my main source document, on which I have to run queries. Document b.xml contains all possible information to fetch records from a.xml.
Document: «a.xml»
<rs>
<r id="r1">
<f0>typeA</f0>
<f1>contains value1, value2 and value3</f1>
</r>
<r id="r2">
<f0>typeB</f0>
<f1>contains value4 and value7</f1>
</r>
<r id="r3">
<f0>typeA</f0>
<f1>contains value2 and value5</f1>
</r>
<r id="r4">
<f0>typeC</f0>
<f1>contains value1 and value6</f1>
</r>
<r id="r5">
<f0>typeA</f0>
<f1>contains value5</f1>
</r>
<r id="r6">
<f0>typeC</f0>
<f1>contains value1, value2 and value3</f1>
</r>
</rs>
Document: «b.xml»
<?xml version="1.0"?>
<qs>
<q id="q1">
<i0>typeA</i0>
<i1>value1|value2|value3</i1>
<i2>value18|value35</i2>
<i3>value1|value7</i3>
</q>
<q id="q2">
<i0>typeB</i0>
<i1>value2|value7</i1>
<i2>value9|value20</i2>
<i3>value4</i3>
</q>
</qs>
Now I like to generate dynamic XPath selector strings based on the values of b.xml to be stored in Document c.xml. And it would look like:
Document c.xml
<xps>
<xp id="q1">
<t1>/rs/r[contains(f0,'typeA')
and contains(f1,'value1')
and contains(f1,'value2')
and contains(f1,'value3')]</t1>
<t2>/rs/r[contains(f0,'typeA')
and contains(f1,'value18')
and contains(f1,'value35')]</t2>
<t3>/rs/r[contains(f0,'typeA')
and contains(f1,'value1')
and contains(f1,'value7')]</t3>
</xp>
<xp id="q2">
<t1>/rs/r[contains(f0,'typeB')
and contains(f1,'value2')
and contains(f1,'value7')]</t1>
<t2>/rs/r[contains(f0,'typeA')
and contains(f1,'value9')
and contains(f1,'value20')]</t2>
<t3>/rs/r[contains(f0,'typeA')
and contains(f1,'value4')]</t3>
</xp>
</xps>
If somebody here having any idea, how to do that job in XSLT version 1.0. Thanks in advance.
Here is a solution in XSLT 1.0:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="#*|text()" />
<xsl:template match="/">
<xps>
<xsl:apply-templates/>
</xps>
</xsl:template>
<xsl:template match="q">
<xp id="{#id}">
<xsl:apply-templates/>
</xp>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'i')][not(self::i0)]">
<xsl:element name="t{substring-after(name(), 'i')}">
<xsl:text>/rs/r[contains(f0, '</xsl:text>
<xsl:value-of select="preceding-sibling::i0"/>
<xsl:text>')</xsl:text>
<xsl:call-template name="more-conditions">
<xsl:with-param name="list" select="."/>
</xsl:call-template>
<xsl:text>]</xsl:text>
</xsl:element>
</xsl:template>
<xsl:template name="more-conditions">
<xsl:param name="list"/>
<xsl:param name="delimiter" select="'|'"/>
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<xsl:call-template name="more-conditions">
<xsl:with-param name="list" select="substring-before($list, $delimiter)"/>
</xsl:call-template>
<xsl:call-template name="more-conditions">
<xsl:with-param name="list" select="substring-after($list, $delimiter)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:text> and contains(f1, '</xsl:text>
<xsl:value-of select="$list"/>
<xsl:text>')</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Applied to your input document, it produces the following output:
<xps>
<xp id="q1">
<t1>/rs/r[contains(f0, 'typeA') and contains(f1, 'value1') and contains(f1, 'value2') and contains(f1, 'value3')]</t1>
<t2>/rs/r[contains(f0, 'typeA') and contains(f1, 'value18') and contains(f1, 'value35')]</t2>
<t3>/rs/r[contains(f0, 'typeA') and contains(f1, 'value1') and contains(f1, 'value7')]</t3>
</xp>
<xp id="q2">
<t1>/rs/r[contains(f0, 'typeB') and contains(f1, 'value2') and contains(f1, 'value7')]</t1>
<t2>/rs/r[contains(f0, 'typeB') and contains(f1, 'value9') and contains(f1, 'value20')]</t2>
<t3>/rs/r[contains(f0, 'typeB') and contains(f1, 'value4')]</t3>
</xp>
</xps>
I added some of the whitespace. You can modify the transform based on your needs, but this should get you started.
You cannot use a variable as an xpath selector in xslt version 1, however there are likely other ways you might accomplish this task. If you provide an idea of the problem rather than your intended solution people might be able to help :)