Search code examples
xmlxsltsaxonxslt-3.0

XML - XSLT - Adding new element to xml document with second xml document


I've been with this problem for some time now and I can't solve it, so I decided to post here,

I have the following simple XML input document:

<?xml version="1.0" encoding="UTF-8"?>
<doc1 xmlns="http://www.eclipse.org/birt/2005/design" version="3.2.23">
    <text-prop name="docName">firstDoc</text-prop>
    <body>
        <column id="17"/>
        <column id="18"/>
        <column id="19"/>
    </body>
</doc1>

I want to add two new elements, one being the <elements> and inside it have I'll have the <element>. Then I also want to add a <newColumn> element that is child of the <body> element. So the result I want to get is:

<?xml version="1.0" encoding="UTF-8"?>
<doc1 xmlns="http://www.eclipse.org/birt/2005/design" version="3.2.23">
    <text-prop name="docName">firstDoc</text-prop>
   <elements>
      <element name="first element"/>
   </elements>
    <body>
        <column id="17"/>
        <column id="18"/>
        <column id="19"/>
      <newColumn name="dataSet">first element</newColumn>
    </body>
</doc1>

Here is the XSLT stylesheet I'm using, based on @Martin Honnen's stylesheet that he gave me in a related post:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xpath-default-namespace="http://www.eclipse.org/birt/2005/design"
    xmlns="http://www.eclipse.org/birt/2005/design"
    exclude-result-prefixes="xs"
    expand-text="yes"
    version="3.0">


  <xsl:param name="doc2" xmlns="">
<doc2>
    <elemList1>
        <elem1 ID="001" name="firstDoc" />
        <elem1 ID="002" name="secondDoc"/>
    </elemList1>
    <elemList2>
        <elem2 elemID="001">
                <elemData name="first element"/>
        </elem2>
        <elem2 elemID="002">
                <elemData name="second element"/>
        </elem2>
    </elemList2>
</doc2>      
  </xsl:param>

  <xsl:output indent="yes" cdata-section-elements="xml-prop"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:key name="layout-ref" match="elem1" use="@name" xpath-default-namespace=""/>
  <xsl:key name="report-ref" match="elem2" use="@elemID" xpath-default-namespace=""/>

  <xsl:template match="doc1/text-prop[@name = 'docName']">
      <xsl:next-match/>

      <xsl:variable name="layout" select="key('layout-ref', ., $doc2)"/>
      <xsl:variable name="report" select="key('report-ref', $layout/@ID, $doc2)"/>

      <elements>
          <xsl:apply-templates select="$report//elemData" xpath-default-namespace=""/>
      </elements>

  </xsl:template>

  <xsl:template match="elemData" xpath-default-namespace="">
      <element name="{@name}"/>
  </xsl:template>

  <!--HERE IT STOPS WORKING-->

  <xsl:template match="doc1/body/column[last()]" >
      <xsl:next-match/>
      <xsl:variable name="layout2" select="key('layout-ref', ., $doc2)"/>
      <xsl:variable name="report2" select="key('report-ref', $layout2/@ID, $doc2)"/>

      <newColumn name="dataSet">
          <xsl:value-of select="$report2/elemData/@name" xpath-default-namespace=""/>
      </newColumn>

  </xsl:template>


</xsl:stylesheet>

So the result I'm actually getting is this one:

<?xml version="1.0" encoding="UTF-8"?>
<doc1 xmlns="http://www.eclipse.org/birt/2005/design" version="3.2.23">
    <text-prop name="docName">firstDoc</text-prop>
   <elements>
      <element name="first element"/>
   </elements>
    <body>
        <column id="17"/>
        <column id="18"/>
        <column id="19"/>
      <newColumn name="dataSet"/>
    </body>
</doc1>

Basically I want the value of element <newColumn> to be the same as the value of attribute name of element <element>, but I'm not being able to access that value and put it inside the <newColumn> element

As you can see I'm able to access that same value and put as an attribute of the new <element> element.

Can't I access the document I defined as a <xsl:param> (doc2) that way?

SEE XSLT FIDDLE AT: https://xsltfiddle.liberty-development.net/3NzcBtf/1

Thank you!

Alexandre Jacinto


Solution

  • As it seems I made the right guess on what you wanted in a comment I now post it as an answer so that you can mark your question as solved.

    When you use the key function you need to pass in the key value for the node(s) you want to find, i.e. in match="doc1/text-prop[@name = 'docName']" you pass in key('layout-ref', ., $doc2) which is the string value (e.g. firstDoc) of the matched text-prop element. Inside your match="doc1/body/column[last()]" the column element does not have that string value (your columns are empty), so it is not quite clear from your question how you want to relate the column to the other element, unless you want to navigate to key('layout-ref', ancestor::doc1/text-prop[@name = 'docName'], $doc2).