Search code examples
xmlxsltappendflatsiblings

XSLT - append sibling


I'm trying to use XSLT to append context-param as last sibling. There's no common parent element so the task is a little bit harder.

I'd like to append following element:

<context-param>
    <param-name>miku</param-name>
    <param-value>kawaii</param-value>
</context-param>

as the last context-param element (eg. all context-param elements must be next to each other, they can't be scattered anywhere in xml) in following xml:

<web-app>
  <not_interesting_element1/>
  <not_interesting_element2/>

  <context-param>
    <param-name>not_interesting_param_key1</param-name>
    <param-value>kawaii</param-value>
  </context-param>
  <context-param>
    <param-name>not_interesting_param_key2</param-name>
    <param-value>kawaii</param-value>
  </context-param>
  <context-param>
    <param-name>parameterThatsGuaranteedToBeHere</param-name>
    <param-value>someValue</param-value>
  </context-param>


  <not_interesting_element3/>
  <not_interesting_element4/>
  <!-- ... servlets, ... -->
</web-app>

The result should look like this:

<web-app>
  <not_interesting_element1/>
  <not_interesting_element2/>

  <context-param>
    <param-name>not_interesting_param_key1</param-name>
    <param-value>kawaii</param-value>
  </context-param>
  <context-param>
    <param-name>not_interesting_param_key2</param-name>
    <param-value>kawaii</param-value>
  </context-param>
  <context-param>
    <param-name>parameterThatsGuaranteedToBeHere</param-name>
    <param-value>someValue</param-value>
  </context-param>
  <context-param>
      <param-name>miku</param-name>
      <param-value>kawaii</param-value>
  </context-param>

  <not_interesting_element3/>
  <not_interesting_element4/>
  <!-- ... servlets, ... -->
</web-app>

How could I do it please?


Solution

  • This transformation:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:param name="pElemToAdd">
        <context-param>
            <param-name>miku</param-name>
            <param-value>kawaii</param-value>
        </context-param>
     </xsl:param>
    
     <xsl:template match="node()|@*" name="identity">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="context-param[last()]">
      <xsl:call-template name="identity"/>
      <xsl:copy-of select="$pElemToAdd"/>
     </xsl:template>
    </xsl:stylesheet>
    

    when applied to the provided XML document:

    <web-app>
        <not_interesting_element1/>
        <not_interesting_element2/>
        <context-param>
            <param-name>not_interesting_param_key1</param-name>
            <param-value>kawaii</param-value>
        </context-param>
        <context-param>
            <param-name>not_interesting_param_key2</param-name>
            <param-value>kawaii</param-value>
        </context-param>
        <context-param>
            <param-name>parameterThatsGuaranteedToBeHere</param-name>
            <param-value>someValue</param-value>
        </context-param>
        <not_interesting_element3/>
        <not_interesting_element4/>
        <!-- ... servlets, ... -->
    </web-app>
    

    produces the wanted, correct result:

    <web-app>
       <not_interesting_element1/>
       <not_interesting_element2/>
       <context-param>
          <param-name>not_interesting_param_key1</param-name>
          <param-value>kawaii</param-value>
       </context-param>
       <context-param>
          <param-name>not_interesting_param_key2</param-name>
          <param-value>kawaii</param-value>
       </context-param>
       <context-param>
          <param-name>parameterThatsGuaranteedToBeHere</param-name>
          <param-value>someValue</param-value>
       </context-param>
       <context-param>
          <param-name>miku</param-name>
          <param-value>kawaii</param-value>
       </context-param>
       <not_interesting_element3/>
       <not_interesting_element4/><!-- ... servlets, ... -->
    </web-app>
    

    Explanation:

    1. The identity rule copies every node "as-is".

    2. There is a single template, overriding the identity template. This template matches the last context-param element of all context-param elements that are children of their parent.

    3. In the overriding template two actions are performed; the current node is copied by a call to the identity rule; then the element to be appended is copied to the output. For convenience and flexibility, we assume that the element to be appended is passed as a parameter to the transformation.