tags:

views:

42

answers:

1

i laughed out loud when i read this article on Daily WTF : http://thedailywtf.com/Articles/WellFormed-XML.aspx, but now it's not funny anymore because i have begun to recognize this "xml design pattern" in the wild with alarming frequency. for example, i just exported some data from a rational clearquest query and i got this:

<?xml version="1.0" encoding="us-ascii"?>
<?xml-stylesheet type="text/xsl" href="http://scm/rational/clearquest/webservice/resultset.xsl"?&gt;
<resultset dbset="CQMaster" dbname="PROD" entitydefname="TR" count="1" name="_my trs">
  <header count="3">
    <column type="dbid">dbid</column>
    <column type="id">id</column>
    <column type="short_string">Abstract</column>
  </header>
  <record>
    <field>33607697</field>
    <field>PROD00011111</field>
    <field>The product has a bug that needs fixed.</field>
  </record>
</resultset>

i'm not an xslt wizard - i'll probably figure this out sooner or later, but it can't hurt to ask... what is the simplest xslt pattern to transform the above into something more useful like this:

<?xml version="1.0" encoding="us-ascii"?>
<resultset dbset="CQMaster" dbname="PROD" entitydefname="TR" count="1" name="_my trs">
  <record>
    <dbid type="dbid">33607697</dbid>
    <id type="id">PROD00011111</id>
    <Abstract type="short_string">The product has a bug that needs fixed.</Abstract>
  </record>
</resultset>
+1  A: 

The following transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kColByPos" match="column"
  use="count(preceding-sibling::*) +1"/>

 <xsl:template match="/*">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <xsl:apply-templates select="record"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="record">
  <record>
    <xsl:apply-templates/>
  </record>
 </xsl:template>

 <xsl:template match="field">
  <xsl:variable name="vColumn" select=
   "key('kColByPos', position())"/>

   <xsl:element name="{translate($vColumn, ' ', '_'}">
    <xsl:copy-of select="$vColumn/@type"/>
    <xsl:apply-templates/>
   </xsl:element>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<resultset dbset="CQMaster" dbname="PROD" entitydefname="TR" count="1" name="_my trs">
  <header count="3">
    <column type="dbid">dbid</column>
    <column type="id">id</column>
    <column type="short_string">Abstract</column>
  </header>
  <record>
    <field>33607697</field>
    <field>PROD00011111</field>
    <field>The product has a bug that needs fixed.</field>
  </record>
</resultset>

produces the wanted, correct result:

<resultset dbset="CQMaster" dbname="PROD" entitydefname="TR" count="1" name="_my trs">
    <record>
        <dbid type="dbid">33607697</dbid>
        <id type="id">PROD00011111</id>
        <Abstract type="short_string">The product has a bug that needs fixed.</Abstract>
    </record>
</resultset>
Dimitre Novatchev
+1 for speedy solution. it works great except when the source xml has a <column type="my type">my type</column> - the space in the column name breaks the result. i resolved this by modifying your script with <xsl:element name="{translate($vColumn,' ','_')}">. thanks for the lesson; i've never used xsl:key or position() before.
rev
@rev: Oh, I should have thought about that myself (very absent-minded today ...). Your fix to this issue is exactly what I'd have done. I edited the answer and put the fix there.
Dimitre Novatchev