Question

I'm trying to build an xml file from data in a Filemaker database. I can get the basic structure of the XML to work but am having issues rendering portal rows in the right way.

Here is the end result of what I'm trying to achieve.

<product>
<model>Model</model>
<caliber>Caliber</caliber>
<submodel>Sub Model</submodel>
    <Dimensions>
        <label>Overall length</label>
        <label>Overall height</label>
        <value>178 mm</value>       
        <value>138 mm</value>
    </Dimensions>

    <weight>
        <label>Overall weight</label>
        <label>with magazine</label>
        <value>739 g</value>
        <value>91 g</value> 
    </weight>
</product>

The Model and Caliber and Submodel items are fields in a table in my database. The dimensions and weight are pulling from a related table as a portal. The relationship key is the model.

Each portal row as a Category, Label and Value.

I'm trying to write an if statement in the XSL file that will grab all the portal rows with a category of "Dimensions" and group them together. The same for Weight.

Here is my current working XSL file:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fmp="http://www.filemaker.com/fmpxmlresult" version="1.0" exclude-result-prefixes="fmp">
<xsl:output method="xml" version="1.0" encoding="windows-1251" indent="yes"/>
    <xsl:template match="/">
        <product>
            <xsl:for-each select="fmp:FMPXMLRESULT/fmp:RESULTSET/fmp:ROW">
                <model>
                    <xsl:value-of select="fmp:COL[2]/fmp:DATA"/>
                </model>

                <caliber>
                    <xsl:value-of select="fmp:COL[1]/fmp:DATA"/>
                </caliber>

                <submodel>
                    <xsl:value-of select="fmp:COL[4]/fmp:DATA"/>
                </submodel>

                <specs>
                    <xsl:for-each select="fmp:COL[10]/fmp:DATA">
                        <category>
                            <xsl:value-of select="."/>
                        </category>
                    </xsl:for-each>
                    <xsl:for-each select="fmp:COL[8]/fmp:DATA">
                        <label>
                            <xsl:value-of select="."/>
                        </label>
                    </xsl:for-each>

                    <xsl:for-each select="fmp:COL[9]/fmp:DATA">
                        <value> 
                            <xsl:value-of select="."/>
                        </value>
                    </xsl:for-each>
                </specs>
            </xsl:for-each>
        </product>
    </xsl:template>
</xsl:stylesheet>

This will output all the information I need but puts all the categories from the portal row together. I'm very new to XSL and don't know how to formulate the if statement.

I need to change the info inside to the if statement. Here's what I was playing with:

<xsl:if test="fmp:COL[10]/fmp:DATA= 'Dimensions'">
    <Dimensions>
        <label>Overall length</label>
        <label>Overall height</label>
        <value>178 mm</value>       
        <value>138 mm</value>
    </Dimensions>
</xsl:if>

Here is the raw export from Filemaker.

<?xml version="1.0" encoding="UTF-8"?>
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
   <ERRORCODE>0</ERRORCODE>
   <PRODUCT BUILD="03-15-2012" NAME="FileMaker" VERSION="ProAdvanced 12.0v1" />
   <DATABASE DATEFORMAT="M/d/yyyy" LAYOUT="" NAME="HK Product Database.fmp12" RECORDS="61" TIMEFORMAT="h:mm:ss a" />
   <METADATA>
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="Caliber" TYPE="TEXT" />
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="Model" TYPE="TEXT" />
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="ModelID" TYPE="TEXT" />
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="PrimaryCategory" TYPE="TEXT" />
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="ProductID" TYPE="TEXT" />
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="SubModel" TYPE="TEXT" />
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="Models::WP Post ID" TYPE="NUMBER" />
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="Specs::SpecLabel" TYPE="TEXT" />
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="Specs::SpecData" TYPE="TEXT" />
      <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="Specs::SpecCategory" TYPE="TEXT" />
   </METADATA>
   <RESULTSET FOUND="61">
      <ROW MODID="1" RECORDID="196">
         <COL>
            <DATA>9 mm x 19</DATA>
         </COL>
         <COL>
            <DATA>P59</DATA>
         </COL>
         <COL>
            <DATA>P59</DATA>
         </COL>
         <COL>
            <DATA>Semi-Automatic Pistols</DATA>
         </COL>
         <COL>
            <DATA>P30-9</DATA>
         </COL>
         <COL>
            <DATA>P30</DATA>
         </COL>
         <COL>
            <DATA>181</DATA>
         </COL>
         <COL>
            <DATA>Overall length</DATA>
            <DATA>Overall height</DATA>
            <DATA>Overall width w/lever</DATA>
            <DATA>Barrel length</DATA>
            <DATA>Sight radius</DATA>
            <DATA>With magazine</DATA>
            <DATA>Magazine</DATA>
            <DATA>Magazine capacity</DATA>
            <DATA>Trigger system</DATA>
            <DATA>Trigger pull (N)</DATA>
            <DATA>Trigger travel</DATA>
            <DATA>Sights</DATA>
         </COL>
         <COL>
            <DATA>178 mm</DATA>
            <DATA>138 mm</DATA>
            <DATA>35 mm</DATA>
            <DATA>98 mm</DATA>
            <DATA>149 mm</DATA>
            <DATA>739 g</DATA>
            <DATA>91 g</DATA>
            <DATA>15 Cartridges</DATA>
            <DATA>SA/DA</DATA>
            <DATA>* 20 + 4/-2 (SA) 
51 +/- 5 (DA)</DATA>
            <DATA>.25 inch (SA)
&lt; .55 inch (DA)</DATA>
            <DATA>Fixed (Open square notch rear sight with contrast points)</DATA>
         </COL>
         <COL>
            <DATA>Dimensions</DATA>
            <DATA>Dimensions</DATA>
            <DATA>Dimensions</DATA>
            <DATA>Dimensions</DATA>
            <DATA>Dimensions</DATA>
            <DATA>Weight</DATA>
            <DATA>Weight</DATA>
            <DATA>Other Specifications</DATA>
            <DATA>Other Specifications</DATA>
            <DATA>Other Specifications</DATA>
            <DATA>Other Specifications</DATA>
            <DATA>Other Specifications</DATA>
         </COL>
      </ROW>
  </RESULTSET>
</FMPXMLRESULT>

It's not working right. Any help would be appreciated!

Edit:

Here is an update of where I"m at: This stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fmp="http://www.filemaker.com/fmpxmlresult" version="1.0" exclude-result-prefixes="fmp">
<xsl:output method="xml" version="1.0" encoding="windows-1251" indent="yes"/>
    <xsl:template match="/">
        <product>
            <xsl:for-each select="fmp:FMPXMLRESULT/fmp:RESULTSET/fmp:ROW">
                <model>
                    <xsl:value-of select="fmp:COL[2]/fmp:DATA"/>
                </model>
                <productID>
                    <xsl:value-of select="fmp:COL[5]/fmp:DATA"/>
                </productID>                
                <caliber>
                    <xsl:value-of select="fmp:COL[1]/fmp:DATA"/>
                </caliber>

                <submodel>
                    <xsl:value-of select="fmp:COL[4]/fmp:DATA"/>
                </submodel>

                <xsl:if test="fmp:COL[10]/fmp:DATA= 'Dimensions'">
                    <Dimensions>
                        <xsl:value-of select="."/>
                    </Dimensions>
                </xsl:if>

                <specs>
                    <xsl:for-each select="fmp:COL[10]/fmp:DATA">
                        <category>
                            <xsl:value-of select="."/>
                        </category>
                    </xsl:for-each>
                    <xsl:for-each select="fmp:COL[8]/fmp:DATA">
                        <label>
                            <xsl:value-of select="."/>
                        </label>
                    </xsl:for-each>

                    <xsl:for-each select="fmp:COL[9]/fmp:DATA">
                        <value> 
                            <xsl:value-of select="."/>
                        </value>
                    </xsl:for-each>
                </specs>
            </xsl:for-each>
        </product>
    </xsl:template>
</xsl:stylesheet>

Renders this XML:

<product>
<model>P30</model>
<productID>P30-9</productID>
<caliber>9 mm x 19</caliber>
<submodel>Semi-Automatic Pistols</submodel>
<Dimensions>9 mm x 19P30P30Semi-Automatic PistolsP30-9P30181Overall lengthOverall heightOverall width w/leverBarrel lengthSight radiusWith magazineMagazineMagazine capacityTrigger systemTrigger pull (N)Trigger travelSights178 mm138 mm35 mm98 mm149 mm739 g91 g15 CartridgesSA/DA* 20 + 4/-2 (SA) 
51 +/- 5 (DA).25 inch (SA)
&lt; .55 inch (DA)Fixed (Open square notch rear sight with contrast points)
DimensionsDimensionsDimensionsDimensionsDimensionsWeightWeightOther SpecificationsOther SpecificationsOther SpecificationsOther SpecificationsOther Specifications</Dimensions>
<specs>
<category>Dimensions</category>
<category>Dimensions</category>
<category>Dimensions</category>
<category>Dimensions</category>
<category>Dimensions</category>
<category>Weight</category>
<category>Weight</category>
<category>Other Specifications</category>
<category>Other Specifications</category>
<category>Other Specifications</category>
<category>Other Specifications</category>
<category>Other Specifications</category>
<label>Overall length</label>
<label>Overall height</label>
<label>Overall width w/lever</label>
<label>Barrel length</label>
<label>Sight radius</label>
<label>With magazine</label>
<label>Magazine</label>
<label>Magazine capacity</label>
<label>Trigger system</label>
<label>Trigger pull (N)</label>
<label>Trigger travel</label>
<label>Sights</label>
<value>178 mm</value>
<value>138 mm</value>
<value>35 mm</value>
<value>98 mm</value>
<value>149 mm</value>
<value>739 g</value>
<value>91 g</value>
<value>15 Cartridges</value>
<value>SA/DA</value>
<value>* 20 + 4/-2 (SA) 
51 +/- 5 (DA)</value>
<value>.25 inch (SA)
&lt; .55 inch (DA)</value>
<value>Fixed (Open square notch rear sight with contrast points)
</value>
</specs>
</product>

Using the same source XML. The dimensions if statement is working but now just need to separate that content.

Was it helpful?

Solution

Here’s my suggested stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fmp="http://www.filemaker.com/fmpxmlresult" version="1.0" exclude-result-prefixes="fmp">
<xsl:output method="xml" version="1.0" encoding="windows-1251" indent="yes"/>
    <xsl:template match="/">
      <xsl:for-each select="fmp:FMPXMLRESULT/fmp:RESULTSET/fmp:ROW">
        <product id="{fmp:COL[5]/fmp:DATA}">
                <model>
                    <xsl:value-of select="fmp:COL[2]/fmp:DATA"/>
                </model>
                <caliber>
                    <xsl:value-of select="fmp:COL[1]/fmp:DATA"/>
                </caliber>

                <submodel>
                    <xsl:value-of select="fmp:COL[4]/fmp:DATA"/>
                </submodel>

                <dimensions>
                    <xsl:for-each select="fmp:COL[10]/fmp:DATA[text()='Dimensions']">
                      <xsl:variable name="category-position" select="count(preceding-sibling::*) + 1"/>

                      <xsl:variable name="label" select="ancestor::fmp:ROW/fmp:COL[8]/fmp:DATA[$category-position]"/>

                      <xsl:if test="$label = 'Overall length' or $label = 'Overall height'">
                        <value label="{$label}"><xsl:value-of select="ancestor::fmp:ROW/fmp:COL[9]/fmp:DATA[$category-position]"/></value>
                      </xsl:if>
                    </xsl:for-each>
                </dimensions>

                <weight>
                    <xsl:for-each select="fmp:COL[10]/fmp:DATA[text()='Weight']">
                      <xsl:variable name="category-position" select="count(preceding-sibling::*) + 1"/>
                      <value label="{ancestor::fmp:ROW/fmp:COL[8]/fmp:DATA[$category-position]}"><xsl:value-of select="ancestor::fmp:ROW/fmp:COL[9]/fmp:DATA[$category-position]"/></value>
                    </xsl:for-each>
                </weight>
              </product>
          </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Output:

<?xml version="1.0" encoding="windows-1251"?>
<product id="P30-9">
  <model>P59</model>
  <caliber>9 mm x 19</caliber>
  <submodel>Semi-Automatic Pistols</submodel>
  <dimensions>
    <value label="Overall length">178 mm</value>
    <value label="Overall height">138 mm</value>
  </dimensions>
  <weight>
    <value label="With magazine">739 g</value>
    <value label="Magazine">91 g</value>
  </weight>
</product>

I notice you only wanted two dimensions values in your output, so I added a filter based on the label.

NOTE: what follows applies to an earlier version of the stylesheet. I changed it to create a result more like what I describe below.

Is your output XML already specified? Because I think it could be improved to be more XML-y:

<dimensions>
  <dimension label="Overall length">178 mm</dimension>
  <dimension label="Overall height">139 mm</dimension>
</dimensions>

Or really, if you know in advance what your key/value pairs are:

<dimensions length="178mm" height="139mm"/>

If you have control over the design of your output XML, might as well take advantage of its features rather than emulating the awkward Filemaker output. <label/> and <value/> pairs that are connected by nothing other than position are pretty un-XML, better to combine them into an element!

OTHER TIPS

Would something like this work for you?

<?xml version="1.0" encoding="utf-8"?>
<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">
<xsl:output method="xml" version="1.0" encoding="windows-1251" indent="yes"/>

<xsl:template match="/">
    <product>
        <xsl:for-each select="fmp:FMPXMLRESULT/fmp:RESULTSET/fmp:ROW">
            <model>
                <xsl:value-of select="fmp:COL[2]/fmp:DATA"/>
            </model>
            <caliber>
                <xsl:value-of select="fmp:COL[1]/fmp:DATA"/>
            </caliber>
            <submodel>
                <xsl:value-of select="fmp:COL[4]/fmp:DATA"/>
            </submodel>
            <Dimensions>
                <xsl:for-each select="fmp:COL[10]/fmp:DATA[.='Dimensions']">
                    <xsl:variable name="i" select="count(preceding-sibling::fmp:DATA) + 1" />
                    <label><xsl:value-of select="../../fmp:COL[8]/fmp:DATA[$i]"/></label>
                    <value><xsl:value-of select="../../fmp:COL[9]/fmp:DATA[$i]"/></value>
                </xsl:for-each>
            </Dimensions>
            <Weight>
                <xsl:for-each select="fmp:COL[10]/fmp:DATA[.='Weight']">
                    <xsl:variable name="i" select="count(preceding-sibling::fmp:DATA) + 1" />
                    <label><xsl:value-of select="../../fmp:COL[8]/fmp:DATA[$i]"/></label>
                    <value><xsl:value-of select="../../fmp:COL[9]/fmp:DATA[$i]"/></value>
                </xsl:for-each>
            </Weight>
        </xsl:for-each>
    </product>
</xsl:template>
</xsl:stylesheet>

Given your input, the following result is obtained:

<?xml version="1.0" encoding="windows-1251"?>
<product>
   <model>P59</model>
   <caliber>9 mm x 19</caliber>
   <submodel>Semi-Automatic Pistols</submodel>
   <Dimensions>
      <label>Overall length</label>
      <value>178 mm</value>
      <label>Overall height</label>
      <value>138 mm</value>
      <label>Overall width w/lever</label>
      <value>35 mm</value>
      <label>Barrel length</label>
      <value>98 mm</value>
      <label>Sight radius</label>
      <value>149 mm</value>
   </Dimensions>
   <Weight>
      <label>With magazine</label>
      <value>739 g</value>
      <label>Magazine</label>
      <value>91 g</value>
   </Weight>
</product>

Of course, a better structure would group label and value together under a common parent element.


Edit:

To do the above, replace:

<label><xsl:value-of select="../../fmp:COL[8]/fmp:DATA[$i]"/></label>
<value><xsl:value-of select="../../fmp:COL[9]/fmp:DATA[$i]"/></value>

with:

<value label="{../../fmp:COL[8]/fmp:DATA[$i]}">
     <xsl:value-of select="../../fmp:COL[9]/fmp:DATA[$i]"/>
</value>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top