Reputation: 205
I have a source XML file as follows:
<section name="Test" code="" type="Table" fundid="15" subtype="SOI1" style="" xmlns="http://composition.bowne.com/2010/v4">
<table xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" accountperiod="2014-07-31" accountperiodtype="0" code="I2" name="Holdings" fundid="15" type="" cols="2">
<colspec colnum="1" colname="1" />
<colspec colnum="2" colname="2" />
<tbody>
<tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="621" hierarchykey="989">
<td colname="1">
<datapoint type="Regular" subtype="" name="Caption" value="Health Care Equipment & Supplies" display="always">Health Care Equipment & Supplies</datapoint>
</td>
<td colname="2">
<datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="16.6" display="always">16.6</datapoint>
</td>
</tr>
<tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="623" hierarchykey="989">
<td colname="1">
<datapoint type="Literal" subtype="Custom" name="All Others*" value="All Others*" display="always">All Others*</datapoint>
</td>
<td colname="2">
<datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="1.5" display="always">[~PONT]1.5[#~PONT]</datapoint> (Line A)
</td>
</tr>
<tr type="otherassets" level="1" itemtype="otherassets" hierarchykey="858">
<td colname="1">
<datapoint type="Literal" subtype="Custom" name="Other Assets" value="Other Assets" display="always">Other Assets</datapoint>
</td>
<td colname="2">
<datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="0.1" display="always">[~PONT]0.1[#~PONT]</datapoint> (Line B)
</td>
</tr>
</tbody>
</table>
</section>
What I would like to do is take the 0.1 between the [~PONT] and [#~PONT] tags from (Line B) and add it to the 1.5 between the [~PONT] and [#~PONT] tags on (Line A), then suppress the node containing (Line B). The resultant XML should look like this:
<section name="Test" code="" type="Table" fundid="15" subtype="SOI1" style="" xmlns="http://composition.bowne.com/2010/v4">
<table xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" accountperiod="2014-07-31" accountperiodtype="0" code="I2" name="Holdings" fundid="15" type="" cols="2">
<colspec colnum="1" colname="1" />
<colspec colnum="2" colname="2" />
<tbody>
<tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="621" hierarchykey="989">
<td colname="1">
<datapoint type="Regular" subtype="" name="Caption" value="Health Care Equipment & Supplies" display="always">Health Care Equipment & Supplies</datapoint>
</td>
<td colname="2">
<datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="16.6" display="always">16.6</datapoint>
</td>
</tr>
<tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="623" hierarchykey="989">
<td colname="1">
<datapoint type="Literal" subtype="Custom" name="All Others*" value="All Others*" display="always">All Others*</datapoint>
</td>
<td colname="2">
<datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="1.6" display="always">[~PONT]1.6[#~PONT]</datapoint>
</td>
</tr>
</tbody>
</table>
</section>
I know I can isolate the numeric values by using something like:
<xsl:variable name="Value1" select="substring-before(substring-after(string(.),'[~PONT]'),'[#~PONT]')"/>
Unfortunately that's about all I do know. I apologise if this question seems rather vague, it's kind of difficult to explain, so please ask me for any more details. Thanks in advance for any advice. I am using XSLT version 1 by the way.
Upvotes: 0
Views: 1398
Reputation: 11416
I'm not sure if this matches the requirements as they are a bit unspecific, but following XSLT
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tab="http://composition.bowne.com/2010/v4" version="1.0">
<xsl:output method="html" doctype-public="XSLT-compat"
omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="tab:datapoint[parent::*/preceding-sibling::*/tab:datapoint[@value='All Others*']]/@value">
<xsl:attribute name="value">
<xsl:value-of select="sum(.) +
sum(//tab:tr[@type='otherassets']/tab:td[2]/tab:datapoint/@value)"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="tab:datapoint[parent::*/preceding-sibling::*/tab:datapoint[@value='All Others*']]/text()">
<xsl:text>[~PONT]</xsl:text>
<xsl:value-of select="sum(./parent::*/@value) + sum(//tab:tr[@type='otherassets']/tab:td[2]/tab:datapoint/@value)"/>
<xsl:text>[#~PONT]</xsl:text>
</xsl:template>
<xsl:template match="tab:tr[@type='otherassets']"/>
</xsl:transform>
when applied to the input XML in the question produces the output
<section xmlns="http://composition.bowne.com/2010/v4" name="Test" code="" type="Table" fundid="15" subtype="SOI1" style="">
<table xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" accountperiod="2014-07-31" accountperiodtype="0" code="I2" name="Holdings" fundid="15" type="" cols="2">
<colspec colnum="1" colname="1"></colspec>
<colspec colnum="2" colname="2"></colspec>
<tbody>
<tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="621" hierarchykey="989">
<td colname="1">
<datapoint type="Regular" subtype="" name="Caption" value="Health Care Equipment & Supplies" display="always">Health Care Equipment & Supplies</datapoint>
</td>
<td colname="2">
<datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="16.6" display="always">16.6</datapoint>
</td>
</tr>
<tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="623" hierarchykey="989">
<td colname="1">
<datapoint type="Literal" subtype="Custom" name="All Others*" value="All Others*" display="always">All Others*</datapoint>
</td>
<td colname="2">
<datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="1.6" display="always">[~PONT]1.6[#~PONT]</datapoint> (Line A)
</td>
</tr>
</tbody>
</table>
For this transformation I added as example namespace in the XSLT for the additional namespace xmlns="http://composition.bowne.com/2010/v4"
of the input XML
xmlns:tab="http://composition.bowne.com/2010/v4"
The template matching
<xsl:template match="tab:tr[@type='otherassets']"/>
is empty and removes this tr
.
The template matching
<xsl:template match="tab:datapoint[parent::*/preceding-sibling::*
/tab:datapoint[@value='All Others*']]/@value">
changes the value of the value
attribute to the sum of this value and the value of the datapoint
of the tr[@type='otherassets']
:
<xsl:attribute name="value">
<xsl:value-of select="sum(.) +
sum(//tab:tr[@type='otherassets']/tab:td[2]/tab:datapoint/@value)"/>
</xsl:attribute>
To change the text, the template matching the text()
of this tr
<xsl:template match="tab:datapoint[parent::*/preceding-sibling::*
/tab:datapoint[@value='All Others*']]/text()">
doesn't use the substring-before()
and substring-after()
as suggested in the question, but instead the values of the value
attributes of the datapoints to get the sum of both values:
<xsl:value-of select="sum(./parent::*/@value) +
sum(//tab:tr[@type='otherassets']/tab:td[2]/tab:datapoint/@value)"/>
This is based on the assumption that the value of the value
attribute is the same as the value in the text between the [~PONT][#~PONT]
.
Update: For the question in the comment how this could be adjusted in case the text in the match patterns wouldn't be hardcoded but parameters:
Following XSLT
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tab="http://composition.bowne.com/2010/v4" version="1.0">
<xsl:output method="html" doctype-public="XSLT-compat"
omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:param name="otherRemove" select="'otherassets'"/>
<xsl:param name="otherKeep" select="'All Others*'"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="tab:datapoint[@name='PercentOfNetAssets']/@value">
<xsl:choose>
<xsl:when test="parent::tab:datapoint/parent::tab:td
/preceding-sibling::*/tab:datapoint[@value=$otherKeep]">
<xsl:attribute name="value">
<xsl:value-of select="sum(.) +
sum(//tab:tr[@type=$otherRemove]/tab:td[2]/tab:datapoint/@value)"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="tab:datapoint[@name='PercentOfNetAssets']/text()">
<xsl:choose>
<xsl:when test="parent::tab:datapoint/parent::tab:td
/preceding-sibling::*/tab:datapoint[@value=$otherKeep]">
<xsl:text>[~PONT]</xsl:text>
<xsl:value-of select="sum(./parent::*/@value) +
sum(//tab:tr[@type=$otherRemove]/tab:td[2]/tab:datapoint/@value)"/>
<xsl:text>[#~PONT]</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="tab:tr">
<xsl:choose>
<xsl:when test="@type=$otherRemove"/>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:transform>
produces the same result.
As adjustments the two parameters
<xsl:param name="otherRemove" select="'otherassets'"/>
<xsl:param name="otherKeep" select="'All Others*'"/>
have been added.
To remove the row, now the template matching all tr
<xsl:template match="tab:tr">
<xsl:choose>
<xsl:when test="@type=$otherRemove"/>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
copies all tr
if they don't have the @type
attribute value $otherRemove
which is otherassets
.
The templates matching text()
and @value
are adjusted in the same way:
<xsl:template match="tab:datapoint[@name='PercentOfNetAssets']/text()">
and
<xsl:template match="tab:datapoint[@name='PercentOfNetAssets']/@value">
Both templates check in an <xsl:choose>
<xsl:when test="parent::tab:datapoint/parent::tab:td
/preceding-sibling::*/tab:datapoint[@value=$otherKeep]">
for the same condition that was the hardcoded template match pattern of the first version as $otherKeep
is All Others*
, adjusts the values of matching elements and copies all other text()
and @value
elements.
Upvotes: 2
Reputation: 111561
tr
you no longer want. In
the example below, I keyed off of @itemtype='otherassets'
.datapoint
to receive the tally. In
the example below, I keyed off of categorykey
, itemtype
, and
colname
. You may wish to adapt/generalize per your full example,
but this criteria works with your sample input and should give you a
feel for what's needed.This XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:b="http://composition.bowne.com/2010/v4">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b:tr[@categorykey='623']/b:td[@colname='2']/b:datapoint">
<xsl:variable name="v1"
select="substring-before(substring-after(string(.),'[~PONT]'),'[#~PONT]')"/>
<xsl:variable name="otherdp"
select="../../../b:tr[@itemtype='otherassets']/b:td[@colname='2']/b:datapoint"/>
<xsl:variable name="v2"
select="substring-before(substring-after(string($otherdp),'[~PONT]'),'[#~PONT]')"/>
<xsl:copy>[~PONT]<xsl:value-of select="$v1 + $v2"/>[#~PONT]</xsl:copy>
</xsl:template>
<xsl:template match="b:tr[@itemtype='otherassets']"/>
</xsl:stylesheet>
Yields the desired XML output:
<?xml version="1.0" encoding="UTF-8"?><section xmlns="http://composition.bowne.com/2010/v4" name="Test" code="" type="Table" fundid="15" subtype="SOI1" style="">
<table xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" accountperiod="2014-07-31" accountperiodtype="0" code="I2" name="Holdings" fundid="15" type="" cols="2">
<colspec colnum="1" colname="1"/>
<colspec colnum="2" colname="2"/>
<tbody>
<tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="621" hierarchykey="989">
<td colname="1">
<datapoint type="Regular" subtype="" name="Caption" value="Health Care Equipment & Supplies" display="always">Health Care Equipment & Supplies</datapoint>
</td>
<td colname="2">
<datapoint type="Regular" subtype="" name="PercentOfNetAssets" value="16.6" display="always">16.6</datapoint>
</td>
</tr>
<tr type="categoryhead" level="1" itemtype="categoryhead" categorykey="623" hierarchykey="989">
<td colname="1">
<datapoint type="Literal" subtype="Custom" name="All Others*" value="All Others*" display="always">All Others*</datapoint>
</td>
<td colname="2">
<datapoint>[~PONT]1.6[#~PONT]</datapoint>
</td>
</tr>
</tbody>
</table>
</section>
Upvotes: 1