[xsl] Adding an element to a list of similar elements?

Subject: [xsl] Adding an element to a list of similar elements?
From: Dan Stromberg <dstromberglists@xxxxxxxxx>
Date: Fri, 04 Apr 2008 21:07:30 -0700
This feels incredibly simple - the sort of thing that should be in 62 different web-based XSLT tutorials, but I've been googling for quite a while now, and I don't see much (anything) on the topic. This is about my 5th XSLT script, so I'm more than a bit of a newb.

All I want to do is add an element to an xml document after a bunch of similar elements - hopefully with just one attribute changed relative to the similar line before it, or with a skeletal element if there isn't a similar element already in the file. I'm willing to do xpath queries to list what's in the file now, and run one or another XSLT script depending on what kind of change is needed, using some other language like bash or python to control which XSLT kicks in.

I'm starting to feel like I should ignore XSLT and use a DOM-based Python script, but hopefully this list can save me from that (mostly because I believe it'd toast the comments, and I like comments :) .

I have a basic XML document I'm testing with (it comes from cruisecontrol, a java-based automated build system) :

<?xml version="1.0"?>
<cruisecontrol>
  <project.mergeanddelete.dist name="db" buildAfterFailed="false"/>
  <project.mergeanddelete.dist name="DScripts" buildAfterFailed="false">
      <property name="log.mergedir" value="checkout/DScripts/dist"/>
  </project.mergeanddelete.dist>
  <project.fullbuild name="3.1.2-dev" buildAfterFailed="false"/>
  <project.fullbuild name="3.1.3-dev" buildAfterFailed="false"/>
</cruisecontrol>

And I just want to add <project.fullbuild name="3.1.4-ga" buildAfterFailed="false"/> after <project.fullbuild name="3.1.3-dev" buildAfterFailed="false"/> ideally using the 3.1.3-dev line as a template for the new 3.1.4 line.

I experimented a bunch on my own with matching the last() project.fullbuild and trying to output two elements on it instead of one, but that seems to be going nowhere despite too many variations and machinations to count (you don't want to see all the things I tried - too much for the list). Is this at all a reasonable approach in standard XSLT? After more googling, I saw something saying that you shouldn't try to add attributes after a child, and I'm not sure if I was running afoul of that or not. I just kept getting the last() element nicely, and then a new element with empty attributes - not much in the way of a helpful error message, not even with --verbose.

Here's -one- of the many things I tried based on this approach:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/cruisecontrol/project.fullbuild[last()]">
<xsl:copy>
<xsl:copy-of select="node() | @*"/>
</xsl:copy>
<xsl:text>&#xA; </xsl:text>
<xsl:element name="project.fullbuild">
<xsl:attribute name="name" value="${to_add}"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>


That {$to_add} is coming from a --string-param on the command line - but I'm not even getting a proper name attribute - it comes out a null string.

I'm using xsltproc on an openSUSE 10.3 system:

$ links -dump /tmp/t.html | sed '/^ *$/d'
 XSL version: 1.0
 Vendor: libxslt
 Vendor URL: http://xmlsoft.org/XSLT/
 Product name: [Undefined]
 Product version: [Undefined]
 Is schema-aware: [Undefined]
 Supports serialization: [Undefined]
 Supports backwards compatibility: [Undefined]

$ xsltproc --version
Using libxml 20630, libxslt 10120 and libexslt 813
xsltproc was compiled against libxml 20630, libxslt 10120 and libexslt 813
libxslt 10120 was compiled against libxml 20630
libexslt 813 was compiled against libxml 20630

Help me, Obi-Wan Kenobi... :)

TIA!

Current Thread