I hope I don\'t lose anyone by mentioning Filemaker. I am trying to turn it\'s XML export into something usable by SSIS. FM\'s native XML export has field names and data i
One elegant way to solve this problem is this:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fmp="http://www.filemaker.com/fmpxmlresult"
exclude-result-prefixes="fmp"
>
<!-- the key indexes the METADATA fields by their position -->
<xsl:key
name="kMetaData"
match="fmp:METADATA/fmp:FIELD"
use="count(preceding-sibling::fmp:FIELD) + 1"
/>
<!-- separate templates increase readability -->
<xsl:template match="/fmp:FMPXMLRESULT">
<PRODUCTRECS>
<xsl:apply-templates select="fmp:RESULTSET/fmp:ROW" />
</PRODUCTRECS>
</xsl:template>
<xsl:template match="fmp:ROW">
<PRODUCT>
<xsl:apply-templates select="fmp:COL" />
</PRODUCT>
</xsl:template>
<xsl:template match="fmp:COL">
<!-- column name lookup is high-speed because of the key -->
<xsl:element name="{string(key('kMetaData', position())/@NAME)}">
<xsl:value-of select="fmp:DATA" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Which outputs on my system:
<PRODUCTRECS>
<PRODUCT>
<name>Dr. Zim</name>
<address>1234 Internet Way</address>
<city></city>
<state></state>
<zip></zip>
</PRODUCT>
</PRODUCTRECS>
However, be warned that XML element names are subject to stricter rules than FileMaker column names. The above will crash and burn if your column names violate those rules.
The notable features of the stylesheet are:
<xsl:key>
for speedy lookup of nodes - this should become noticeable for larger inputsexclude-result-prefixes
to prevent declaration of the fmp
namespace in the result<xsl:element>
to create elements with a dynamic namepreceding-sibling
XPath axis as a way of determining node position (because the position()
function does not work in <xsl:key>
sGo ahead and ask if anything is unclear.
Your other problem (number formatting) has an answer here: XSL: Formatting numbers, excluding trailing zeroes.
This should get you started:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:fm="http://www.filemaker.com/fmpxmlresult">
<xsl:template match="/fm:FMPXMLRESULT">
<PRODUCTRECS>
<xsl:apply-templates select="fm:RESULTSET/fm:ROW"/>
</PRODUCTRECS>
</xsl:template>
<xsl:template match="fm:ROW">
<PRODUCT>
<!--
Use this if the element containing the NAME="FileMaker Pro" attribute is the one you want to use
for each row name.
<xsl:element name="{name(/fm:FMPXMLRESULT/*[@NAME='FileMaker Pro'])}">-->
<xsl:for-each select="fm:COL/fm:DATA">
<xsl:variable name="currentPos" select="position()"/>
<xsl:element name="{/fm:FMPXMLRESULT/fm:METADATA/fm:FIELD[position()=$currentPos]/@NAME}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
<!--</xsl:element>-->
</PRODUCT>
</xsl:template>
</xsl:stylesheet>
Also, take a look at xsl:number for the other part or possibly just the number() function.
You can jump back out to the root starting the xpath query with /
i.e /fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
xmlns:fmp="http://www.filemaker.com/fmpxmlresult"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/fmp:FMPXMLRESULT">
<PRODUCTRECS>
<xsl:apply-templates select="fmp:RESULTSET/fmp:ROW" />
</PRODUCTRECS>
</xsl:template>
<xsl:template match="fmp:RESULTSET/fmp:ROW">
<PRODUCT>
<xsl:apply-templates select="fmp:COL" />
</PRODUCT>
</xsl:template>
<xsl:template match="fmp:COL">
<xsl:variable name="currentPosition" select="position()" />
<xsl:element name="{/fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD[position() = $currentPosition]/@NAME}">
<xsl:value-of select="fmp:DATA"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>