XSL conditional formatting - xslt

I am using xsl to transform xml to kml format. I would like to add conditional logic to the xsl to switch the styleUrl based on part of an attribute value. The attribute name is FROM_SYSTEM_ID. The format of the attribute value is "A-123-CAM-1" where "CAM" is part of the string to determine which style definition to use (in this case CAM stands for Camera, CAB stands for Cabinet, etc).
How can I parse this attribute to perform the needed style definition switch?
Following is my xsl template:
<xsl:template match="Line">
<Folder>
<name>
Lines
<!--<xsl:value-of select="#name"/>-->
</name>
<xsl:for-each select="Row">
<Placemark>
<name>
<xsl:value-of select="#FROM_SYSTEM_ID"/>
</name>
<description>
<xsl:value-of select="#TO_SYSTEM_ID"/>
</description>
<styleUrl>#msn_open-diamond00</styleUrl>
<LineString>
<tessellate>1</tessellate>
<coordinates>
<xsl:value-of select="#FromLong"/>,<xsl:value-of select="#FromLat"/>,0 <xsl:value-of select="#ToLong"/>,<xsl:value-of select="#ToLat"/>,0
</coordinates>
</LineString>
</Placemark>
</xsl:for-each>
</Folder>
</xsl:template>
Following is a sample of the XML:
<Line>
<Row PrimaryRoute="A-123" FROM_SYSTEM_ID="A-123-CAB-1"
TO_SYSTEM_ID="A-123-CAM-3" FromLat="42.624948852000"
FromLong="-83.107221652500"
ToLat="42.624940325900" ToLong="-83.107353167000" />
<Row PrimaryRoute="A-123" FROM_SYSTEM_ID="A-123-CAM-1"
TO_SYSTEM_ID="A-123-HH-16" FromLat="42.641662528600"
FromLong="-83.151500129600"
ToLat="42.641709802200" ToLong="-83.151552587600" />
<!-- additional rows here -->
</Line>

You can extract the CAM or CAB portion of the FROM_SYSTEM_ID attribute using a combination of substring-after and substring-before:
<xsl:value-of select="
substring-before(
substring-after(
substring-after(#FROM_SYSTEM_ID, '-'), '-'), '-')"/>
Putting this together with your stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Line">
<Folder>
<name>
Lines
<!--<xsl:value-of select="#name"/>-->
</name>
<xsl:for-each select="Row">
<Placemark>
<name>
<xsl:value-of select="#FROM_SYSTEM_ID"/>
</name>
<description>
<xsl:value-of select="#TO_SYSTEM_ID"/>
</description>
<styleUrl>
<xsl:value-of select="
substring-before(
substring-after(
substring-after(#FROM_SYSTEM_ID, '-'), '-'), '-')"/>
</styleUrl>
<LineString>
<tessellate>1</tessellate>
<coordinates>
<xsl:value-of select="#FromLong"/>,<xsl:value-of select="#FromLat"/>,0 <xsl:value-of select="#ToLong"/>,<xsl:value-of select="#ToLat"/>,0
</coordinates>
</LineString>
</Placemark>
</xsl:for-each>
</Folder>
</xsl:template>
</xsl:stylesheet>
Applied to this input:
<Line>
<Row PrimaryRoute="A-123" FROM_SYSTEM_ID="A-123-CAB-1"
TO_SYSTEM_ID="A-123-CAM-3" FromLat="42.624948852000"
FromLong="-83.107221652500"
ToLat="42.624940325900" ToLong="-83.107353167000" />
<Row PrimaryRoute="A-123" FROM_SYSTEM_ID="A-123-CAM-1"
TO_SYSTEM_ID="A-123-HH-16" FromLat="42.641662528600"
FromLong="-83.151500129600"
ToLat="42.641709802200" ToLong="-83.151552587600" />
</Line>
Produces the following result:
<Folder>
<name>Lines</name>
<Placemark>
<name>A-123-CAB-1</name>
<description>A-123-CAM-3</description>
<styleUrl>CAB</styleUrl>
<LineString>
<tessellate>1</tessellate>
<coordinates>-83.107221652500,42.624948852000,0
-83.107353167000,42.624940325900,0
</coordinates>
</LineString>
</Placemark>
<Placemark>
<name>A-123-CAM-1</name>
<description>A-123-HH-16</description>
<styleUrl>CAM</styleUrl>
<LineString>
<tessellate>1</tessellate>
<coordinates>-83.151500129600,42.641662528600,0
-83.151552587600,42.641709802200,0
</coordinates>
</LineString>
</Placemark>
</Folder>

Related

Optimize XSLT code by eliminating repeating code

This XSLT transformation works, but I am repeating the same code multiple times, which makes it very redundant!
How can I optimize this?
<xsl:for-each select="RVWT">
<xsl:variable name="rvwt" select="tokenize(., '\|')"/>
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">
<xsl:value-of select="$rvwt[1]"/>
</Text>
<TextSourceTitle>
<xsl:value-of select="normalize-space(substring($rvwt[2], 3))"/>
</TextSourceTitle>
</xsl:for-each>
<xsl:if test="not(RVWT)">
<xsl:for-each select="RVW">
<xsl:variable name="rvwt" select="tokenize(., '\|')"/>
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">
<xsl:value-of select="$rvwt[1]"/>
</Text>
<TextSourceTitle>
<xsl:value-of select="normalize-space(substring($rvwt[2], 3))"/>
</TextSourceTitle>
</xsl:for-each>
</xsl:if>
Thanks!
I. I would use the best that XSLT 2.0 can offer: creating a function:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" exclude-result-prefixes="my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:sequence select=
"my:Extract(RVW[not(../RVWT)] | RVWT)"/>
</xsl:template>
<xsl:function name="my:Extract">
<xsl:param name="pItems" as="item()+"/>
<xsl:for-each select="$pItems">
<xsl:variable name="vItemTokens" select="tokenize(., '\|')"/>
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">
<xsl:value-of select="$vItemTokens[1]"/>
</Text>
<TextSourceTitle>
<xsl:value-of select="normalize-space(substring($vItemTokens[2], 3))"/>
</TextSourceTitle>
</xsl:for-each>
</xsl:function>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<t>
<RVWT>a|bbbTail|c</RVWT>
<RVWT>d|eeeTail|f</RVWT>
<RVWT>g|hhhTail|i</RVWT>
<RVW>p|qqqTail|r</RVW>
</t>
the wanted, correct result is produced:
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">a</Text>
<TextSourceTitle>bTail</TextSourceTitle>
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">d</Text>
<TextSourceTitle>eTail</TextSourceTitle>
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">g</Text>
<TextSourceTitle>hTail</TextSourceTitle>
when applied on this document:
<t>
<RVWTX>a|bbbTail|c</RVWTX>
<RVWTX>d|eeeTail|f</RVWTX>
<RVWTX>g|hhhTail|i</RVWTX>
<RVW>p|qqqTail|r</RVW>
</t>
again the wanted, correct result is produced:
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">p</Text>
<TextSourceTitle>qTail</TextSourceTitle>
II. Do note:
With this approach we have the added benefit that the function accepts any sequence of items, not only elements.
For example, one could call the function like this:
my:Extract(('a|bbbTail|c', 'd|eeeTail|f'))
and still get the wanted result:
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">a</Text>
<TextSourceTitle>bTail</TextSourceTitle>
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">d</Text>
<TextSourceTitle>eTail</TextSourceTitle>
You can write a template
<xsl:template match="RVWT | RVW">
<xsl:variable name="rvwt" select="tokenize(., '\|')"/>
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">
<xsl:value-of select="$rvwt[1]"/>
</Text>
<TextSourceTitle>
<xsl:value-of select="normalize-space(substring($rvwt[2], 3))"/>
</TextSourceTitle>
</xsl:template>
and then in the parent you process <xsl:apply-templates select="if (RVWT) then RVWT else RVW"/>.
I am guessing you could do:
<xsl:for-each select="RVWT | RVW[not(../RVWT)]">
<xsl:variable name="rvwt" select="tokenize(., '\|')"/>
<TextTypeCode>08</TextTypeCode>
<Text textformat="05">
<xsl:value-of select="$rvwt[1]"/>
</Text>
<TextSourceTitle>
<xsl:value-of select="normalize-space(substring($rvwt[2], 3))"/>
</TextSourceTitle>
</xsl:for-each>

XSL lookup from a properties (csv) file

I have an XML which needs to be formatted to a CSV. I have chosen XSL to do achieve this. In addition to the XML file, I have a Properties file which needs to be looked through for getting the values for the variables defined in the XML.
Can any one help me in how to do the lookups via an XSL using an external csv file?
CSV file after transformation:
Article.AclFlag|%field.Article.AclFlag.name|false|true|true|||||||||||master-data|%category.MasterData|Integer||0|||Enum.Acls|%enum.Acls.name|%enum.Acls.entry.0;%enum.Acls.entry.1;%enum.Acls.entry.2;%enum.Acls.entry.3;%enum.Acls.entry.4;%enum.Acls.entry.5;%enum.Acls.entry.6;|0;1;2;3;4;5;6;|0;1;2;3;4;5;6;|Article|%entity.Article.name|Product2G Variant
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:key name="kEntity" match="entity-type" use="#identifier" />
<xsl:key name="kCategory" match="category" use="#identifier" />
<xsl:key name="kFieldID" match="field-type" use="#identifier" />
<xsl:key name="kEnumID" match="enum" use="#identifier" />
<xsl:key name="k1" match="entry" use="#key"/>
<xsl:variable name="map-doc" select="document('../transform/Properties.properties')"/>
<xsl:template match="/">
<xsl:apply-templates select="repository/custom/entity/field"/>
</xsl:template>
<xsl:template match="field">
<xsl:copy>
<field>
<xsl:value-of select="#identifier"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="name"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="editable"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="visible"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="visible-from-top"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="max-length"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="exportPurpose"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="importPurpose"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="upper-bound"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="average-length"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="active"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="multiline"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="display-by-default"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="richtext"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="value"/>
<xsl:text>|</xsl:text>
</field>
<category-ref-name>
<xsl:value-of select="key('kCategory', #category-ref)/#identifier"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kCategory', #category-ref)/name"/>
<xsl:text>|</xsl:text>
</category-ref-name>
<field-type-ref>
<xsl:value-of select="substring-after(key('kFieldID', #field-type-ref)/persistence-class-name, 'java.lang.')"/>
<xsl:text>|</xsl:text>
</field-type-ref>
<proxy-entity-ref>
<xsl:value-of select="key('kFieldID', #field-type-ref)/#proxy-ref"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kFieldID', #field-type-ref)/lower-bound"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kFieldID', #field-type-ref)/range-min"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kFieldID', #field-type-ref)/range-max"/>
<xsl:text>|</xsl:text>
</proxy-entity-ref>
<enum-ref-name>
<xsl:value-of select="key('kEnumID', #enum-ref)/#identifier"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kEnumID', #enum-ref)/name"/>
<xsl:text>|</xsl:text>
<xsl:for-each select="key('kEnumID', #enum-ref)/entry">
<xsl:value-of select="#label"/>
<xsl:text>;</xsl:text>
</xsl:for-each>
<xsl:text>|</xsl:text>
<xsl:for-each select="key('kEnumID', #enum-ref)/entry">
<xsl:value-of select="#external-code"/>
<xsl:text>;</xsl:text>
</xsl:for-each>
<xsl:text>|</xsl:text>
<xsl:for-each select="key('kEnumID', #enum-ref)/entry">
<xsl:value-of select="#key"/>
<xsl:text>;</xsl:text>
</xsl:for-each>
<xsl:text>|</xsl:text>
</enum-ref-name>
<entity>
<xsl:value-of select="../#identifier"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="../name"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="../#parentEntities-ref"/>
</entity>
</xsl:copy>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Properties
field.Article.AclFlag.name= Object right type
category.MasterData= Header data
enum.Acls.name=Object right types
The csv file is also not formatted well.
Sample XML
<?xml version="1.0" encoding="UTF-8"?>
<repository>
<types>
<entity-type identifier="ArticleType" identifying-field-type-ref="ArticleType.Id">
<object-name>article</object-name>
<class-name>ArticleType</class-name>
<persistence-xpath>/Article</persistence-xpath>
<persistence-class-name>db.model.Article</persistence-class-name>
<lower-bound>1</lower-bound>
<upper-bound>1</upper-bound>
<field-type identifier="ArticleType.AclFlag">
<object-name>aclFlag</object-name>
<class-name>commons.AclFlags</class-name>
<persistence-xpath>/aclFlag</persistence-xpath>
<persistence-class-name>java.lang.Integer</persistence-class-name>
<fragment-column-access>Article.AclFlag</fragment-column-access>
<internal>true</internal>
<lower-bound>0</lower-bound>
<range-min></range-min>
<range-max></range-max>
<min-length>0</min-length>
</field-type>
</entity-type>
</types>
<custom>
<category identifier="master-data" order="1">
<name>%category.MasterData</name>
</category>
<enum identifier="Enum.Acls">
<name>%enum.Acls.name</name>
<description>%enum.Acls.description</description>
<class-name>com.heiler.ppm.repository.enumerations.StdEnumProvider</class-name>
<key-class-name>commons.AclFlags</key-class-name>
<entry label="%enum.Acls.entry.0" external-code="0" key="0"/>
<entry label="%enum.Acls.entry.1" external-code="1" key="1"/>
<entry label="%enum.Acls.entry.2" external-code="2" key="2"/>
<entry label="%enum.Acls.entry.3" external-code="3" key="3"/>
<entry label="%enum.Acls.entry.4" external-code="4" key="4"/>
<entry label="%enum.Acls.entry.5" external-code="5" key="5"/>
<entry label="%enum.Acls.entry.6" external-code="6" key="6"/>
</enum>
<entity entity-type-ref="ArticleType" identifier="Article" parentEntities-ref="Product2G Variant">
<name>%entity.Article.name</name>
<description>%entity.Article.description</description>
<label-pattern-short>{Article.SupplierAID}</label-pattern-short>
<label-pattern-long>{Article.SupplierAID} - {ArticleLang.DescriptionShort}</label-pattern-long>
<label-pattern-description>{ArticleLang.DescriptionLong}</label-pattern-description>
<field identifier="Article.AclFlag" category-ref="master-data" enum-ref="Enum.Acls" field-type-ref="ArticleType.AclFlag">
<name>%field.Article.AclFlag.name</name>
<description>%field.Article.AclFlag.description</description>
<editable>false</editable>
<visible>true</visible>
<visible-from-top>true</visible-from-top>
<help-context></help-context>
<mergeable>false</mergeable>
</field>
</custom>
</repository>
Sample Properties in XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Default</comment>
<entry key="field.Article.AclFlag.name">Object right type</entry>
<entry key="category.MasterData">Header data</entry>
<entry key="enum.Acls.name">Object right types</entry>
</properties>
Any help is much appreciated.
Given that your properties are of this form
<entry key="field.Article.AclFlag.name">Object right type</entry>
You can define a key to look up entry elements by their key attribute, and I see such a key already exists in your XSLT
<xsl:key name="k1" match="entry" use="#key"/>
Next, ensure you have a variable reference to your external XML document
<xsl:variable name="map-doc" select="document('../transform/Properties.properties.xml')"/>
Then, to look up a value from the properties you can do this (obviously replacing the second argument with the actual value you want to look up):
<xsl:variable name="test" select="'enum.Acls.name'" />
<xsl:value-of select="key('k1', $test, $map-doc)" />
Note this form of the key function, with a third parameter, is only valid in XSLT 2.0. If you were to do <xsl:value-of select="key('k1', $test) />, it would look for the value in the input XML, not your properties XML.
In XSLT 1.0 you could do this to change the document context for the key
<xsl:variable name="test" select="'enum.Acls.name'" />
<xsl:for-each select="$map-doc">
<xsl:value-of select="key('k1', $test)" />
</xsl:for-each>
Alternatively, do it without a key
<xsl:variable name="test" select="'enum.Acls.name'" />
<xsl:value-of select="$map-doc//entry[#key=$test]" />

Getting values after the key colon in xslt v2.0

I've seen many related topics in the internet about getting the value after the colon or keys. So, what I did is, I applied all the possible solution provided by the xslt expert. But, upon combining the codes, I can't get the exact structure of the output. Here is my sample input file:
<Data>
:A:00001
:B:Sample Data Only B
:C:082917
:D2:Sample Data Only D1/090909
:E2:Sample Data Only E1/121212
:E2:Sample Data only E2/232323
:D2:Sample Data Only D2/343434
:E2:Sample Data Only E1/454545
:F:092917
:A:00002
:B:Sample Data Only BB
:C:053017
:D2:Sample Data Only DD2/565656
:E2:Sample Data Only EE1/676767
:F:063017
</Data>
For :C: and :F:, they belong to the same level group <AccountPeriod>, but in my output it created another level group of <AccountPeriod> for :C: and :F:. For every occurrence of :D2:, it should create <Detail> group, and for each occurrence of :E:, it should create <Group> group inside <Trans> group. But, in my current output, it only gets all the value of :D: inside the <Group> elements instead of :E:. And lastly, my current output didn't iterate even if I have 2 occurrence of :A:. The tokenize(.,'\n\n') didn't work in my code.
Here is my XSLT code:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Data">
<xsl:for-each select="tokenize(., '\n\n')">
<Data>
<xsl:analyze-string select="." regex=":([0-9A-Za-z]+):(.*)\n">
<xsl:matching-substring>
<xsl:if test="regex-group(1)='A'">
<Line>
<xsl:value-of select="regex-group(2)"/>
</Line>
</xsl:if>
<xsl:if test="regex-group(1)='B'">
<Number>
<xsl:value-of select="regex-group(2)"/>
</Number>
</xsl:if>
<xsl:variable name="SDate">
<xsl:if test="regex-group(1)='C'">
<xsl:value-of select="regex-group(2)"/>
</xsl:if>
</xsl:variable>
<xsl:variable name="EDate">
<xsl:if test="regex-group(1)='F'">
<xsl:value-of select="regex-group(2)"/>
</xsl:if>
</xsl:variable>
<xsl:if test="not(normalize-space($SDate)='') or not(normalize-space($EDate)='')">
<AccountPeriod>
<StartDate>
<xsl:value-of select="$SDate"/>
</StartDate>
<EndDate>
<xsl:value-of select="$EDate"/>
</EndDate>
</AccountPeriod>
</xsl:if>
</xsl:matching-substring>
</xsl:analyze-string>
<xsl:variable name="temp" as="element()*">
<xsl:analyze-string select="." regex="^:([D2][E2]):(.*)$" flags="m">
<xsl:matching-substring>
<xsl:element name="Detail{regex-group(1)}">
<xsl:value-of select="regex-group(2)"/>
</xsl:element>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:for-each-group select="$temp" group-starting-with="DetailD">
<Detail>
<Seq>
<xsl:value-of select="substring-after(current-group()[1],'/')"/>
</Seq>
<Ext>
<xsl:value-of select="substring-before(current-group()[1],'/')"/>
</Ext>
<Trans>
<xsl:for-each select="current-group()[position() ge 2]">
<Group>
<AcctNo>
<xsl:value-of select="substring-after(.,'/')"/>
</AcctNo>
<Desc>
<xsl:value-of select="substring-before(.,'/')"/>
</Desc>
</Group>
</xsl:for-each>
</Trans>
</Detail>
</xsl:for-each-group>
</Data>
</xsl:for-each>
</xsl:template>
Current Output:
<Data>
<Line>00001</Line>
<Number>Sample Data Only B</Number>
<AccountPeriod>
<StartDate>082917</StartDate>
<EndDate/>
</AccountPeriod>
<AccountPeriod>
<StartDate/>
<EndDate>092917</EndDate>
</AccountPeriod>
<Line>00002</Line>
<Number>Sample Data Only BB</Number>
<AccountPeriod>
<StartDate>053017</StartDate>
<EndDate/>
</AccountPeriod>
<AccountPeriod>
<StartDate/>
<EndDate>063017</EndDate>
</AccountPeriod>
<Detail>
<Seq>090909</Seq>
<Ext>Sample Data Only D1</Ext>
<Trans>
<Group>
<DetailD2>Sample Data Only D2/343434</DetailD2>
</Group>
<Group>
<DetailD2>Sample Data Only DD2/565656</DetailD2>
</Group>
</Trans>
</Detail>
</Data>
Expected output:
<Record>
<Data>
<Line>00001</Line>
<Number>Sample Data Only B</Number>
<AccountPeriod>
<StartDate>082917</StartDate>
<EndDate>092917</EndDate>
</AccountPeriod>
<Detail>
<Seq>090909</Seq>
<Ext>Sample Data Only D1</Ext>
<Trans>
<Group>
<AcctNo>121212</AcctNo>
<Desc>Sample Data Only E1</Desc>
</Group>
<Group>
<AcctNo>232323</AcctNo>
<Desc>Sample Data Only E2</Desc>
</Group>
</Trans>
</Detail>
<Detail>
<Seq>343434</Seq>
<Ext>Sample Data Only D2</Ext>
<Trans>
<Group>
<AcctNo>454545</AcctNo>
<Desc>Sample Data Only E1</Desc>
</Group>
</Trans>
</Detail>
</Data>
<Data>
<Line>00002</Line>
<Number>Sample Data Only BB</Number>
<AccountPeriod>
<StartDate>053017</StartDate>
<EndDate>063017</EndDate>
</AccountPeriod>
<Detail>
<Seq>565656</Seq>
<Ext>Sample Data Only DD2</Ext>
<Trans>
<Group>
<AcctNo>676767</AcctNo>
<Desc>Sample Data Only EE1</Desc>
</Group>
</Trans>
</Detail>
</Data>
Your feedback is much appreciated. Thanks!

XSLT - Problems changing the node context in conjunction with the identity pattern

I have a given source XML document with a structure like this:
<?xml version="1.0" encoding="UTF-8"?>
<sample>
<definition>
<variable>
<name>object01_ID_138368350261919620</name>
<value>NUL</value>
</variable>
<variable>
<name>param01_ID_138368350261919621</name>
<value>10</value>
</variable>
<variable>
<name>param02_ID_138368350261919622</name>
<value>100</value>
</variable>
</definition>
<override>
<assignment>
<name>object01_ID_138368350261919620</name>
<path>module01/object01</path>
</assignment>
<assignment>
<name>param01_ID_138368350261919621</name>
<path>module01/object01/param01</path>
</assignment>
<assignment>
<name>param02_ID_138368350261919622</name>
<path>module01/object01/param02</path>
</assignment>
</override>
</sample>
The characteristic of the source XML is:
Each <assignment> element within the <override> element corresponds to exactly one <variable> element within the <definition> element. This 1:1 relationship is established by the content of their <name> element.
The requirements of the transformation and the target XML are:
Depending on the pattern of the content of the <path> elements, within the <assignment> elements in an <override> element, I like to add a new <assignment> element. Important to note is, that an <assignment> element is the leading information. Therefore always at first, a new <assignment> element with its <path> and <name> content has to be created and in conjunction with that a corresponding new <variable> element with the same <name> content and a specific <value> content has to be created and inserted at last position in the <definition> element. For example, for adding param03, the right result should look like:
<?xml version="1.0" encoding="UTF-8"?>
<sample>
<definition>
<variable>
<name>param00_ID_138368350261919620</name>
<value>NUL</value>
</variable>
<variable>
<name>param01_ID_138368350261919621</name>
<value>10</value>
</variable>
<variable>
<name>param02_ID_138368350261919622</name>
<value>100</value>
</variable>
<variable>
<name>Param03_ID_138368350261919623</name>
<value>1000</value>
</variable>
</definition>
<override>
<assignment>
<name>param00_ID_138368350261919620</name>
<path>module01/object01</path>
</assignment>
<assignment>
<name>param01_ID_138368350261919621</name>
<path>module01/object01/param01</path>
</assignment>
<assignment>
<name>param02_ID_138368350261919622</name>
<path>module01/object01/param02</path>
</assignment>
<assignment>
<name>Param03_ID_138368350261919623</name>
<xpath>module01/object01/param03</xpath>
</assignment>
</override>
</sample>
My XSL 2.0 stylesheet for transformation:
For identity transformation, I have choosen to use the fine-grained control identity rule, recommended by [Dimitre Novatchev]. Applying the processing param03 template, I create a new <assignment> element with its specific <path> and <name> content. Within that template, I like to change the node context by using for-each, to the <definition> element and add at last position a new <variable> element with the corresponding <name> content and a specific <value> content. This stylesheet has been tested with Saxon HE 9.5.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="fo xs fn">
<!--
global declarations ==========================================================
-->
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- randomid here is just a fake for sake of simplification -->
<xsl:variable name="randomid" select="138368350261919623"/>
<!--
template - identity ==========================================================
-->
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<!--
template - variable assignment ===============================================
-->
<xsl:template name="variable_assignment">
<xsl:param name="value_node_name"/>
<xsl:param name="value_node_path"/>
<xsl:message select="'processing: variable assignment'"/>
<xsl:message select="concat('applying name: ', $value_node_name)"/>
<xsl:message select="concat('applying path: ', $value_node_path)"/>
<xsl:call-template name="identity"/>
<assignment>
<name>
<xsl:value-of select="$value_node_name"/>
</name>
<xpath>
<xsl:value-of select="$value_node_path"/>
</xpath>
</assignment>
</xsl:template>
<!--
template - processing param03 =============================================
-->
<xsl:template match="/sample/override[not(assignment
/path[matches(text(), '.*/object01/param03$')])]
/assignment[path[matches(text(), '.*/object01$')]]">
<!-- setting params -->
<xsl:param name="value_node_name_target">
<xsl:value-of select="concat('Param03_ID', '_', $randomid)"/>
</xsl:param>
<xsl:param name="value_node_path_target">
<xsl:value-of select="concat(./path, '/param03')"/>
</xsl:param>
<xsl:param name="value_node_value_target" select="'1000'"/>
<!-- processing variable assignment -->
<xsl:call-template name="variable_assignment">
<xsl:with-param name="value_node_name" select="$value_node_name_target"/>
<xsl:with-param name="value_node_path" select="$value_node_path_target"/>
</xsl:call-template>
<!-- processing variable definition -->
<xsl:for-each select="/sample/definition/*[position()=last()]">
<xsl:message select="'processing: variable definition'"/>
<xsl:message select="concat('Here we are: ', .)"/>
<xsl:message select="concat('applying name: ', $value_node_name_target)"/>
<xsl:message select="concat('applying value: ', $value_node_value_target)"/>
<xsl:call-template name="identity"/>
<variable>
<name>
<xsl:value-of select="$value_node_name_target"/>
</name>
<value>
<xsl:value-of select="$value_node_value_target"/>
</value>
</variable>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The resulting wrong XML is:
<?xml version="1.0" encoding="UTF-8"?>
<sample>
<definition>
<variable>
<name>object01_ID_138368350261919620</name>
<value>NUL</value>
</variable>
<variable>
<name>param01_ID_138368350261919621</name>
<value>10</value>
</variable>
<variable>
<name>param02_ID_138368350261919622</name>
<value>100</value>
</variable>
</definition>
<override>
<assignment>
<name>object01_ID_138368350261919620</name>
<path>module01/object01</path>
</assignment>
<assignment>
<name>param01_ID_138368350261919621</name>
<path>module01/object01/param01</path>
</assignment>
<assignment>
<name>param02_ID_138368350261919622</name>
<path>module01/object01/param02</path>
</assignment>
<assignment>
<name>Param03_ID_138368350261919623</name>
<xpath>module01/object01/param03</xpath>
</assignment>
<variable>
<name>param02_ID_138368350261919622</name>
<value>100</value>
</variable>
<variable>
<name>Param03_ID_138368350261919623</name>
<value>1000</value>
</variable>
</override>
</sample>
The problems I got are:
The node context becomes not changed. The new <variable> element becomes added at last position into the <override> element, instead into the <definition> element, as wanted.
Additionally the last <variable> element from <definition> element becomes copied into the <override> element. That is not what I want.
Help needed!
I really would appreciate if somebody could advice me, in which way I would have to adapt my XSLT in order to get rid of the problems and the right behavior as delineated above.
Many thanks.
The XSLT 2.0 proposed by you, adapted by me:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="fo xs fn">
<!--
global declarations ==========================================================
-->
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- baserandom here is just a fake for sake of simplification -->
<xsl:param name="baserandom" select="138368350261919623"/>
<!--MOVED PARAMS FROM ORIGINAL TEMPLATE HERE SO THEY CAN BE USED BY MULTIPLE TEMPLATES -->
<!--xsl:param name="value_node_path"-->
<!--I LEFT THE PREDICATE BECAUSE IT APPEARS THAT THERE COULD BE MORE THAN ONE override ELEMENT.-->
<!--xsl:value-of select="concat(/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])]
/assignment[1]/path, '/param03')"/>
</xsl:param>
<xsl:param name="value_node_value" select="'1000'"/-->
<!--
template - identity ==========================================================
-->
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<!--
template - definition ========================================================
-->
<!--REPLACES THE xsl:for-each THAT PROCESSES THE VARIABLE DEFINITION-->
<xsl:template match="definition/*[last()]">
<xsl:param name="value_node_name"/>
<xsl:param name="value_node_value"/>
<xsl:call-template name="identity"/>
<xsl:if test="$value_node_name">
<xsl:message select="'processing: variable definition'"/>
<xsl:message select="concat('Here we are: ', .)"/>
<xsl:message select="concat('applying name: ', $value_node_name)"/>
<xsl:message select="concat('applying value: ', $value_node_value)"/>
<variable>
<name>
<xsl:value-of select="$value_node_name"/>
</name>
<value>
<xsl:value-of select="$value_node_value"/>
</value>
</variable>
</xsl:if>
</xsl:template>
<!--
template - processing param03 =============================================
-->
<xsl:template match="/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])]
/assignment[path[matches(text(), '.*/object01$')]]">
<!-- name -->
<xsl:param name="value_node_name">
<xsl:value-of select="concat('param03_ID', '_', $baserandom)"/>
</xsl:param>
<!-- path -->
<xsl:param name="value_node_path">
<xsl:value-of select="concat(./path, '/param03')"/>
</xsl:param>
<!-- value -->
<xsl:param name="value_node_value" select="'1000'"/>
<!-- processing definition -->
<xsl:apply-templates select="/sample/definition/*[last()]">
<xsl:with-param name="value_node_name" select="$value_node_name"/>
<xsl:with-param name="value_node_value" select="$value_node_value"/>
</xsl:apply-templates>
<!-- processing assignment -->
<xsl:message select="'processing: variable assignment'"/>
<xsl:message select="concat('applying name: ', $value_node_name)"/>
<xsl:message select="concat('applying path: ', $value_node_path)"/>
<xsl:call-template name="identity"/>
<assignment>
<name>
<xsl:value-of select="$value_node_name"/>
</name>
<path>
<xsl:value-of select="$value_node_path"/>
</path>
</assignment>
</xsl:template>
</xsl:stylesheet>
The resulting XML (still wrong):
<?xml version="1.0" encoding="UTF-8"?>
<sample>
<definition>
<variable>
<name>object01_ID_138368350261919620</name>
<value>NUL</value>
</variable>
<variable>
<name>param01_ID_138368350261919621</name>
<value>10</value>
</variable>
<variable>
<name>param02_ID_138368350261919622</name>
<value>100</value>
</variable>
</definition>
<override>
<variable>
<name>param02_ID_138368350261919622</name>
<value>100</value>
</variable>
<variable>
<name>param03_ID_138368350261919623</name>
<value>1000</value>
</variable>
<assignment>
<name>object01_ID_138368350261919620</name>
<path>module01/object01</path>
</assignment>
<assignment>
<name>param01_ID_138368350261919621</name>
<path>module01/object01/param01</path>
</assignment>
<assignment>
<name>param02_ID_138368350261919622</name>
<path>module01/object01/param02</path>
</assignment>
<assignment>
<name>param03_ID_138368350261919623</name>
<path>module01/object01/param03</path>
</assignment>
</override>
</sample>
The issue you are having is that xsl:for-each isn't changing the output context. It's only changing the iteration context.
You are still outputting in the context of assignment (template match) when you're iterating over xsl:for-each select="/sample/definition/*[position()=last()].
You'll need to output the new variable from the context of definition.
I've modified your XSLT to produce what you want. It might not be the final solution, but should get you closer. I added comments (all uppercase) to try to explain what I changed. Please let me know if there are questions.
Modified XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="fo xs fn">
<!--
global declarations ==========================================================
-->
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- randomid here is just a fake for sake of simplification -->
<xsl:param name="randomid" select="138368350261919623"/>
<!--MOVED PARAMS FROM ORIGINAL TEMPLATE HERE SO THEY CAN BE USED BY MULTIPLE TEMPLATES -->
<xsl:param name="value_node_name_target">
<xsl:value-of select="concat('Param03_ID', '_', $randomid)"/>
</xsl:param>
<xsl:param name="value_node_path_target">
<!--I LEFT THE PREDICATE BECAUSE IT APPEARS THAT THERE COULD BE MORE THAN ONE override ELEMENT.-->
<xsl:value-of select="concat(/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])]
/assignment[1]/path, '/param03')"/>
</xsl:param>
<xsl:param name="value_node_value_target" select="'1000'"/>
<!--
template - identity ==========================================================
-->
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()[1]"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<!--
template - variable assignment ===============================================
-->
<!--MOVED CODE FROM THIS TEMPLATE INTO THE assignment TEMPLATE-->
<!--REPLACES THE xsl:for-each THAT PROCESSES THE VARIABLE DEFINITION-->
<xsl:template match="definition[../override[not(assignment/path[matches(text(), '.*/object01/param03$')])]
/assignment[path[matches(text(), '.*/object01$')]]]/*[last()]">
<xsl:message select="'processing: variable definition'"/>
<xsl:message select="concat('Here we are: ', .)"/>
<xsl:message select="concat('applying name: ', $value_node_name_target)"/>
<xsl:message select="concat('applying value: ', $value_node_value_target)"/>
<xsl:call-template name="identity"/>
<variable>
<name>
<xsl:value-of select="$value_node_name_target"/>
</name>
<value>
<xsl:value-of select="$value_node_value_target"/>
</value>
</variable>
</xsl:template>
<!--
template - processing param03 =============================================
-->
<xsl:template match="/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])]
/assignment[path[matches(text(), '.*/object01$')]]">
<!-- setting params -->
<!--MOVED TEMPLATE PARAMS TO GLOBAL PARAMS-->
<!-- processing variable assignment -->
<!--REPLACED UNNECESSARY xsl:call-template WITH ACTUAL CODE-->
<xsl:message select="'processing: variable assignment'"/>
<xsl:message select="concat('applying name: ', $value_node_name_target)"/>
<xsl:message select="concat('applying path: ', $value_node_path_target)"/>
<xsl:call-template name="identity"/>
<assignment>
<name>
<xsl:value-of select="$value_node_name_target"/>
</name>
<!--CHANGED FROM xpath TO path (APPEARED TO BE A TYPO)-->
<path>
<xsl:value-of select="$value_node_path_target"/>
</path>
</assignment> <!-- processing variable definition -->
<!--THIS IS NOW DONE BY A SEPARATE MATCHING TEMPLATE-->
</xsl:template>
</xsl:stylesheet>
Output
<sample>
<definition>
<variable>
<name>object01_ID_138368350261919620</name>
<value>NUL</value>
</variable>
<variable>
<name>param01_ID_138368350261919621</name>
<value>10</value>
</variable>
<variable>
<name>param02_ID_138368350261919622</name>
<value>100</value>
</variable>
<variable>
<name>Param03_ID_138368350261919623</name>
<value>1000</value>
</variable>
</definition>
<override>
<assignment>
<name>object01_ID_138368350261919620</name>
<path>module01/object01</path>
</assignment>
<assignment>
<name>param01_ID_138368350261919621</name>
<path>module01/object01/param01</path>
</assignment>
<assignment>
<name>param02_ID_138368350261919622</name>
<path>module01/object01/param02</path>
</assignment>
<assignment>
<name>Param03_ID_138368350261919623</name>
<path>module01/object01/param03</path>
</assignment>
</override>
</sample>

XSL to create folders based on attribute

I am using XSL to transform XML to KML to be viewed in Google Earth. I would to be able to create folders for each "IT_Type" in the XML sample below
Currently, the XML is being transformed into a folder structure like this:
Point
VSS1
VSS2
Cab1
Cab2
DMS1
DMS2
Line
From:To:
From:To:
From:To:
It needs to be structured into folders like this, with a folder for each IT_Type under the Point and Line parent folders.
Point
VSS
VSS1
VSS2
Cabinet
Cab1
Cab2
DMS
DMS1
DMS2
Line
Handhole
From:To:
Cabinet
From:To:
Other
From:To:
What is the best way to go about setting up the XSL for this? Will performance be an issue on large data sets?
Any advice or code sample is appreciated.
Thank you.
Please see below for sample XML
<Parents>
<Point>
<Row IT_ID="116" IT_Name="VSS1" IT_Type="VSS" GPSLat="43.953000000000" GPSLong="-85.671800000000" />
<Row IT_ID="117" IT_Name="VSS2" IT_Type="VSS" GPSLat="43.966900000000" GPSLong="-85.678900000000" />
<Row IT_ID="122" IT_Name="Cab1" IT_Type="Cabinet" GPSLat="43.903100000000" GPSLong="-85.677100000000" />
<Row IT_ID="123" IT_Name="Cab2" IT_Type="Cabinet" GPSLat="43.913500000000" GPSLong="-85.677300000000" />
<Row IT_ID="254" IT_Name="DMS1" IT_Type="DMS" GPSLat="43.903100000000" GPSLong="-85.677100000000" />
<Row IT_ID="255" IT_Name="DMS2" IT_Type="DMS" GPSLat="43.989400000000" GPSLong="-85.676800000000" />
</Point>
<Line>
<Row LINE_ID="1117" IT_Type="Handhole" FROM_SYSTEM_ID="2127" TO_SYSTEM_ID="1947" FromLat="43.438474034300" FromLong="-83.195331982500" ToLat="43.437072542900" ToLong="-83.193657308800">
<Row2 LINE_ID="1117" CONDUIT_NUMBER="1" CONDUIT_TYPE="Fiber" FIBER_NUMBER="1" FIBER_TYPE="Trunk" STRANDS="96" />
<Row2 LINE_ID="1117" CONDUIT_NUMBER="2" CONDUIT_TYPE="Empty" FIBER_NUMBER="" FIBER_TYPE="" STRANDS="" />
<Row2 LINE_ID="1117" CONDUIT_NUMBER="3" CONDUIT_TYPE="Empty" FIBER_NUMBER="" FIBER_TYPE="" STRANDS="" />
<Row2 LINE_ID="1117" CONDUIT_NUMBER="4" CONDUIT_TYPE="Empty" FIBER_NUMBER="" FIBER_TYPE="" STRANDS="" />
</Row>
<Row LINE_ID="997" IT_Type="Cabinet" FROM_SYSTEM_ID="2011" TO_SYSTEM_ID="2012" FromLat="43.482705558800" FromLong="-83.260130135400" ToLat="43.482694479700" ToLong="-83.260107590500">
<Row2 LINE_ID="997" CONDUIT_NUMBER="1" CONDUIT_TYPE="Other" FIBER_NUMBER="" FIBER_TYPE="" STRANDS="" />
</Row>
<Row LINE_ID="1220" IT_Type="Other" FROM_SYSTEM_ID="2415" TO_SYSTEM_ID="2413" FromLat="43.624664303600" FromLong="-83.086848805700" ToLat="43.624645615600" ToLong="-83.086770805500">
<Row2 LINE_ID="1220" CONDUIT_NUMBER="1" CONDUIT_TYPE="Fiber" FIBER_NUMBER="1" FIBER_TYPE="Dist" STRANDS="12" />
<Row2 LINE_ID="1220" CONDUIT_NUMBER="2" CONDUIT_TYPE="Electric" FIBER_NUMBER="" FIBER_TYPE="" STRANDS="" />
</Row>
</Line>
</Parents>
Please see below for my XSL:
<xsl:template match="Parents">
<Folder>
<name>
Point
</name>
<xsl:for-each select="Point/Row">
<Placemark>
<name>
<xsl:value-of select="#IT_Name"/>
</name>
<description>
<xsl:value-of select="#Location"/>
</description>
<styleUrl>
<xsl:value-of select="concat($hash,#IT_Type)"/>
</styleUrl>
<Point>
<coordinates>
<xsl:value-of select="#GPSLong"/>,
<xsl:value-of select="#GPSLat"/>
</coordinates>
</Point>
</Placemark>
</xsl:for-each>
</Folder>
<Folder>
<name>
Line
</name>
<xsl:for-each select="Line/Row">
<Placemark>
<name>
From: <xsl:value-of select="#FROM_SYSTEM_ID"/> to: <xsl:value-of select="#TO_SYSTEM_ID"/>
</name>
<description>
<xsl:value-of select="#CONDUIT_NUMBER"/>
</description>
<styleUrl>
<xsl:value-of select="concat($hash,#IT_Type)"/>
</styleUrl>
<LineString>
<tessellate>1</tessellate>
<coordinates>
<xsl:value-of select="#FromLong"/>,<xsl:value-of select="#FromLat"/>,0 <xsl:value-of select="#ToLong"/>,<xsl:value-of select="#ToLat"/>,0
</coordinates>
</LineString>
</Placemark>
</xsl:for-each>
</Folder>
</xsl:template>
Sorry for having completely revised your initial template, but the common way to do this (in XSLT 1.0) is by applying Meunchian's method on a multi-level grouping. In your specific case, you can create a xsl:key based on concatenation of #IT_Type and the parent element of Row.
For instance, this XSLT 1.0 (tested under Saxon 6.5)
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kIT_Type"
match="Row"
use="concat(
name(parent::node()),#IT_Type
)"/>
<xsl:template match="Parents/*">
<Folder>
<name><xsl:value-of select="name()"/></name>
<xsl:apply-templates select="Row[
generate-id(.)
=
generate-id(key('kIT_Type',
concat(name(parent::node()),#IT_Type))[1])
]"/>
</Folder>
</xsl:template>
<xsl:template match="Row">
<Folder>
<name><xsl:value-of select="#IT_Type"/></name>
<xsl:apply-templates select="key('kIT_Type',
concat(name(parent::node()),#IT_Type))"
mode="placemark"/>
</Folder>
</xsl:template>
<xsl:template match="Row[parent::Point]" mode="placemark">
<Placemark>
<name>
<xsl:value-of select="#IT_Name"/>
</name>
<description>
<!--xsl:value-of select="#Location"/-->
</description>
<styleUrl>
<!--xsl:value-of select="concat($hash,#IT_Type)"/-->
</styleUrl>
<Point>
<coordinates>
<xsl:value-of select="#GPSLong"/>,
<xsl:value-of select="#GPSLat"/>
</coordinates>
</Point>
</Placemark>
</xsl:template>
<xsl:template match="Row[parent::Line]" mode="placemark">
<Placemark>
<name>
From: <xsl:value-of select="#FROM_SYSTEM_ID"/> to: <xsl:value-of select="#TO_SYSTEM_ID"/>
</name>
<description>
<xsl:value-of select="#CONDUIT_NUMBER"/>
</description>
<styleUrl>
<!-- xsl:value-of select="concat($hash,#IT_Type)"/-->
</styleUrl>
<LineString>
<tessellate>1</tessellate>
<coordinates>
<xsl:value-of select="#FromLong"/>,<xsl:value-of select="#FromLat"/>,0 <xsl:value-of select="#ToLong"/>,<xsl:value-of select="#ToLat"/>,0
</coordinates>
</LineString>
</Placemark>
</xsl:template>
<xsl:template match="Row2"/>
</xsl:stylesheet>
Applied on your input, produces the RTF:
<Folder>
<name>Point</name>
<Folder>
<name>VSS</name>
<Placemark>
<name>VSS1</name>
<description/>
<styleUrl/>
<Point>
<coordinates>-85.671800000000,
43.953000000000</coordinates>
</Point>
</Placemark>
<Placemark>
<name>VSS2</name>
<description/>
<styleUrl/>
<Point>
<coordinates>-85.678900000000,
43.966900000000</coordinates>
</Point>
</Placemark>
</Folder>
<Folder>
<name>Cabinet</name>
<Placemark>
<name>Cab1</name>
<description/>
<styleUrl/>
<Point>
<coordinates>-85.677100000000,
43.903100000000</coordinates>
</Point>
</Placemark>
<Placemark>
<name>Cab2</name>
<description/>
<styleUrl/>
<Point>
<coordinates>-85.677300000000,
43.913500000000</coordinates>
</Point>
</Placemark>
</Folder>
<Folder>
<name>DMS</name>
<Placemark>
<name>DMS1</name>
<description/>
<styleUrl/>
<Point>
<coordinates>-85.677100000000,
43.903100000000</coordinates>
</Point>
</Placemark>
<Placemark>
<name>DMS2</name>
<description/>
<styleUrl/>
<Point>
<coordinates>-85.676800000000,
43.989400000000</coordinates>
</Point>
</Placemark>
</Folder>
</Folder>
<Folder>
<name>Line</name>
<Folder>
<name>Handhole</name>
<Placemark>
<name>
From: 2127 to: 1947</name>
<description/>
<styleUrl/>
<LineString>
<tessellate>1</tessellate>
<coordinates>-83.195331982500,43.438474034300,0 -83.193657308800,43.437072542900,0
</coordinates>
</LineString>
</Placemark>
</Folder>
<Folder>
<name>Cabinet</name>
<Placemark>
<name>
From: 2011 to: 2012</name>
<description/>
<styleUrl/>
<LineString>
<tessellate>1</tessellate>
<coordinates>-83.260130135400,43.482705558800,0 -83.260107590500,43.482694479700,0
</coordinates>
</LineString>
</Placemark>
</Folder>
<Folder>
<name>Other</name>
<Placemark>
<name>
From: 2415 to: 2413</name>
<description/>
<styleUrl/>
<LineString>
<tessellate>1</tessellate>
<coordinates>-83.086848805700,43.624664303600,0 -83.086770805500,43.624645615600,0
</coordinates>
</LineString>
</Placemark>
</Folder>
</Folder>