XSLT 3.0 assertion and exception throwing after iteration - xslt-3.0

I have XSLT module containing dependent components: iteration, validation and …
Input example port1.xml
<valuationDocument>
<valuationSet id="val1">
<assetValuation>
<objectReference href="tid-1" />
<quote>
<value>1000000</value>
<sensitivitySet>
<sensitivity name="SEP21">0.707</sensitivity>
<sensitivity name="2Y">1.82</sensitivity>
<sensitivity name="3Y">2.73</sensitivity>
<sensitivity name="4Y">3.68</sensitivity>
<sensitivity name="5Y">4.51</sensitivity>
<sensitivity name="7Y">3.64</sensitivity>
<sensitivity name="10Y">9.06</sensitivity>
<sensitivity name="MAR21">0.315</sensitivity>
<sensitivity name="15Y">13.59</sensitivity>
</sensitivitySet>
</quote>
</assetValuation>
<assetValuation>
<objectReference href="tid-2" />
<quote>
<value>1100000</value>
<sensitivitySet>
<sensitivity name="SEP21">0.947</sensitivity>
<sensitivity name="MAR21">0.502</sensitivity>
<sensitivity name="2Y">1.91</sensitivity>
<sensitivity name="3Y">2.84</sensitivity>
<sensitivity name="4Y">3.72</sensitivity>
<sensitivity name="5Y">4.63</sensitivity>
<sensitivity name="7Y">3.71</sensitivity>
<sensitivity name="10Y">9.15</sensitivity>
<sensitivity name="15Y">13.64</sensitivity>
</sensitivitySet>
</quote>
</assetValuation>
</valuationSet>
</valuationDocument>
I am quasi-satisfied with the iteration component (incapacitating the streaming…). The iteration result is below:
<valuationDocument>
<valuationSet id="val1">
<assetValuation>
<objectReference href="tid-1"/>
<quote>
<value>1000000</value>
<BasisPointValue>
<shiftUp units="100bp" sensitivityName="SEP21" sensitivity="0.707">992,930.00</shiftUp>
<shiftDown units="100bp" sensitivityName="SEP21" sensitivity="0.707">1,007,070.00</shiftDown>
==========================================================================================
<shiftUp units="100bp" sensitivityName="15Y" sensitivity="13.59">864,100.00</shiftUp>
<shiftDown units="100bp" sensitivityName="15Y" sensitivity="13.59">1,135,900.00</shiftDown>
</BasisPointValue>
<sensitivitySet>
<sensitivity name="SEP21">0.707</sensitivity>
<sensitivity name="2Y">1.82</sensitivity>
<sensitivity name="3Y">2.73</sensitivity>
<sensitivity name="4Y">3.68</sensitivity>
<sensitivity name="5Y">4.51</sensitivity>
<sensitivity name="7Y">3.64</sensitivity>
<sensitivity name="10Y">9.06</sensitivity>
<sensitivity name="MAR21">0.315</sensitivity>
<sensitivity name="15Y">13.59</sensitivity>
</sensitivitySet>
</quote>
</assetValuation>
<assetValuation>
<objectReference href="tid-2" />
<quote>
<value>1100000</value>
<BasisPointValue>
<shiftUp units="100bp" sensitivityName="SEP21" sensitivity="0.947">1,089,583.00</shiftUp>
<shiftDown units="100bp" sensitivityName="SEP21" sensitivity="0.947">1,110,417.00</shiftDown>
==========================================================================================
<shiftUp units="100bp" sensitivityName="15Y" sensitivity="13.64">949,960.00</shiftUp>
<shiftDown units="100bp" sensitivityName="15Y" sensitivity="13.64">1,250,040.00</shiftDown>
</BasisPointValue>
<sensitivitySet>
<sensitivity name="SEP21">0.947</sensitivity>
<sensitivity name="MAR21">0.502</sensitivity>
<sensitivity name="2Y">1.91</sensitivity>
<sensitivity name="3Y">2.84</sensitivity>
<sensitivity name="4Y">3.72</sensitivity>
<sensitivity name="5Y">4.63</sensitivity>
<sensitivity name="7Y">3.71</sensitivity>
<sensitivity name="10Y">9.15</sensitivity>
<sensitivity name="15Y">13.64</sensitivity>
</sensitivitySet>
</quote>
</assetValuation>
</valuationSet>
</valuationDocument>
I would like to validate the iteration result as soon as it completes. Should any of the validation fail, the exception is reported and the program is terminated. However, the validation ISN'T played out after the iteration. (The actual validation is to raise exception through xsl:try|catch and terminate if any of the assertion fails)
The validation logic is to compare three metrics of the sensitivitySet/sensitivity with those of iterated & transformed BasisPointValue/shiftUp|shiftDown/#sensitivity
(NOTE: xsl:text is to simulate exception throwing, apparently, the XSLT editor can’t emit the message)
<xsl:variable name="MinE">
<!-- <xsl:message terminate="yes">Fatal Error: Minimum NPV and BPV sensitivity is different</xsl:message>-->
<xsl:text>
</xsl:text>
<xsl:text>Retro: Min NPV and BPV sensitivity</xsl:text>
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:variable name="MaxE">
<!--<xsl:message terminate="yes">Fatal Error: Maximum NPV and BPV sensitivity is different</xsl:message>-->
<xsl:text>
</xsl:text>
<xsl:text>Retro: Max NPV and BPV sensitivity</xsl:text>
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:variable name="AvgDownE">
<!--<xsl:message terminate="yes">Fatal Error: Average NPV and BPV ShiftDown sensitivity is different</xsl:message>-->
<xsl:text>
</xsl:text>
<xsl:text>Retro: Average NPV and BPV ShiftDown</xsl:text>
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:template match="/">
<xsl:source-document href="port1.xml">
<xsl:apply-templates select="valuationDocument" mode="val"/>
</xsl:source-document>
</xsl:template>
<xsl:template match="sensitivitySet" mode="val">
<!-- ITERATION -->
==========================================
<!-- <xsl:apply-imports/>-->
<xsl:copy-of select="."/>
<xsl:apply-templates select="parent::quote" mode="validation"/>
</xsl:template>
<!-- VALIDATION -->
<xsl:template match="quote" mode="validation" expand-text="yes">
<Assertion>
<xsl:variable name="m" as="map(*)">
<xsl:map>
<xsl:map-entry key="'min-npv'" select="min(outermost(descendant::sensitivity)/number())"/>
<xsl:map-entry key="'max-npv'" select="max(outermost(descendant::sensitivity)/number())"/>
<xsl:map-entry key="'min-bpv'" select="min(outermost(descendant::BasisPointValue//#sensitivity)/number())"/>
<xsl:map-entry key="'max-bpv'" select="max(outermost(descendant::BasisPointValue//#sensitivity)/number())"/>
<xsl:map-entry key="'avg-npv'" select="avg(outermost(descendant::sensitivity)/number())"/>
<xsl:map-entry key="'avg-bpv-down'" select="avg(outermost(descendant::BasisPointValue/shiftDown/#sensitivity)/number())"/>
</xsl:map>
</xsl:variable>
<xsl:choose>
<xsl:when test="deep-equal($m('min-npv'), $m('min-bpv'))">
<xsl:value-of select="$MinE"/>
</xsl:when>
<xsl:when test="deep-equal($m('max-npv'), $m('max-bpv'))">
<xsl:value-of select="$MaxE"/>
</xsl:when>
<xsl:when test="deep-equal($m('avg-npv'), $m('avg-bpv-down'))">
<xsl:value-of select="$AvgDownE"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="'Validation Succeeds!'"/></xsl:otherwise>
</xsl:choose>
</Assertion>
</xsl:template>
What is the XSLT3.0 solution of validation after the iteration?

If I understand the question correctly, this is a standard multi-phase transformation. You can either use two separate stylesheets, chained together using some external API (e.g. shell script, Ant/gradle, XProc, Java application) or you can put both phases in the same stylesheet using the logic
<xsl:variable name="temp">
<xsl:apply-templates select="/" mode="phase-1"/>
</xsl:variable>
<xsl:apply-templates select="$temp" mode="phase-2"/>

Related

How to change this VBA/XSLT-based code so the output is written in a single XML-File

I got the following code as a result of a previous question.
The meaning of the code was to iterate through an excelsheet and automatically fill XML-tags with cell contents. The Output was a XML-File for every ROW in this excelsheet.
Now I have a very similar case and I know, that it can't be a lot to change to get it done. I want my code NOT to create a new XML-File for every row, but to fill everything in the same XML-File.
I want to automatically fill in the content of this excel-table:
into an XML-Template that initially looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<Codes>
<AreaCodes>
<Area>
<Name></Name>
<Desc/>
<Facility_Area></Facility_Area>
</Area>
</AreaCodes>
</Codes>
The column FACILITY has to be moved in the Facility_Area-Tag.
The column AREA has to be moved in the Name-Tag.
All the nested tags inside of Area should be repeated.
For my Excel-Example the output should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<Codes>
<AreaCodes>
<Area>
<Name>RA 001</Name>
<Desc/>
<Facility_Area>ZUF</Facility_Area>
</Area>
<Area>
<Name>RA 002</Name>
<Desc/>
<Facility_Area>ZUF</Facility_Area>
</Area>
<Area>
<Name>RA 003</Name>
<Desc/>
<Facility_Area>ZUF</Facility_Area>
</Area>
<Area>
<Name>RA 004</Name>
<Desc/>
<Facility_Area>ZUF</Facility_Area>
</Area>
...
</AreaCodes>
</Codes>
all in one file.
This is the XSLT-Template I have atm:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:param name="facility" />
<xsl:param name="area" />
<!-- IDENTITY TRANSFORM -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Templates -->
<xsl:template match="Codes/AreaCodes/Area/Name">
<xsl:copy>
<xsl:value-of select="$area"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Codes/AreaCodes/Area/Facility_Area">
<xsl:copy>
<xsl:value-of select="$facility"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
and last but not least the VBA-Code in which everything comes together:
Sub Param_XSLT_Process()
...
' LOAD XML AND XSL FILES
xmldoc.async = False
xmldoc.Load "Path\To\Vorlage_AREA.xml"
xslDoc.async = False
xslDoc.setProperty "AllowDocumentFunction", True
xslDoc.Load "Path\To\XSL_SHEET.xsl"
' INITIALIZE NEEDED OBJECTS
Set xslTemp.stylesheet = xslDoc
Set xslProc = xslTemp.createProcessor()
xslProc.input = xmldoc
' ITERATE THROUGH EACH ROW, TRANSFORM, SAVE XML OUTPUT
With ActiveWorkbook.Worksheets(1)
lLastRow = .UsedRange.Rows.Count
For lRow = 2 To lLastRow
xslProc.addParameter "area", CStr(.Cells(lRow, 2).Value) ' ADD PARAMETER(S)
xslProc.addParameter "facility", CStr(.Cells(lRow, 1).Value)
xslProc.transform ' TRANSFORM XML
newDoc.LoadXML xslProc.output ' LOAD RESULT TREE
newDoc.Save "Path\To\Output_" & lRow - 1 & ".xml" ' SAVE OUTPUT TO FILE
Next lRow
End With
...
End Sub
Right now, everything is dropped in a separate XML-File.
Could anyone tell me, what i have to change? I know that, for my VBA-Code, I should move those save-commands outside of the loop, but that doesn't work.
Sorry for struggling with English and thank you all for your help.
I wrote a VBA class module SheetWrapper:
Private mySheet As Object
Sub Init(sheet)
Set mySheet = sheet
End Sub
Public Property Get Cell(rowIndex, cellIndex)
Cell = CStr(mySheet.Cells(rowIndex, cellIndex).Value)
End Property
Then it is possible to use
Sub Param_XSLT_Process()
Dim xmlDoc As New MSXML2.DOMDocument60
Dim xslDoc As New MSXML2.FreeThreadedDOMDocument60
Dim xslTemp As New MSXML2.XSLTemplate60
Dim xslProc As MSXML2.IXSLProcessor
Dim resultDoc As New MSXML2.DOMDocument60
Dim worksheet As Object
Set worksheet = ActiveWorkbook.Worksheets(1)
Dim myWrapper As SheetWrapper
Set myWrapper = New SheetWrapper
myWrapper.Init worksheet
' LOAD XML AND XSL FILES
xmlDoc.async = False
xmlDoc.Load "C:\SomePath\template.xml"
xslDoc.async = False
xslDoc.SetProperty "AllowDocumentFunction", True
xslDoc.Load "C:\SomePath\sheet.xsl"
' INITIALIZE NEEDED OBJECTS
Set xslTemp.stylesheet = xslDoc
Set xslProc = xslTemp.createProcessor()
xslProc.addObject myWrapper, "http://example.com/excel"
xslProc.addParameter "first-row-index", 2, ""
xslProc.addParameter "last-row-index", ActiveWorkbook.Worksheets(1).UsedRange.Rows.Count, ""
xslProc.input = xmlDoc
xslProc.output = resultDoc
xslProc.transform
resultDoc.Save "C:\SomePath\transformation-result.xml"
End Sub
together with the XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:excel="http://example.com/excel"
exclude-result-prefixes="msxsl excel">
<xsl:param name="sheet"/>
<xsl:param name="first-row-index"/>
<xsl:param name="last-row-index"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:output indent="yes"/>
<xsl:template match="AreaCodes">
<xsl:copy>
<xsl:call-template name="make-areas">
<xsl:with-param name="area" select="Area"/>
<xsl:with-param name="index" select="$first-row-index"/>
<xsl:with-param name="last" select="$last-row-index"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="make-areas">
<xsl:param name="area"/>
<xsl:param name="index"/>
<xsl:param name="last"/>
<xsl:apply-templates select="$area">
<xsl:with-param name="row-index" select="$index"/>
</xsl:apply-templates>
<xsl:if test="$index < $last">
<xsl:call-template name="make-areas">
<xsl:with-param name="area" select="$area"/>
<xsl:with-param name="index" select="$index + 1"/>
<xsl:with-param name="last" select="$last"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="Area">
<xsl:param name="row-index"/>
<xsl:copy>
<xsl:apply-templates>
<xsl:with-param name="row-index" select="$row-index"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="Area/Name">
<xsl:param name="row-index"/>
<xsl:copy>
<xsl:value-of select="excel:get-Cell($row-index, 1)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Area/Facility_Area">
<xsl:param name="row-index"/>
<xsl:copy>
<xsl:value-of select="excel:get-Cell($row-index, 2)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I had hoped to be able to pass the Excel sheet object directly to XSLT to read out its cells but somehow MSXML didn't grok that.

XSL to Excel: Choose not processing formula

I declared a variable with a simple Excel formula (not the formula I will eventually use, just something simple for testing)
<xsl:variable name="nistcci" ss:Formula="=RC19"></xsl:variable>
Then I am trying to use a Choose to determine if the attribute data is empty, then return based on that determination.
<Cell ss:StyleID="stig_rules"> <!-- IA Control(s) -->
<Data ss:Type="String">
<xsl:choose>
<xsl:when test="STIG_DATA/VULN_ATTRIBUTE[node()='IA_Controls']/../ATTRIBUTE_DATA != ''">
<xsl:value-of select="STIG_DATA/VULN_ATTRIBUTE[node()='IA_Controls']/../ATTRIBUTE_DATA" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$nistcci"/>
</xsl:otherwise>
</xsl:choose>
</Data>
This works if I do something simple like:
<xsl:variable name="nistcci">sean</xsl:variable>
But isn't working with a formula within a referenced variable.
Thanks for the help. Sean.
It's not clear what you want your variable to contain.
It is quite clear that the xsl:variable instruction cannot have a ss:Formula attribute, or any other attribute except select and (in XSLT 2.0) as.
Your variable needs to be constructed as:
<xsl:variable name="myVar" select="myExpr"/>
where myExpr must be a valid XPath expression.
Alternatively, you could use something like:
<xsl:variable name="myVar">
<xsl:attribute name="ss:Formula">=RC19</xsl:attribute>
</xsl:variable>
to construct a variable that holds an attribute.

XSL parent node data in child node

Source XML:
<COVER_DETAIL>
<COVERDETAILS>
<COVERNAME>AAA</COVERNAME>
<EFFECTIVEDATE>2010-04-30</EFFECTIVEDATE>
<EXPIRYDATE>2022-03-31</EXPIRYDATE>
<COVERAMOUNT/>
<COVERPERCENT/>
<COVERCODE>60</COVERCODE>
</COVERDETAILS>
<COVERDETAILS>
<COVERNAME>BBB</COVERNAME>
<EFFECTIVEDATE>2010-04-30</EFFECTIVEDATE>
<EXPIRYDATE>2022-03-31</EXPIRYDATE>
<COVERAMOUNT/>
<COVERPERCENT/>
<COVERCODE>60</COVERCODE>
</COVERDETAILS>
<COVERDETAILS>
<COVERNAME>CCC</COVERNAME>
<EFFECTIVEDATE>2022-04-01</EFFECTIVEDATE>
<EXPIRYDATE>2032-03-31</EXPIRYDATE>
<COVERAMOUNT/>
<COVERPERCENT>100</COVERPERCENT>
<COVERCODE>62</COVERCODE>
</COVERDETAILS>
<COVERDETAILS>
<COVERNAME>DDD</COVERNAME>
<EFFECTIVEDATE>2022-04-01</EFFECTIVEDATE>
<EXPIRYDATE>2032-03-31</EXPIRYDATE>
<COVERAMOUNT/>
<COVERPERCENT/>
<COVERCODE>85</COVERCODE>
</COVERDETAILS>
</COVER_DETAIL>
XSLT:
<xsl:template name="COVERDETAILS">
<xsl:variable name="i" select="position()" />
<fo:table-row>
<fo:table-cell>
<fo:block>
<xsl:value-of select="$i"/>
<xsl:text>. </xsl:text>
<xsl:value-of select="COVERNAME"/>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block> --- Here i want to display percentage </fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
How can I apply the coverpercent value of covercode 62 to as coverpercent value of all other covers in for each template. I want to apply the coverpecent of cover with covercode 62 to each and every other cover.
You could start by defining a variable as:
<xsl:variable name="perc" select="/COVER_DETAIL/COVERDETAILS[COVERCODE='62']/COVERPERCENT" />
Then use this variable inside your template, where you want to display the percentage as:
<xsl:value-of select="$perc"/>

Xstl global variable set in for each and used after foreach

First of all, I'm new to XSLT.
I'm working with Sharepoint list and I need to get a link to show up if there's data in specific quarters. If there's no data in a certain quarter, I need to have a label that says so.
So what I did is that i created a foreach loop for each data of the same Month of a given year.I know that i cannot re-assing a variable in xslt but I dont know how to do what I want.
Here's a sample of my code. Since I'm working with Sharepoint i dont have acces to the XML. :/
<xsl:variable name="DataQ1" select="'False'"/>
<xsl:variable name="DataQ2" select="'False'"/>
<xsl:variable name="DataQ3" select="'False'"/>
<xsl:variable name="DataQ4" select="'False'"/>
<xsl:for-each select="../Row[generate-id()=generate-id(key('MonthKey', substring(#Date,6,7))[substring('#Date',1,4) = $varYear)][1])]">
<xsl:variable name="currentMonth" select="number(substring(#Date,6,7))"/>
<xsl:choose>
<xsl:when test="$currentMonth >= 1 and $currentMonth $lt;=4">
<!--set $DataQ1 to true-->
</xsl:when>
<xsl:when test="$currentMonth >= 4 and $currentMonth $lt;=7">
<!--set $DataQ2 to true-->
</xsl:when>
<xsl:when test="$currentMonth >= 7 and $currentMonth $lt;=10">
<!--set $DataQ3 to true-->
</xsl:when>
<xsl:otherwise>
<!--set $DataQ4 to true-->
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<div>
<xsl:choose>
<xsl:when test="$DataQ1= 'True'">
<a>
<xsl:attribute name="href">
<xsl:value-of select="www.example.come"/>
</xsl:attribute>
<xsl:value-of select="'LinkToDataofQ1'"/>
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'There's no data for this quarter.'"/>
</xsl:otherwise>
</xsl:choose>
</div>
You use the key function in your example code but you didn't post the declaration of your key. But I think you can achieve what you want with the following code:
<div>
<xsl:choose>
<xsl:when test="../Row[substring(#Date, 1, 4) = $varYear and substring(#Date, 6, 2) >= 1 and substring(#Date, 6, 2) < 4]">
LinkToDataofQ1
</xsl:when>
<xsl:otherwise>There's no data for this quarter.</xsl:otherwise>
</xsl:choose>
</div>
Some other notes:
In your test for Q1 you wrote $currentMonth <= 4. I think what you want is $currentMonth < 4.
To extract the month from #Date you used substring(#Date, 6, 7). The third argument to substring is the length of the substring, not the end index. So you'll probably should write substring(#Date, 6, 2).
Instead of <xsl:value-of select="'string'"/>, you can simply write string.

XSLT finding matching node by comparing element text to substring result

I am processing this XML:
<Brand>
<Brand_Name>BLENDERM</Brand_Name>
<Brand_Code>1103</Brand_Code>
<Groups>
<Group>
<Group_Code>657</Group_Code>
<Parent_Code>0</Parent_Code>
<Group_Level>1</Group_Level>
<Group_Name>Brand Default</Group_Name>
<Product>
<Pip_code>0032359</Pip_code>
<Status>In Use</Status>
Using this XSLT:
<xsl:template match="Product" mode="phase-3">
<xsl:value-of select="document('rx_catmapping.xml')/descendant::mapping[source=substring(ancestor::Brand/Brand_Name,1,1)]/target"/>
</xsl:template>
Here is a sample of rx_catmapping.xml:
<Lookup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<mapping>
<source>a</source>
<target>788</target>
</mapping>
<mapping>
<source>B</source>
<target>789</target>
</mapping>
</Lookup>
So, I am processing the Product element, which is a descendant of Brand. The first letter of Brand/Brand_Name in this case is B, and I am trying to output the value 789 by looking it up in rx_catmapping.xml. This should be really simple but I am completely stumped! I have tried changing the first part of the XPath to refer to document('rx_catmapping.xml')/Lookup/mapping, or document('rx_catmapping.xml')//mapping. I have also tried changing the first half of the comparison to string(source), or to source/text(), but neither of these works either. (The reason for trying this was that using source='B', for example, did seem to work, so I wondered if I was trying to compare two incompatible data types.)
Thanks in advance for your help.
Define a key
<xsl:key name="k1" match="mapping" use="source"/>
then use
<xsl:variable name="map-doc" select="document('rx_catmapping.xml')"/>
and
<xsl:variable name="letter" select="substring(ancestor::Brand/Brand_Name,1,1)"/>
<xsl:for-each select="$map-doc">
<xsl:value-of select="key('k1', $letter)/target"/>
</xsl:for-each>
With XSLT 2.0 you can simplify that to
<xsl:value-of select="key('k1', substring(ancestor::Brand/Brand_Name,1,1), $map-doc)"/>
The problem is, I believe, that at the point where you do ancestor::Brand/Brand_Name, the context is the mapping element in the external file. This worked for me
<xsl:template match="/">
<xsl:apply-templates select=".//Product"/>
</xsl:template>
<xsl:template match="Product">
<xsl:variable name="x" select="substring(ancestor::Brand/Brand_Name,1,1)"/>
<xsl:value-of select="document('file:///c:/temp/rx_catmapping.xml')//mapping[source=$x]/target"/>
</xsl:template>

Resources