Charistine
Charistine

Reputation: 61

XSLT - for-each column without consistent column name

I have the following XML coming in:

<Record>
    <FILE>F1235-01</FILE>
    <PERSONID>1234</PERSONID>
    <LNAME>Smith</LNAME>
    <AGE>54</AGE>
    <WEIGHT>158</WEIGHT>
    <RACE>White</RACE>
</Record>
<Record>
    <FILE>F1237-02</FILE>
    <PERSONID>7856</PERSONID>
    <LNAME>Hill</LNAME>
    <SEX>F</SEX>
    <WEIGHT>124</WEIGHT>
</Record>
<Record>
    <FILE>F1240-07</FILE>
    <PERSONID>5634</PERSONID>
    <LNAME>Patel</LNAME>
    <HEIGHT>67</HEIGHT>
    <HAIR>Black</HAIR>
</Record>

The first three elements in each Record will always be the same, FILE, PERSONID, LNAME. After that, each Record will have different elements, not even the same number of elements- some may have one 1, others may have 50. In order to Database this, I need to get one record for each of the varying elements BUT with the first three identifying elements.

<Record>
    <FILE>F1235-01</FILE>
    <PERSONID>1234</PERSONID>
    <LNAME>Smith</LNAME>
    <ATTRIBUTE>AGE</ATTRIBUTE>
    <VALUE>54</VALUE>
</Record>
<Record>
    <FILE>F1235-01</FILE>
    <PERSONID>1234</PERSONID>
    <LNAME>Smith</LNAME>
    <ATTRIBUTE>WEIGHT</ATTRIBUTE>
    <VALUE>158</VALUE>
</Record>
<Record>
    <FILE>F1235-01</FILE>
    <PERSONID>1234</PERSONID>
    <LNAME>Smith</LNAME>
    <ATTRIBUTE>RACE</ATTRIBUTE>
    <VALUE>White</VALUE>
</Record>
<Record>
    <FILE>F1237-02</FILE>
    <PERSONID>7856</PERSONID>
    <LNAME>Hill</LNAME>
    <ATTRIBUTE>SEX</ATTRIBUTE>
    <VALUE>F</VALUE>
</Record>
<Record>
    <FILE>F1237-02</FILE>
    <PERSONID>7856</PERSONID>
    <LNAME>Hill</LNAME>
    <ATTRIBUTE>WEIGHT</ATTRIBUTE>
    <VALUE>124</VALUE>
</Record>
<Record>
    <FILE>F1240-07</FILE>
    <PERSONID>5634</PERSONID>
    <LNAME>Patel</LNAME>
    <ATTRIBUTE>HEIGHT</ATTRIBUTE>
    <VALUE>67</VALUE>
</Record>
<Record>
    <FILE>F1240-07</FILE>
    <PERSONID>5634</PERSONID>
    <LNAME>Patel</LNAME>
    <ATTRIBUTE>HAIR</ATTRIBUTE>
    <VALUE>Black</VALUE>
</Record>

I can't come up with any XML which loops through elements after the third, though it seems like it would be simple in theory. Any help would be appreciated.

Upvotes: 0

Views: 257

Answers (2)

zx485
zx485

Reputation: 29052

This is another XSLT-1.0 solution:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" />
<xsl:strip-space elements="*" />

    <!-- Identity template -->
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Record | Record/* | text()">
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="Record/*[position()>3]">
        <Record>
            <xsl:copy-of select="../*[3 >= position()]" />
            <ATTRIBUTE><xsl:value-of select="local-name()" /></ATTRIBUTE>
            <VALUE><xsl:value-of select="." /></VALUE>
        </Record>
    </xsl:template>
    
</xsl:stylesheet>

Upvotes: 0

michael.hor257k
michael.hor257k

Reputation: 117102

How about 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="/Records">
    <xsl:copy>
        <xsl:for-each select="Record">
            <xsl:variable name="common" select="FILE | PERSONID | LNAME"/>
            <xsl:for-each select="*[position() > 3]">
                <Record>
                    <xsl:copy-of select="$common"/>
                    <ATTRIBUTE>
                        <xsl:value-of select="name()"/>
                    </ATTRIBUTE>
                    <VALUE>
                        <xsl:value-of select="."/>
                    </VALUE>
                </Record>   
            </xsl:for-each>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Note that this assumes every Record has at least one additional attribute. And that the input is a well-formed XML, unlike the one in your question - for example, it has a root element named Records.

Upvotes: 1

Related Questions