views:

760

answers:

1

I would like to select a node and modify its attributes and child-nodes using an xsl:script function. In addition, templates matching child-nodes of that node should STILL perform their job (after script is done processing the node).

  1. Can it be done using XSLT?
  2. Can you please provide an example / skeleton for such a transformation?
+1  A: 

Yes, it can be done. I don't seem to see what the problem is because the XML (or whatever output) of an XSL script is buffered independently from its input.

This is illustrated in the following example whereby a simple XSL script copies an input XML document mostly as-is, changing a few things:

  • the root element name and attribute
  • flattening by removing the element from the hierarchy
  • dropping the results/date element
  • rename the item's 'source' attribute 'origin'
  • change the item's 'level' attribute value
  • rename the FirstName and LastName elements of the item elements

Sample input

<?xml version="1.0" encoding="ISO-8859-1"?>
<MyRoot version="1.2">
    <results>
        <info>Alpha Bravo</info>
        <author>Employee No 321</author>
        <date/>
        <item source="www" level="6" cost="33">
            <FirstName>Jack</FirstName>
            <LastName>Frost</LastName>
            <Date>1998-10-30</Date>
            <Organization>Lemon growers association</Organization>
         </item>
         <item source="db-11" level="1" cost="65" qry="routine 21">
            <FirstName>Mike</FirstName>
            <LastName>Black</LastName>
            <Date>2006-10-30</Date>
            <Organization>Ford Motor Company</Organization>
         </item>
    </results>
</MyRoot>

Output produced

<?xml version="1.0" encoding="utf-16"?>
<MyNewRoot version="0.1">
    <author>Employee No 321</author>
    <info>Alpha Bravo</info>
    <item cost="33" origin="www" level="77">
        <GivenName>Jack</GivenName>
        <FamilyName>Frost</FamilyName>
        <Date>1998-10-30</Date>
        <Organization>Lemon growers association</Organization>
    </item>
    <item cost="65" qry="routine 21" origin="db-11" level="77">
        <GivenName>Mike</GivenName>
        <FamilyName>Black</FamilyName>
        <Date>2006-10-30</Date>
        <Organization>Ford Motor Company</Organization>
    </item>
</MyNewRoot>

XSL script

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  exclude-result-prefixes="#default">

<xsl:template match="MyRoot">
   <xsl:call-template name="MainTemplate">
   </xsl:call-template>
</xsl:template>

<xsl:template name="MainTemplate">
   <MyNewRoot version="0.1">

   <xsl:copy-of select="results/author" />
   <xsl:copy-of select="results/info" />

   <xsl:for-each select="results/item">
      <xsl:call-template name="FixItemElement"/>
   </xsl:for-each>

  </MyNewRoot> 
</xsl:template>

<xsl:template name="FixItemElement">
    <xsl:copy>
        <xsl:copy-of select="@*[not(name()='source' or name()='level')]" />
        <xsl:attribute name="origin">
            <xsl:value-of select="@source"/>
        </xsl:attribute>
        <xsl:attribute name="level">
            <xsl:value-of select="77"/>
        </xsl:attribute>

        <xsl:for-each select="descendant::*">
          <xsl:choose>
            <xsl:when test="local-name(.) = 'FirstName'">
                <GivenName>
                   <xsl:value-of select="."/>
                </GivenName>
            </xsl:when>
            <xsl:when test="local-name(.) = 'LastName'">
                 <FamilyName>
                   <xsl:value-of select="."/>
                </FamilyName>
            </xsl:when>
            <xsl:otherwise>
              <xsl:copy>
                 <xsl:apply-templates select="@*|node()"/>
              </xsl:copy>
            </xsl:otherwise>
          </xsl:choose>       
        </xsl:for-each>       
    </xsl:copy>
</xsl:template>

mjv
Sorry Israel Chen for long delay before posting the sample code. Although she doesn't give me SO rep points, my wife had a long honey-do-list for me today ;-)
mjv
The XSL above is XSLT 1.0, but it's just as easy with 2.0 I stuck with 1.0 as some frameworks are still catching up...
mjv
Thanks for your reply, it helps a lot.
Israel Chen
@mjv: I'm afraid that you've missed the entire `<xsl:script>` part? Yours is vanilla XSLT, no script involved.
Tomalak
@Tomalak. Yes I did! [totally miss the <xsl:script> part of the OP's question...]. Hum... now that I see this requirement, I'm at a loss on how to respond. My only exposure to xsl:script is with extension functions which merely reformat variables and such (which are then handed back to XSLT-proper for inclusion in the result tree). Do you know of techniques that would allow the extensions to directly mess with the result tree? Aside from plain ignorance, I tend to stay clear of features that are quite dependent on the XSLT processor implementation, but I'm none the less curious...
mjv