I have a SharePoint list with news items. From that, I want to be able to select articles, and change the order of the selected items to make some kind of a newsletter.
I tried using a query-string to specify item-IDs and their order, something like this:
SortFilter.aspx?selection='i13o1,i5o2,i27o3,'
( i is for Item, o is for Order)
The filtering works ok, but I can't figure out how to sort using $elementLookup
<xsl:param name="QUERY_STRING" />
<xsl:variable name="string" select= 'substring-before(substring-after($QUERY_STRING, "'"), "'")'/>
<xsl:key name="myKey" match="Row" use="#ID" />
<xsl:template match="/">
<xsl:for-each select="/dsQueryResponse/Rows/Row[generate-id(.)=generate-id(key('myKey',#ID))]">
<xsl:variable name="articleNumber">
<xsl:text>i</xsl:text><xsl:value-of select="#ID" /><xsl:text>o</xsl:text>
</xsl:variable>
<xsl:variable name="elementLookUp" select= 'substring-before(substring-after($string, $articleNumber), ",")'/>
<xsl:if test="$elementLookUp">
<xsl:value-of select="#ID" />
<xsl:text> </xsl:text>
<xsl:value-of select="#Title" />
<xsl:text> </xsl:text>
<xsl:value-of select="$elementLookUp"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You should be able to do so like this:
<xsl:sort
select="substring-before(substring-after($string, concat('i', #ID, 'o')), ',')"
data-type="number" />
This expression will select the number that comes after i<id>o, and then you can sort on that.
After a bit of tidying, your stylesheet becomes:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="QUERY_STRING" />
<xsl:variable
name="selection"
select='substring-before(substring-after($QUERY_STRING, "selection='"), "'")'/>
<xsl:key name="myKey" match="Row" use="#ID" />
<xsl:template match="/">
<xsl:for-each
select="/dsQueryResponse/Rows/Row[generate-id(.)=generate-id(key('myKey',#ID)[1])]">
<xsl:sort
select="substring-before(substring-after($selection, concat('i', #ID, 'o')), ',')"
data-type="number"
/>
<xsl:variable name="articleNumber" select="concat('i', #ID, 'o')" />
<xsl:variable name="elementLookUp"
select='substring-before(substring-after($selection, $articleNumber), ",")'/>
<xsl:if test="$elementLookUp">
<xsl:value-of select="#ID" />
<xsl:text> </xsl:text>
<xsl:value-of select="#Title" />
<xsl:text> </xsl:text>
<xsl:value-of select="$elementLookUp"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Related
This is my input
<SimpleInput>
<variable1>1</variable1>
<variable2>2</variable2>
<variable3>3</variable3>
</SimpleInput>
This is the output i am geting now
<Classes>
<ClassA>
<input1>overwrite 1</input1>
<input2>2</input2>
<input3>3</input3>
</ClassA>
<ClassB>
<input1>1</input1>
<input2>overwrite 2</input2>
<input3>3</input3>
</ClassB>
</Classes>
This is how i have achieved the above
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:variable name="myVar">
<input1><xsl:value-of select="/SimpleInput/variable1"/></input1>
<input2><xsl:value-of select="/SimpleInput/variable2"/></input2>
<input3><xsl:value-of select="/SimpleInput/variable3"/></input3>
</xsl:variable>
<xsl:template match="/">
<Classes>
<xsl:variable name="paramForClassA">
<input1><xsl:value-of select="'overwrite 1'"/></input1>
<input2><xsl:value-of select="/SimpleInput/variable2"/></input2>
<input3><xsl:value-of select="/SimpleInput/variable3"/></input3>
</xsl:variable>
<ClassA>
<xsl:call-template name="buildSection">
<xsl:with-param name="obj" select="$paramForClassA"/>
</xsl:call-template>
</ClassA>
<ClassB>
<xsl:variable name="paramForClassB">
<input1><xsl:value-of select="/SimpleInput/variable1"/></input1>
<input2><xsl:value-of select="'overwrite 2'"/></input2>
<input3><xsl:value-of select="/SimpleInput/variable3"/></input3>
</xsl:variable>
<xsl:call-template name="buildSection">
<xsl:with-param name="obj" select="$paramForClassB"/>
</xsl:call-template>
</ClassB>
</Classes>
</xsl:template>
<xsl:template name="buildSection">
<xsl:param name="obj" select="()"/>
<xsl:for-each select="$obj/*">
<xsl:element name="{name()}">
<xsl:copy-of select="#*|node()" />
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
But I want to be able to overwrite/create the nodes i want dynamically without rebuilding the entire node tree before passing it to the buildSection template.
Below is an example of the output I am looking for (with an additional input4 on ClassB which i am not able to achieve with the code above)
<Classes>
<ClassA>
<input1>overwrite 1</input1>
<input2>2</input2>
<input3>3</input3>
</ClassA>
<ClassB>
<input1>1</input1>
<input2>overwrite 2</input2>
<input3>3</input3>
<input4>New Item</input4>
</ClassB>
</Classes>
Something similar to what is done here, but i am looking to replace or add the node itself if it doesnt exist.
There are 2 limitations with my current design which i am looking to overcome when passing the parameter to a template call.
Adding new elements to ClassX if necessary
Removing the need to rebuild the entire node tree (There could be over 80 variable in the node tree in some cases)
The build template is actually in a separate file and could be used from multiple callers. If we hard code the node tree building logic in each class (which are also in separate files), if there is a requirement to update the template, then I will need to also update all the callers (which can be upto 30 separate ones). Hope this explains the necessity of the new design.
Ideally something like this when building paramForX
<xsl:variable name="myReplacementsForA">
<input1>overwrite 1</input1>
</xsl:variable>
<xsl:variable name="myReplacementsForB">
<input2>overwrite 2</input2>
<input4>Add 4</input4>
</xsl:variable>
<xsl:variable name="paramForClassA">
<xsl:apply-templates select="$myVar" mode="add-or-replace-value">
<with-param name="replacements" select="$myReplacementsForA"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="paramForClassB">
<xsl:apply-templates select="$myVar" mode="add-or-replace-value">
<with-param name="replacements" select="$myReplacementsForB"/>
</xsl:apply-templates>
</xsl:variable>
I managed to do it this way. Not sure if it is the most efficient
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:variable name="myVar">
<input1><xsl:value-of select="/SimpleInput/variable1"/></input1>
<input2><xsl:value-of select="/SimpleInput/variable2"/></input2>
<input3><xsl:value-of select="/SimpleInput/variable3"/></input3>
</xsl:variable>
<xsl:template match="/">
<Classes>
<xsl:variable name="myReplacementsForA">
<input1>overwrite 1</input1>
</xsl:variable>
<xsl:variable name="paramForClassA">
<xsl:apply-templates mode="buildNewParam">
<xsl:with-param name="original" select="$myVar"/>
<xsl:with-param name="replacements" select="$myReplacementsForA"/>
</xsl:apply-templates>
</xsl:variable>
<ClassA>
<xsl:value-of select="$paramForClassA/item2"/>
<xsl:call-template name="buildSection">
<xsl:with-param name="obj" select="$paramForClassA"/>
</xsl:call-template>
</ClassA>
<ClassB>
<xsl:variable name="myReplacementsForB">
<input2>overwrite 2</input2>
<input4>Add 4</input4>
</xsl:variable>
<xsl:variable name="paramForClassB">
<xsl:apply-templates mode="buildNewParam">
<xsl:with-param name="original" select="$myVar"/>
<xsl:with-param name="replacements" select="$myReplacementsForB"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:call-template name="buildSection">
<xsl:with-param name="obj" select="$paramForClassB"/>
</xsl:call-template>
</ClassB>
</Classes>
</xsl:template>
<xsl:template name="buildSection">
<xsl:param name="obj" select="()"/>
<xsl:for-each select="$obj/*">
<xsl:element name="{name()}">
<xsl:copy-of select="#*|node()" />
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template mode="buildNewParam" match=".">
<xsl:param name="original"/>
<xsl:param name="replacements"/>
<xsl:variable name="merged">
<xsl:for-each select="$original/*">
<xsl:element name="{name()}">
<xsl:copy-of select="#*|node()" />
</xsl:element>
</xsl:for-each>
<xsl:for-each select="$replacements/*">
<xsl:element name="{name()}">
<xsl:copy-of select="#*|node()" />
</xsl:element>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="sorted">
<xsl:for-each select="$merged/*">
<xsl:sort select="name()"/>
<xsl:element name="{name()}">
<xsl:copy-of select="#*|node()" />
</xsl:element>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$sorted/*">
<xsl:if test="name()!=name(following-sibling::node()[1])">
<xsl:element name="{name()}">
<xsl:copy-of select="#*|node()" />
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Following on from my original question:
Filtering, Grouping, Counting and Selecting specific nodes in XML using XSLT 1.0
I've now run into another problem. Using this code that was kindly provided by michael.hor257k
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:key name="vehicle-by-series" match="Vehicle[Model='KA']" use="Series" />
<xsl:template match="/Dealer">
<xsl:for-each select="Vehicle[Model='KA'][count(. | key('vehicle-by-series', Series)[1]) = 1]">
<xsl:value-of select="Model"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="Series"/>
<xsl:text>, </xsl:text>
<xsl:variable name="grp" select="key('vehicle-by-series', Series)" />
<xsl:value-of select="count($grp)"/>
<xsl:text> in stock, starting from </xsl:text>
<xsl:for-each select="$grp">
<xsl:sort select="Price" data-type="number" order="ascending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="Price"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I'm passing in the parameter model:
<xsl:param name="model" />
From an asp.NET URL variable.
I want to substitute the currently hardcoded KA for the model parameter. From the searching I've done you can't use parameters or variables in a key expression in XSLT 1.0. And I don't have the option to upgrade to 2.0. Mores the pity.
Would really appreciate some more help please.
Thanks in advance.
I would suggest you do:
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:param name="model"/>
<xsl:key name="vehicle-by-series" match="Vehicle" use="concat(Model, '|', Series)" />
<xsl:template match="/Dealer">
<xsl:for-each select="Vehicle[Model=$model][count(. | key('vehicle-by-series', concat(Model, '|', Series))[1]) = 1]">
<xsl:value-of select="Model"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="Series"/>
<xsl:text>, </xsl:text>
<xsl:variable name="grp" select="key('vehicle-by-series', concat(Model, '|', Series))" />
<xsl:value-of select="count($grp)"/>
<xsl:text> in stock, starting from </xsl:text>
<xsl:for-each select="$grp">
<xsl:sort select="Price" data-type="number" order="ascending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="Price"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I would like to print path of element and attributes if any along with values using XSLT. e.g
XML :
<root>
<node attr='abc' module='try'>
<subnode>Roshan</subnode>
<subnode>Chetan</subnode>
<subnode>Jennifer</subnode>
</node>
</root>
Output :
/root##
/root/node##
/root/node/#attr##abc
/root/node/#module##try
/root/node/subnode[1]##Roshan
/root/node/subnode[2]##Chetan
/root/node/subnode[3]##Jennifer
I am trying with below snippet, but could only print path of element and it's value
<xsl:template match="*">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/',local-name())" />
<xsl:if
test="(preceding-sibling::*|following-sibling::*)[local-name()=local-name(current())]">
<xsl:value-of
select="concat('[',count(preceding-sibling::*[local-name()=local-name(current())])+1,']')" />
</xsl:if>
<!-- <xsl:call-template name="attrData"></xsl:call-template> -->
</xsl:for-each>
<xsl:text>##</xsl:text>
<xsl:apply-templates select="node()" />
</xsl:template>
I am new to XSLT. Please help!!!!
I made the following XSLT and added also the [position] to the output. You can remove that if you need.
This gives this output:
/root[1]
/root[1]/node[1]
/root[1]/node[1]/#attr[1]##abc
/root[1]/node[1]/#module[1]##try
/root[1]/node[1]/subnode[1]##Roshan
/root[1]/node[1]/subnode[2]##Chetan
/root[1]/node[1]/subnode[3]##Jennifer
With this XSLT. With the two output template you can choose how to print the Xpath.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="" version="2.0">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="/">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="*">
<xsl:call-template name="generateXPath">
<xsl:with-param name="previous" select="''"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="generateXPath">
<xsl:param name="previous" as="xs:string"/>
<xsl:variable name="this" select="." as="node()"/>
<xsl:if test="not(empty(.))">
<xsl:variable name="thisXPath" select="concat($previous, '/', name(.),'[', count(preceding-sibling::*[name() = name($this)])+1,']')"></xsl:variable>
<xsl:apply-templates select="." mode="output">
<xsl:with-param name="previous" select="$previous"/>
</xsl:apply-templates>
<xsl:text>
</xsl:text>
<xsl:for-each select="*|#*">
<xsl:call-template name="generateXPath">
<xsl:with-param name="previous" select="$thisXPath"/>
</xsl:call-template>
</xsl:for-each>
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="output">
<xsl:param name="previous" as="xs:string"/>
<xsl:variable name="this" select="." as="node()"/>
<xsl:variable name="thisXPath">
<xsl:value-of select="concat($previous, '/', name(.),'[', count(preceding-sibling::*[name() = name($this)])+1,']')"></xsl:value-of>
<xsl:if test="not(*)">
<xsl:value-of select="concat('##',text())"></xsl:value-of>
</xsl:if>
</xsl:variable>
<xsl:value-of select="$thisXPath" />
</xsl:template>
<xsl:template match="#*" mode="output">
<xsl:param name="previous" as="xs:string"/>
<xsl:variable name="this" select="." as="node()"/>
<xsl:variable name="thisXPath" select="concat($previous, '/#', name(.),'[', count(preceding-sibling::*[name() = name($this)])+1,']','##',.)"></xsl:variable>
<xsl:value-of select="$thisXPath" />
</xsl:template>
</xsl:stylesheet>
What would be an efficient way to reorder a group of nodes selected using xsl:choose (XSLT 1.0).
Below is the sample source XML:
<Universe>
<CObj>
<Galaxies>
<Galaxy>
<Profile>
<Name>MilkyWay</Name>
<Age>12.5</Age>
</Profile>
<PlanetarySystem>
<Name>Solar</Name>
<Location></Location>
<Planet>
<Name>Earth</Name>
<Satellite>Y</Satellite>
...
...
...
</Planet>
...
...
...
</PlanetarySystem>
<PlanetarySystem>
...
...
...
</PlanetarySystem>
</Galaxy>
<Galaxy>
...
...
...
</Galaxy>
</Galaxies>
</CObj>
</Universe>
XSL snippet:
<xsl:template name="get-galaxy-types">
<xsl:variable name="galaxy_age1" select ="1" />
<xsl:variable name="galaxy_age2" select ="5" />
<xsl:variable name="galaxy_age3" select ="10" />
<xsl:for-each select="Galaxies/Galaxy/Profile/Age">
<xsl:choose>
<xsl:when test=".=$galaxy_age2">
<GalaxyType2>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType2>
</xsl:when>
<xsl:when test=".=$galaxy_age3">
<GalaxyType3>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType3>
</xsl:when>
<xsl:when test=".=$galaxy_age1">
<GalaxyType1>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType1>
</xsl:when>
</xsl:choose>
</xsl:for-each>
Above XSL template is called from main template like:
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:call-template name="get-galaxy-types"/>
</GalaxyTypes>
</xsl:template>
Output XML:
Note that the order of <GalaxyType> cannot be changed.
<Universe>
...
...
...
<GalaxyTypes>
<GalaxyType2>xxxxxx</GalaxyType2>
<GalaxyType3>xxxxxx</GalaxyType3>
<GalaxyType1>xxxxxx</GalaxyType1>
</GalaxyTypes>
...
...
...
</Universe>
Since xsl:choose returns the XML nodes as and when it finds a match I am unable to find a straight forward way to control the order in which I want GalaxyType to appear in the output XML.
How can I have a generic template to perform reordering for any elements that might get added in the future that may fall in to similar requirement. I am fine with having a remapping template within this XSL but I am not really sure how to accomplish this in a really elegant and efficient way.
I am going to guess you want to put the galaxies matching galaxy-age1 first, then the ones matching galaxy-age2 next, and finally the ones for galaxy-age3. I am also assuming the ages specified may not be in ascending order (that is to say, galaxy-age3 could be less that galaxy-age1.
To start with, it might be more natural to do your xsl:for-each over the Galaxy elements
<xsl:for-each select="Galaxies/Galaxy">
Then, to do your customisable sort, you could first define a variable like so...
<xsl:variable name="sortAges"
select="concat('-', $galaxy_age1, '-', $galaxy_age2, '-', $galaxy_age3, '-')" />
Note the order the parameters appear in the concat statement corresponds to the order they need to be output.
Then, your xsl:for-each could look this this...
<xsl:for-each select="Galaxies/Galaxy">
<xsl:sort select="string-length(substring-before($sortAges, concat('-', Profile/Age, '-')))" />
But this is not very elegant. It might be better to simply have a template that matches Galaxy and use three separate xsl:apply-templates to select the Galaxy; one for each age.
Try this XSLT too
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:param name="galaxy_age1" select="1" />
<xsl:param name="galaxy_age2" select="5" />
<xsl:param name="galaxy_age3" select="10" />
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age1]">
<xsl:with-param name="num" select="1" />
</xsl:apply-templates>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age2]">
<xsl:with-param name="num" select="2" />
</xsl:apply-templates>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age3]">
<xsl:with-param name="num" select="3" />
</xsl:apply-templates>
</GalaxyTypes>
</xsl:template>
<xsl:template match="Galaxy">
<xsl:param name="num" />
<xsl:element name="Galaxy{$num}">
<xsl:value-of select="Profile/Name"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
EDIT: To make this more efficient, consider using a key to look up the Galaxy elements by their name. Try this XSLT too
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:param name="galaxy_age1" select="1" />
<xsl:param name="galaxy_age2" select="10" />
<xsl:param name="galaxy_age3" select="5" />
<xsl:key name="galaxy" match="Galaxy" use="Profile/Age" />
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:apply-templates select="key('galaxy', $galaxy_age1)">
<xsl:with-param name="num" select="1" />
</xsl:apply-templates>
<xsl:apply-templates select="key('galaxy', $galaxy_age2)">
<xsl:with-param name="num" select="2" />
</xsl:apply-templates>
<xsl:apply-templates select="key('galaxy', $galaxy_age3)">
<xsl:with-param name="num" select="3" />
</xsl:apply-templates>
</GalaxyTypes>
</xsl:template>
<xsl:template match="Galaxy">
<xsl:param name="num" />
<xsl:element name="Galaxy{$num}">
<xsl:value-of select="Profile/Name"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It's very difficult to navigate between the scattered snippets of your code. Still, it seems to me you should change your strategy to something like:
<xsl:template match="?">
...
<GalaxyTypes>
<xsl:apply-templates select="??/???/Galaxy">
<xsl:sort select="Profile/Age" data-type="number" order="ascending"/>
</xsl:apply-templates=>
</GalaxyTypes>
...
</xsl:template>
<xsl:template match="Galaxy">
<xsl:choose>
<xsl:when test="Profile/Age=$galaxy_age1">
<GalaxyType1>
<xsl:value-of select="Profile/Name"/>
</GalaxyType1>
</xsl:when>
<xsl:when test="Profile/Age=$galaxy_age2">
<GalaxyType2>
<xsl:value-of select="Profile/Name"/>
</GalaxyType2>
</xsl:when>
<xsl:when test="Profile/Age=$galaxy_age3">
<GalaxyType3>
<xsl:value-of select="Profile/Name"/>
</GalaxyType3>
</xsl:when>
</xsl:choose>
</xsl:template>
--
Note that your output would be much better formatted if all galaxies were a uniform <Galaxy> element, with a type attribute to tell them apart.
I've searched far and wide for an example of this, and have so far had no luck in applying a sum template to make my XSTL work.
This is the XML (number of lines varies on each planfeature)
<PlanFeatures>
<PlanFeature name="Line0001">
<CoordGeom>
<Line>
<Start pntRef="7540">5605 8950 1020</Start>
<End pntRef="7541">5605 8951 1019</End>
</Line>
<Line>
<Start pntRef="7541">5605 8951 1019</Start>
<End pntRef="7542">5605 8947 1019</End>
</Line>
<Line>
<Start pntRef="7542">5605 8947 1019</Start>
<End pntRef="7543">5605 8940 1011</End>
</Line>
<Line>
<Start pntRef="7543">5605 8940 1011</Start>
<End pntRef="7544">5605 8931 1020</End>
</Line>
</CoordGeom>
</PlanFeature>
</PlanFeatures>
This is where I'm at with the XSL, which uses a recursive call template to calculate the distance of each line segment.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:landxml="http://www.landxml.org/schema/LandXML-1.2" xmlns:hexagon="http://xml.hexagon.com/schema/HeXML-1.5" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" version="1.0" encoding="UTF-16" indent="no" omit-xml-declaration="yes"/>
<xsl:variable name="XML" select="/"/>
<xsl:variable name="fileExt" select="'txt'"/>
<xsl:variable name="fileDesc" select="'line distance report'"/>
<xsl:template match="/">
<xsl:for-each select="$XML">
<xsl:for-each select="landxml:LandXML/landxml:PlanFeatures/landxml:PlanFeature">
<xsl:value-of select="#name"/><xsl:text>::</xsl:text>
<xsl:for-each select="landxml:CoordGeom/landxml:Line">
<xsl:value-of select="landxml:Start/#pntRef"/><xsl:text>-</xsl:text>
<xsl:variable name="lista" select="landxml:Start"/>
<xsl:variable name="x1" select="substring-before($lista,' ')"/>
<xsl:variable name="yt1" select="substring-after($lista,' ')"/>
<xsl:variable name="y1" select="substring-before($yt1,' ')"/>
<xsl:variable name="z1" select="substring-after($yt1,' ')"/>
<xsl:variable name="listb" select="landxml:End"/>
<xsl:value-of select="landxml:End/#pntRef"/><xsl:text>: </xsl:text>
<xsl:variable name="x2" select="substring-before($listb,' ')"/>
<xsl:variable name="yt2" select="substring-after($listb,' ')"/>
<xsl:variable name="y2" select="substring-before($yt2,' ')"/>
<xsl:variable name="z2" select="substring-after($yt2,' ')"/>
<xsl:variable name="seg" select= "((($x2 - $x1)*($x2 - $x1))+(($y2 - $y1)*($y2 - $y1))+(($z2 - $z1)*($z2 - $z1)))"/>
<xsl:call-template name="root">
<xsl:with-param name="X" select="$seg"/>
</xsl:call-template>
<xsl:text>, </xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template name="root">
<xsl:param name="X"/>
<xsl:param name="xn" select="0"/>
<xsl:param name="xn_1" select="($X+1) div 2"/>
<xsl:choose>
<xsl:when test="string(number($X)) = 'NaN'">
<xsl:value-of select=" ' ' "/>
</xsl:when>
<xsl:when test="($xn_1 - $xn) * ($xn_1 - $xn) < 0.00000001">
<xsl:value-of select='format-number($xn_1, "#.000")'/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="root">
<xsl:with-param name="X" select="$X"/>
<xsl:with-param name="xn" select="$xn_1"/>
<xsl:with-param name="xn_1" select="($xn_1 + ($X div $xn_1)) div 2"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I need to sum the value of X (distance) from the root call template, to create a value which represents the sum of each line segment. I think I need to use a match template, but so far it hand even got close to working.
Currently exporting LINEID::StartPt-EndPt: dist, StartPt-EndPt: dist, etc. I need the sum of the 'dist' to be shown at the end of each line as well. As below
Line0001::7540-7541: 1.414, 7541-7542: 2.000, 7542-7543: 12.042, 7543-7544: 12.720
but I would like
Line0001::7540-7541: 1.414, 7541-7542: 2.000, 7542-7543: 12.042, 7543-7544: 12.728 -- 28.184
Any help would be appreciated... the examples on this site have helped me so much already, but I just can't seem to get through this roadblock.
Cheers,
Chris
You can accomplish this with some relatively simple recursion and parameter passing. Try replacing your first template with these four templates:
<xsl:template match="/">
<xsl:for-each select="$XML">
<xsl:apply-templates
select="landxml:LandXML/landxml:PlanFeatures/landxml:PlanFeature" />
</xsl:for-each>
</xsl:template>
<xsl:template match ="landxml:PlanFeature">
<xsl:value-of select="concat(#name, '::')" />
<xsl:apply-templates select="landxml:CoordGeom/landxml:Line[1]" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="landxml:Line">
<xsl:param name="total" select="0" />
<xsl:value-of
select="concat(landxml:Start/#pntRef, '-', landxml:End/#pntRef, ': ')"/>
<xsl:variable name="len">
<xsl:call-template name="root">
<xsl:with-param name="X">
<xsl:call-template name="seg" />
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$len"/>
<xsl:variable name="next" select="following-sibling::landxml:Line[1]" />
<xsl:variable name="newTot" select="$total + $len" />
<xsl:choose>
<xsl:when test="$next">
<xsl:text>, </xsl:text>
<xsl:apply-templates select="$next">
<xsl:with-param name="total" select="$newTot" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' -- ', format-number($newTot, '#.000'))" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="seg">
<xsl:variable name="lista" select="landxml:Start"/>
<xsl:variable name="x1" select="substring-before($lista,' ')"/>
<xsl:variable name="yt1" select="substring-after($lista,' ')"/>
<xsl:variable name="y1" select="substring-before($yt1,' ')"/>
<xsl:variable name="z1" select="substring-after($yt1,' ')"/>
<xsl:variable name="listb" select="landxml:End"/>
<xsl:variable name="x2" select="substring-before($listb,' ')"/>
<xsl:variable name="yt2" select="substring-after($listb,' ')"/>
<xsl:variable name="y2" select="substring-before($yt2,' ')"/>
<xsl:variable name="z2" select="substring-after($yt2,' ')"/>
<xsl:value-of select= "($x2 - $x1)*($x2 - $x1)+
($y2 - $y1)*($y2 - $y1)+
($z2 - $z1)*($z2 - $z1)"/>
</xsl:template>
When run on your sample input (after adjusting it to match the paths in your XSLT), the result is:
Line0001::7540-7541: 1.414, 7541-7542: 4.000, 7542-7543: 10.630, 7543-7544: 12.728 -- 28.772