Search code examples
xmlparsingxsltfilemaker

Comma delimited parsing of XML for Filemaker XSLT


I am looking to parse a set of comma delimited image URLs for real estate data within a single xml tag and write them out as separate tags. This is way beyond my current knowledge and I am having real trouble learning what I need to do quickly enough to get this job done. If someone could help, it would be much appreciated.

Here is the XML:

<Report>
    <Row1>
        <Reference>001</Reference>
        <Type>House</Type>
        <Location>New York</Estado>
        <Size>665.6</Size>
        <Images>http://www.example.com/001/image1.jpg,http://www.example.com/001/image2.jpg,http://www.example.com/001/image3.jpg,http://www.example.com/001/image4.jpg</Images>
    </Row1>
    <Row2>
        <Reference>056</Reference>
        <Type>Apartment</Type>
        <Location>Washington</Estado>
        <Size>147.5</Size>
        <Images>http://www.example.com/056/image1.jpg,http://www.example.com/056/image5.jpg,http://www.example.com/056/image8.jpg,http://www.example.com/056/image9.jpg</Images>
    </Row2>
    </Report>

This is what I would like to output:

<RESULTSET>
    <ROW>
        <COL><DATA>001</DATA></COL>
        <COL><DATA>House</DATA></COL>
        <COL><DATA>New York</DATA></COL>
        <COL><DATA>665.6</DATA></COL>
        <COL><DATA>http://www.example.com/001/image1.jpg</DATA></COL>
        <COL><DATA>http://www.example.com/001/image2.jpg</DATA></COL>
        <COL><DATA>http://www.example.com/001/image3.jpg</DATA></COL>
        <COL><DATA>http://www.example.com/001/image4.jpg</DATA></COL>
    </ROW>
    <ROW>
        <COL><DATA>056</DATA></COL>
        <COL><DATA>Apartment</DATA></COL>
        <COL><DATA>Washington</DATA></COL>
        <COL><DATA>147.5</DATA></COL>
        <COL><DATA>http://www.example.com/056/image1.jpg</DATA></COL>
        <COL><DATA>http://www.example.com/056/image5.jpg</DATA></COL>
        <COL><DATA>http://www.example.com/056/image8.jpg</DATA></COL>
        <COL><DATA>http://www.example.com/056/image9.jpg</DATA></COL>
    </ROW>
</RESULTSET>

Thank you for any help you can give me.


Solution

  • All source nodes, except for the Images nodes, can be processed by simple templates that extract their content and reorganize it in your new structure:

    <xsl:template match="Report">
        <RESULTSET>
            <xsl:apply-templates/>
        </RESULTSET>
    </xsl:template>
    
    <xsl:template match="Report/*">
        <ROW>
            <xsl:apply-templates/>
        </ROW>
    </xsl:template>
    
    <xsl:template match="Report/*/*[not(self::Images)]">
        <COL><DATA>
            <xsl:value-of select="."/>
        </DATA></COL>
    </xsl:template>
    

    The Images node requires splitting the string by a separator (,). To achieve this in XSLT 1.0 you need a named template that you can call recursively. It will be called by a template that matches the Images element:

    <xsl:template match="Images">
        <xsl:call-template name="split">
            <xsl:with-param name="string" select="."/>
        </xsl:call-template>
    </xsl:template>
    

    The named template split checks if there is a comma in the sequence. If not, it simply returns the contents wrapped in a <COL><DATA> block. If there is, the first element is extracted and the rest of the string is processed by the same template recursively, until all items are processed. The result will be a list of <COL><DATA> blocks each containing one item:

    <xsl:template name="split">
        <xsl:param name="string"/>
        <xsl:param name="separator">,</xsl:param>
            <xsl:choose>
                <xsl:when test="contains($string, $separator)">
                    <xsl:variable name="first-string" select="substring-before($string, $separator)"/>
                    <xsl:variable name="rest" select="substring-after($string, concat($first-string,$separator))"/>
                    <COL><DATA><xsl:value-of select="$first-string"/></DATA></COL>
                    <xsl:call-template name="split">
                        <xsl:with-param name="string" select="$rest"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <COL><DATA><xsl:value-of select="$string"/></DATA></COL>
                </xsl:otherwise>
            </xsl:choose>
    </xsl:template>
    

    You can try it out in this XSLT Fiddle