GPX import to Filemaker Pro using XSL stylesheet - xslt

I know about GPSbabel and have used it for transforming GPX files into CSV that I can then import into Filemaker Pro for manipulation. However, I'd like to be able to import GPX files more simply & neatly into FMP and hoped I could write a XSL stylesheet to convert the GPX form of XML to the FMPXMLRESULT grammar.
I'm looking at GPX files from Garmin Foretrex, Dakota, Nuvi & GPSmap296 devices. My latest attempt at a XSL stylesheet (using snippets cribbed from various examples) looks like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="trkseg">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="01-25-2011" NAME="FileMaker" VERSION="ProAdvanced 11.0v3"/>
<DATABASE DATEFORMAT="Yyyy-m-d" LAYOUT="" NAME="gpx_import_test.fp7" RECORDS="{#count}"
TIMEFORMAT="k:mm:ss "/>
<METADATA>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="ele" TYPE="NUMBER"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="lat" TYPE="NUMBER"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="lon" TYPE="NUMBER"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="time_text" TYPE="TEXT"/>
</METADATA>
<RESULTSET FOUND="#count">
<xsl:for-each select="trkpt">
<ROW MODID="0" RECORDID="{position()}">
<COL>
<DATA>
<xsl:value-of select="lat"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="lon"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="ele"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="time_text"/>
</DATA>
</COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>
When I try to use the above stylesheet, FMP complains "XML parsing error: invalid document structure".
I'd welcome any suggestions - I guess I'm making some very simple and obvious error.
regards
Rowland
Here is a snipped version of a typical GPX file from a Garmin Dakota 20 that I am trying to import.
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx xmlns="http://www.topografix.com/GPX/1/1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:wptx1="http://www.garmin.com/xmlschemas/WaypointExtension/v1"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
creator="Dakota 20" version="1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1
http://www.topografix.com/GPX/1/1/gpx.xsd
http://www.garmin.com/xmlschemas/GpxExtensions/v3
http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd
http://www.garmin.com/xmlschemas/WaypointExtension/v1
http://www8.garmin.com/xmlschemas/WaypointExtensionv1.xsd
http://www.garmin.com/xmlschemas/TrackPointExtension/v1
http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd">
<metadata>
<link href="http://www.garmin.com">
<text>Garmin International</text>
</link>
<time>2011-10-21T16:55:50Z</time>
</metadata>
<trk>
<name>Current Track: 20 OCT 2011 10:31</name>
<extensions>
<gpxx:TrackExtension>
<gpxx:DisplayColor>Black</gpxx:DisplayColor>
</gpxx:TrackExtension>
</extensions>
<trkseg>
<trkpt lat="51.6084605176" lon="-2.2182025295">
<ele>43.40</ele>
<time>2011-10-20T09:31:44Z</time>
</trkpt>
<trkpt lat="51.6084605176" lon="-2.2182025295">
<ele>47.24</ele>
<time>2011-10-20T09:31:53Z</time>
</trkpt>
</trkseg>
</trk>
</gpx>

Several problems here. One is this:
<xsl:template match="trkseg">
You do not start at the root, so the XSLT processor will process the root and all other elements using default rules (which are to output the value of the element). This may add extra text around FMPXMLRESULT. To fix it we need to do something like:
<xsl:template match="/">
<xsl:for-each select="//trkseg/trkpt"> <!-- or use a full path -->
...
</xsl:for-each>
This is not enough because trkseg is not just trkseg, it's trkseg in the GPX namespace. Without proper namespace declaration the stylesheet won't find the elements at all. And the final problem is that lat and lon are attributes and you need to refer to them as #lat and #lon. Here's the working XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gpx="http://www.topografix.com/GPX/1/1"
exclude-result-prefixes="gpx">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="01-25-2011" NAME="FileMaker" VERSION="ProAdvanced 11.0v3"/>
<DATABASE DATEFORMAT="Yyyy-m-d"
LAYOUT="" NAME="gpx_import_test.fp7" RECORDS="{#count}"
TIMEFORMAT="k:mm:ss "/>
<METADATA>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="ele" TYPE="NUMBER"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="lat" TYPE="NUMBER"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="lon" TYPE="NUMBER"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="time" TYPE="TEXT"/>
</METADATA>
<RESULTSET FOUND="count(//gpx:trkseg/gpx:trkpt)">
<xsl:for-each select="//gpx:trkseg/gpx:trkpt">
<ROW MODID="0" RECORDID="{position()}">
<COL>
<DATA>
<xsl:value-of select="#lat"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="#lon"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="gpx:ele"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="gpx:time"/>
</DATA>
</COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>

Here is a xslt file to read out only the waypoints, not the tracks: By
the way: I added the fields time, name, sym and type to the import. If
not needed, delete the corresponding lines in this file.
Copy and store the text below the dotted line in a file named
"yourchoice.xslt" where "yourchoice" might be any name you want and
use this as a xslt transformation file in filemaker.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gpx="http://www.topografix.com/GPX/1/1"
exclude-result-prefixes="gpx">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="01-25-2011" NAME="FileMaker" VERSION="ProAdvanced 11.0v3"/>
<DATABASE DATEFORMAT="Yyyy-m-d"
LAYOUT="" NAME="gpx_import_test.fp7" RECORDS="{#count}"
TIMEFORMAT="k:mm:ss "/>
<METADATA>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="latitude" TYPE="NUMBER"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="longitude" TYPE="NUMBER"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="elevation" TYPE="NUMBER"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="time" TYPE="TEXT"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="name" TYPE="TEXT"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="sym" TYPE="TEXT"/>
<FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="type" TYPE="TEXT"/>
</METADATA>
<RESULTSET FOUND="count(gpx:wpt)">
<xsl:for-each select="gpx:wpt">
<ROW MODID="0" RECORDID="{position()}">
<COL>
<DATA>
<xsl:value-of select="#lat"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="#lon"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="gpx:ele"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="gpx:time"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="gpx:name"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="gpx:sym"/>
</DATA>
</COL>
<COL>
<DATA>
<xsl:value-of select="gpx:type"/>
</DATA>
</COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>

Related

No more tags in output after xslt transform using namespace

I've this source file
<?xml version="1.0" encoding="ISO-8859-1"?>
<producttypesresult xmlns="https://fr.shopping.rakuten.com/res/schema/producttypes"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://fr.shopping.rakuten.com/res/schema/producttypes https://fr.shopping.rakuten.com/res/schema/producttypes/producttypes.2015-06-30.xsd">
<request>
<user>myuser</user>
<version>2015-06-30</version>
</request>
<response>
<producttypetemplate>
<alias>mev_livre_livre_ancien</alias>
<label>Livres</label>
<categoryref>186126</categoryref>
<updatedate>2014-08-06T00:00:00</updatedate>
</producttypetemplate>
</response>
</producttypesresult>
My XSLT is
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="/">
<FMPXMLRESULT
xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="" NAME="" VERSION=""/>
<DATABASE DATEFORMAT="" LAYOUT="" NAME="" RECORDS="" TIMEFORMAT=""/>
<METADATA>
<FIELD NAME="alias" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="label" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="categoryref" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="updatedate" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
</METADATA>
<RESULTSET FOUND="">
<xsl:for-each select="producttypesresult/response/producttypetemplate">
<ROW MODID="" RECORDID="">
<COL><DATA><xsl:value-of select="alias"/></DATA></COL>
<COL><DATA><xsl:value-of select="label"/></DATA></COL>
<COL><DATA><xsl:value-of select="categoryref"/></DATA></COL>
<COL><DATA><xsl:value-of select="updatedate"/></DATA></COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>
It doesn't work with that input due to the namespace, but if I change the input header with
<producttypesresult>
It works fine, and I get the desired output :
<?xml version="1.0" encoding="utf-8"?>
<FMPXMLRESULT
xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="" NAME="" VERSION=""/>
<DATABASE DATEFORMAT="" LAYOUT="" NAME="" RECORDS="" TIMEFORMAT=""/>
<METADATA>
<FIELD NAME="alias" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="label" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="categoryref" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="updatedate" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
</METADATA>
<RESULTSET FOUND="">
<ROW MODID="" RECORDID="">
<COL><DATA>mev_livre_livre_ancien</DATA></COL>
<COL><DATA>Livres</DATA></COL>
<COL><DATA>186126</DATA></COL>
<COL><DATA>2014-08-06T00:00:00</DATA></COL>
</ROW>
</RESULTSET>
</FMPXMLRESULT>
but modifying the input file is not good obviously.
So I figured out it was the namespace issue so I modified the xslt to this
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="https://fr.shopping.rakuten.com/res/schema/producttypes"
xmlns="https://fr.shopping.rakuten.com/res/schema/producttypes"
>
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="my">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="" NAME="" VERSION=""/>
<DATABASE DATEFORMAT="" LAYOUT="" NAME="" RECORDS="" TIMEFORMAT=""/>
<METADATA>
<FIELD NAME="alias" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="label" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="categoryref" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="updatedate" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
</METADATA>
<RESULTSET FOUND="">
<xsl:for-each select="producttypesresult/response/producttypetemplate">
<ROW MODID="" RECORDID="">
<COL><DATA><xsl:value-of select="alias"/></DATA></COL>
<COL><DATA><xsl:value-of select="label"/></DATA></COL>
<COL><DATA><xsl:value-of select="categoryref"/></DATA></COL>
<COL><DATA><xsl:value-of select="updatedate"/></DATA></COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>
I get the desired fileds value, but all the tags are blank, so I jut get
<?xml version="1.0" encoding="utf-8"?>
myuser
2015-06-30
mev_livre_livre_ancien
Livres
186126
2014-08-06T00:00:00
So how to get the desired output with all my tags ?
Thanks
You started correctly by declaring a namespace and binding it to a prefix, but you're not using it as intended.
Try :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="https://fr.shopping.rakuten.com/res/schema/producttypes"
exclude-result-prefixes="my">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="/">
<FMPXMLRESULT
xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="" NAME="" VERSION=""/>
<DATABASE DATEFORMAT="" LAYOUT="" NAME="" RECORDS="" TIMEFORMAT=""/>
<METADATA>
<FIELD NAME="alias" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="label" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="categoryref" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="updatedate" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
</METADATA>
<RESULTSET FOUND="">
<xsl:for-each select="my:producttypesresult/my:response/my:producttypetemplate">
<ROW MODID="" RECORDID="">
<COL><DATA><xsl:value-of select="my:alias"/></DATA></COL>
<COL><DATA><xsl:value-of select="my:label"/></DATA></COL>
<COL><DATA><xsl:value-of select="my:categoryref"/></DATA></COL>
<COL><DATA><xsl:value-of select="my:updatedate"/></DATA></COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>

xslt calculating insurance writeoff not working

I am trying to calculate the contractual write off for medical insurance but the calculation doesn't seem to be working. For example, I need to take the value of the charge attribute on the <charge> tag and subtract the value of the amount attribute on the <adjustment> tag but only when the group and code attributes on the <adjustment> tag are: group ='CO' and code = '45' or '385'
I was able to get everything but the calculations working. I've tried looking at several answers on this forum but am obviously missing something. Any help would be appreciated.
Here is my XML (simplified):
<result check_number="05173408" ...>
<claim ... total_charge="720" total_paid="43">
<charge charge="200">
<adjustment amount="125" code="45" group="CO" />
<adjustment amount="35" code="385" group="CO" />
<adjustment amount="20" code="3" group="PR" />
</charge>
<charge charge="70">
<adjustment amount="70" code="204" group="PR" />
</charge>
<charge charge="300">
<adjustment amount="273" code="45" group="CO" />
<adjustment amount="15" code="3" group="PR" />
</charge>
<charge charge="150">
<adjustment amount="139" code="45" group="CO" />
</charge>
</claim>
</result>
Here is my xslt to date:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<!-- maps to the beginning result tag -->
<xsl:for-each select="/result/claim/charge">
<!-- amount allowed by the insurance for line item -->
<COL>
<DATA>
<xsl:apply-templates select="adjustment" mode="calcAdjTotal" >
</xsl:apply-templates>
</DATA>
</COL>
</xsl:for-each>
<!-- Template created to test Parfait's suggestion -->
<xsl:template match="adjustment" mode="calcAdjTotal" >
<xsl:variable name="condition" select="adjustment[#group='CO' and (#code='45' or #code='385')]"></xsl:variable>
<xsl:value-of select="../#charge - sum($condition/#amount)" />
</xsl:if>
</xsl:template>
</xsl:template>
</xsl:stylesheet>
Here is my output: (updated output)
<COL>
</DATA>
</COL>
<COL>
</DATA>
</COL>
<COL>
</DATA>
</COL>
<COL>
</DATA>
</COL>
Here's what I want to get:
<COL>
<DATA>
40
</DATA>
</COL>
<COL>
<DATA>
</DATA>
</COL>
<COL>
<DATA>
27
</DATA>
</COL>
<COL>
<DATA>
11
</DATA>
</COL>
The format will then be used to import data into Filemaker.
Consider wrapping your conditional equation in an <xsl:if>. Additionally, you can do so with a variable storing the conditional value to avoid repetition:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<ROOT>
<xsl:apply-templates select="*"/>
</ROOT>
</xsl:template>
<xsl:template match="charge">
<xsl:variable name="condition" select="adjustment[(#code='45' or #code='385') and #group='CO']"/>
<COL>
<DATA>
<xsl:if test="$condition">
<xsl:value-of select="#charge - sum($condition/#amount)"/>
</xsl:if>
</DATA>
</COL>
</xsl:template>
</xsl:transform>
I am guessing (!) you want something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/result">
<RESULT>
<xsl:for-each select="claim/charge">
<ROW>
<COL>
<DATA>
<xsl:value-of select="#charge - sum(adjustment[#group='CO' and (#code='45' or #code='385')]/#amount)" />
</DATA>
</COL>
</ROW>
</xsl:for-each>
</RESULT>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<RESULT>
<ROW>
<COL>
<DATA>40</DATA>
</COL>
</ROW>
<ROW>
<COL>
<DATA>70</DATA>
</COL>
</ROW>
<ROW>
<COL>
<DATA>27</DATA>
</COL>
</ROW>
<ROW>
<COL>
<DATA>11</DATA>
</COL>
</ROW>
</RESULT>
This assumes you want to create a record (with one field) for each charge.
Note that this is not valid FileMaker XML syntax.
-- edited in response to clarifications in comments: --
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/result">
<RESULT>
<xsl:for-each select="claim/charge[adjustment[not(#group='PR')]]">
<ROW>
<COL>
<DATA>
<xsl:value-of select="#charge - sum(adjustment[not(#group='PR')]/#amount)" />
</DATA>
</COL>
</ROW>
</xsl:for-each>
</RESULT>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<RESULT>
<ROW>
<COL>
<DATA>40</DATA>
</COL>
</ROW>
<ROW>
<COL>
<DATA>27</DATA>
</COL>
</ROW>
<ROW>
<COL>
<DATA>11</DATA>
</COL>
</ROW>
</RESULT>

H2 refine XSLT to transform Shopify XML for FileMaker import

I'm trying to transform the following Shopify XML ...
<orders>
<order>
<order-no>1001</order-no>
<line-items>
<line-item>
<product-id>knife</product-id>
</line-item>
<line-item>
<product-id>fork</product-id>
</line-item>
</line-items>
<shipping-address>
<post-code>rh10 3hs</post-code>
</shipping-address>
</order>
</orders>
to the FileMaker FMPXMLRESULT grammer..
<ROW>
<COL>
<DATA>1001</DATA>
</COL>
<COL>
<DATA>Knife</DATA>
</COL>
<COL>
<DATA>rh10 3hs</DATA>
</COL>
</ROW>
<ROW>
<COL>
<DATA>1001</DATA>
</COL>
<COL>
<DATA>Fork</DATA>
</COL>
<COL>
<DATA>rh10 3hs</DATA>
</COL>
</ROW>
i.e.
1001 Knife rh10 3hs
1001 Fork rh10 3hs
using a FileMaker xslt example...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/*">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="" NAME="" VERSION=""/>
<DATABASE DATEFORMAT="M/d/yyyy" LAYOUT="" NAME="" RECORDS="{count(/*/*)}" TIMEFORMAT="h:mm:ss a"/>
<METADATA>
<xsl:for-each select="/*/*[position()=1]/*">
<FIELD>
<xsl:attribute name="EMPTYOK">YES</xsl:attribute>
<xsl:attribute name="MAXREPEAT">1</xsl:attribute>
<xsl:attribute name="NAME"><xsl:value-of select="name()"/></xsl:attribute>
<xsl:attribute name="TYPE">TEXT</xsl:attribute>
</FIELD>
</xsl:for-each>
</METADATA>
<RESULTSET>
<xsl:attribute name="FOUND"><xsl:value-of select="count(child::*)"/></xsl:attribute>
<xsl:for-each select="child::*">
<ROW>
<xsl:attribute name="MODID">0</xsl:attribute>
<xsl:attribute name="RECORDID">0</xsl:attribute>
<xsl:for-each select="child::*">
<COL>
<DATA>
<xsl:value-of select="."/>
</DATA>
</COL>
</xsl:for-each>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>
BUT this extracts the top level order details for each order ignoring the children and grandchildren.
I'm a complete beginner and have trawled the net and forums trying many permutations of the line '' but to be honest it's guess work and initial elation is begin replaced by desperation ;-(
Any tips greatly appreciated
Many thanks,
Jim
In Filemaker, you can only import into one table at a time. You need a separate stylesheet for each import. This one for importing into the LineItems table:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="" NAME="" VERSION=""/>
<DATABASE DATEFORMAT="" LAYOUT="" NAME="" RECORDS="" TIMEFORMAT=""/>
<METADATA>
<FIELD NAME="OrderID" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="ProductID" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
</METADATA>
<RESULTSET FOUND="">
<xsl:for-each select="orders/order/line-items/line-item">
<ROW MODID="" RECORDID="">
<COL><DATA><xsl:value-of select="../../order-no"/></DATA></COL>
<COL><DATA><xsl:value-of select="product-id"/></DATA></COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>
and this one for importing into the Orders table:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="" NAME="" VERSION=""/>
<DATABASE DATEFORMAT="" LAYOUT="" NAME="" RECORDS="" TIMEFORMAT=""/>
<METADATA>
<FIELD NAME="OrderID" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
<FIELD NAME="PostCode" TYPE="TEXT" EMPTYOK="YES" MAXREPEAT=""/>
</METADATA>
<RESULTSET FOUND="">
<xsl:for-each select="orders/order">
<ROW MODID="" RECORDID="">
<COL><DATA><xsl:value-of select="order-no"/></DATA></COL>
<COL><DATA><xsl:value-of select="shipping-address/post-code"/></DATA></COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>

xml to xml using xslt : Recursive matching & creating hierarchy like tree

I am completely new to xslt. Please help me to write style sheet.I have input xml like this
Input XML:
<elements>
<e1>
<pid>1</pid>
<cid>2</cid>
</e1>
<e1>
<pid>1</pid>
<cid>3</cid>
</e1>
<e1>
<pid>2</pid>
<cid>4</cid>
</e1>
</elements>
Desired XML:
<tree>
<unit id="1">
<unit id="2">
<unit id="4">
<data></data>
</unit>
<data></data>
</unit>
<unit id="3">
<data></data>
</unit>
<data></data>
</unit>
</tree>
I feel this should be really easy but I'm struggling to find information about how to do this. My XSLT knowledge isn't great.
I'm not 100% sure how you want the XSLT to determine from that input that the top id is 1 (is it because it's the only pid value with no corresponding cid values, or is it always 1?). Nonetheless, this should do the job:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kItemsByC" match="e1" use="cid" />
<xsl:key name="kItemsByP" match="e1" use="pid" />
<xsl:template match="/">
<tree>
<xsl:call-template name="Unit">
<!-- This will be the value of the <pid> that has no <cid> references to
it (assuming there is only one top-level <pid>) -->
<xsl:with-param name="id"
select="string(/elements/e1/pid[not(key('kItemsByC', .))])" />
</xsl:call-template>
</tree>
</xsl:template>
<xsl:template match="e1" name="Unit">
<xsl:param name="id" select="cid" />
<unit id="{$id}">
<xsl:apply-templates select="key('kItemsByP', $id)" />
<data />
</unit>
</xsl:template>
</xsl:stylesheet>
When this is run on your sample input, this produces:
<tree>
<unit id="1">
<unit id="2">
<unit id="4">
<data />
</unit>
<data />
</unit>
<unit id="3">
<data />
</unit>
<data />
</unit>
</tree>
Note: The above XSLT has logic to attempt to dynamically locate the top-level ID. If it can be assumed that the top-level unit will always have ID 1, then one key and the above XSLT's (somewhat) complicated formula can be eliminated:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kItemsByP" match="e1" use="pid" />
<xsl:template match="/">
<tree>
<xsl:call-template name="Unit">
<xsl:with-param name="id" select="1" />
</xsl:call-template>
</tree>
</xsl:template>
<xsl:template match="e1" name="Unit">
<xsl:param name="id" select="cid" />
<unit id="{$id}">
<xsl:apply-templates select="key('kItemsByP', $id)" />
<data />
</unit>
</xsl:template>
</xsl:stylesheet>
This also produces the requested output when run on your sample input.
Ah, after reading JLRishe I think I get it: "pid" means "parent ID", "cid" means "child ID", and e1 represents a parent-child relationship. Brilliant detective work, I would never have worked that out for myself.
The basic model is that when you are positioned on a parent element you do apply-templates to its children. This applies just as well if the parent/child relationships are represented by primary/foreign keys as when they are represented using the XML hierarchy. So the essence is:
<xsl:template match="e1">
<unit id="{pid}">
<xsl:apply-templates select="//e1[pid=current()/cid]"/>
<data/>
</unit>
</xsl:template>
which is essentially JLRishe's solution except he has added an optimization using keys.

How to find start(min) and end(max) date in xml file?

I have an XML file. I should find for same field2 start(min) date and End(max) date from field3. Maybe xsl have some function to find it.Because I try do it trying to find min month and min day and max month, max day.
XML:
<document>
<line id="0">
<field id="2">X111</field>
<field id="3">2011-03-31</field>
</line>
<line id="1">
<field id="2">X111</field>
<field id="3">2011-04-04</field>
</line>
<line id="2">
<field id="2">X111</field>
<field id="3">2011-04-02</field>
</line>
<line id="3">
<field id="2">X222</field>
<field id="3">2011-04-04</field>
</line>
<line id="4">
<field id="2">X222</field>
<field id="3">2011-04-01</field>
</line>
<line id="4">
<field id="2">X333</field>
<field id="3">2011-04-01</field>
</line>
</document>
Output:
<document>
<Message>
<ID>X111</ID>
<dateStart>2011-03-31</dateStart>
<dateEnd>2011-04-04</dateEnd>
</Message>
<Message>
<ID>X222</ID>
<dateStart>2011-04-01</dateStart>
<dateEnd>2011-04-04</dateEnd>
</Message>
<Message>
<ID>X333</ID>
<dateStart>2011-04-01</dateStart>
<dateEnd>2011-04-01</dateEnd>
</Message>
</document>
Please help to solve it. I'm working with stylesheet version="1.0".
This can probably be optimized but it returns the requested results:
<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:key name="test" match="line" use="field[#id=2]"/>
<xsl:template match="/">
<document>
<xsl:for-each select="//line[generate-id()=generate-id(key('test',field[#id=2]))]">
<xsl:sort select="field[#id=2]"/>
<Message>
<ID>
<xsl:value-of select="field[#id=2]"/>
</ID>
<xsl:apply-templates select="key('test',field[#id=2])">
<xsl:sort select="field[#id=3]"/>
</xsl:apply-templates>
</Message>
</xsl:for-each>
</document>
</xsl:template>
<xsl:template match="line">
<xsl:if test="position()=1">
<dateStart>
<xsl:value-of select="field[#id=3]"/>
</dateStart>
</xsl:if>
<xsl:if test="position()=last()">
<dateEnd>
<xsl:value-of select="field[#id=3]"/>
</dateEnd>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I upvoted #mousio's answer, but I'd prefer to see the first of each line type handled in its own template. So, in the spirit of TMTOWTDI, here's how I would have done it:
<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:key name="byField2" match="line" use="field[#id=2]" />
<xsl:template match="/">
<document>
<xsl:apply-templates select="document/line" />
</document>
</xsl:template>
<xsl:template match="line[count(.|key('byField2', field[#id=2])[1])=1]">
<Message>
<ID><xsl:value-of select="field[#id=2]" /></ID>
<xsl:apply-templates select="key('byField2', field[#id=2])" mode="m">
<xsl:sort select="field[#id=3]" />
</xsl:apply-templates>
</Message>
</xsl:template>
<xsl:template match="line" mode="m">
<xsl:if test="position()=1">
<dateStart><xsl:value-of select="field[#id=3]" /></dateStart>
</xsl:if>
<xsl:if test="position()=last()">
<dateEnd><xsl:value-of select="field[#id=3]" /></dateEnd>
</xsl:if>
</xsl:template>
<xsl:template match="line" />
</xsl:stylesheet>
I think this is easier to read (and probably more efficient on large documents, since it doesn't abuse //).
This transformation shows how to find the wanted minimum and maximum:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:apply-templates select="line">
<xsl:sort select="field[#id=3]"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="line">
<xsl:if test="position()=1">
Earliest:
<xsl:copy-of select="."/>
</xsl:if>
<xsl:if test="position()=last()">
Latest:
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<document>
<line id="0">
<field id="2">X111</field>
<field id="3">2011-03-31</field>
</line>
<line id="1">
<field id="2">X111</field>
<field id="3">2011-04-04</field>
</line>
<line id="2">
<field id="2">X111</field>
<field id="3">2011-04-02</field>
</line>
<line id="3">
<field id="2">X222</field>
<field id="3">2011-04-04</field>
</line>
<line id="4">
<field id="2">X222</field>
<field id="3">2011-04-01</field>
</line>
<line id="4">
<field id="2">X333</field>
<field id="3">2011-04-01</field>
</line>
</document>
produces:
Earliest:
<line id="0">
<field id="2">X111</field>
<field id="3">2011-03-31</field>
</line>
Latest:
<line id="3">
<field id="2">X222</field>
<field id="3">2011-04-04</field>
</line>
Explanation:
Choosing the first and last element from the sorted node-list. The dates are in a "good" format, so they are sorted just as strings.