Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT - Copy all other nodes, add 1 new node

Tags:

xml

xslt

I have created an XSLT and i was wondering how it is possible to copy all nodes between one set of tags, and add another tag at the bottom. I have created the XSLT that has all the logic for determining which tag to add, and what it should be called. However the issue i am now getting is that i can not copy all the other tags across too. Below are the files in question:

XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/csvImportSchema">
        <csvImportSchema>
            <xsl:for-each select="payload">
                <payload>
                    <xsl:copy-of select="@*"/>
                    <xsl:variable name="ean">
                        <xsl:value-of select="ean"/>
                    </xsl:variable>
                    <xsl:for-each select="../product">
                        <xsl:if test="ean = $ean">
                            <productId><xsl:value-of select="article"/></productId>
                        </xsl:if>
                    </xsl:for-each>
                </payload>
            </xsl:for-each>
        </csvImportSchema>
    </xsl:template>

</xsl:stylesheet>

INPUT

<?xml version="1.0" encoding="UTF-8"?>
<csvImportSchema>
    <payload>
        <test>1</test>
        <test2>2</test2>
        <test3>3</test3>
        <ean>1111111111</ean>
        <productId/>
    </payload>
    <product>
        <article>722619</article>
        <ean>1111111111</ean>
    </product>
</csvImportSchema>

CURRENT OUTPUT

<?xml version="1.0" encoding="utf-8"?>
<csvImportSchema>
    <payload>
        <productId>722619</productId>
    </payload>
</csvImportSchema>

DESIRED OUTPUT

<?xml version="1.0" encoding="UTF-8"?>
<csvImportSchema>
    <payload>
        <test>1</test>
        <test2>2</test2>
        <test3>3</test3>
        <ean>1111111111</ean>
        <productId>722619</productId>
    </payload>
</csvImportSchema>
like image 686
mmkd Avatar asked Sep 18 '12 09:09

mmkd


People also ask

How do I copy nodes in XSLT?

XSLT <xsl:copy-of> The <xsl:copy-of> element creates a copy of the current node. Note: Namespace nodes, child nodes, and attributes of the current node are automatically copied as well! Tip: This element can be used to insert multiple copies of the same node into different places in the output.

What is following sibling in XSLT?

The following-sibling axis indicates all the nodes that have the same parent as the context node and appear after the context node in the source document.

What is current group () in XSLT?

Returns the contents of the current group selected by xsl:for-each-group. Available in XSLT 2.0 and later versions. Available in all Saxon editions. current-group() ➔ item()*


Video Answer


2 Answers

This short and simple 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:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="productId">
  <productId>
    <xsl:value-of select="../../product/article"/>
  </productId>
 </xsl:template>
 <xsl:template match="product"/>
</xsl:stylesheet>

when applied on the provided XML document:

<csvImportSchema>
    <payload>
        <test>1</test>
        <test2>2</test2>
        <test3>3</test3>
        <ean>1111111111</ean>
        <productId/>
    </payload>
    <product>
        <article>722619</article>
        <ean>1111111111</ean>
    </product>
</csvImportSchema>

produces the wanted, correct result:

<csvImportSchema>
   <payload>
      <test>1</test>
      <test2>2</test2>
      <test3>3</test3>
      <ean>1111111111</ean>
      <productId>722619</productId>
   </payload>
</csvImportSchema>

Explanation:

  1. The identity rule copies "as-is" every node for which it is selected for execution.

  2. An overriding template matching product "deletes" this element from the output (by its empty body).

  3. Another overriding template matches productId and generates this element with a text-node child taken from product/article.

like image 154
Dimitre Novatchev Avatar answered Oct 20 '22 02:10

Dimitre Novatchev


One observation on your code. Don't use this:

<xsl:variable name="ean">
    <xsl:value-of select="../ean"/>
</xsl:variable>

when you could write this:

<xsl:variable name="ean" select="../ean"/>

Not only is it verbose, it's also incredibly inefficient: instead of binding $ean to an existing node, you are extracting the string value of an existing node, forming a text node with that string value, creating a new XML document tree, and adding this text node to the content of this new document. (I once got a stylesheet to run 3 times faster by eliminating this horrible construct.)

like image 34
Michael Kay Avatar answered Oct 20 '22 00:10

Michael Kay