Search code examples
linuxxmlxslt

XML Block insert into .xml file via xsltproc on Linux


I have this original.xml file tree:

<?xml version="1.0"?>
<root xmlns="http://www.tibco.com/xmlns/">
    <element1>
        <name>Hi</name>
    </element1>
    <element2>
        <name>Beer</name>
    </element2>
    <element2>
        <name>Balloon</name>
        <element2_1>
            <name>John</name>
        </element2_1>
    </element2> 
</root>

I am trying to add element2_2 under element2 parent. I have tried to make an insert (xslt stylesheet as part of a script) based on "xsl:element match" as following:

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

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

<xsl:template match="root/element2[@name='Balloon']">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
    <xsl:element name="element2_2">
        <xsl:attribute name="name">Edward</xsl:attribute>
    </xsl:element>
</xsl:template>
</xsl:stylesheet>
EOT

And then use the xsltproc to insert it the insert.xsl into original.xml based on a specific location(tag and element) on Linux:

xsltproc insert.xsl original.xml > ./updated.xml

Unfortunately nothing happens. The only thing I found was working was changing the match pattern to root ("/*") and redact the xsl:element to actual .xml element, which resulted in concatenating the element after root(not something useful).

Desired Output:

<root>
        <element1>
            <name>Hi</name>
        </element1>
        <element2>
            <name>Beer</name>
        </element2>
        <element2>
            <name>Balloon</name>
            <element2_1>
                <name>John</name>
            </element2_1>
            <element2_2>
                <name>Edward</name>
            </element2_2>
        </element2> 
    </root>

Suppose there are no differences between the environments.


Solution

  • @name selects an attribute of which there aren't any in the XML so I suspect you simply want (now after the namespace has been added to the input XML)

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://www.tibco.com/xmlns/"
    xmlns:tc="http://www.tibco.com/xmlns/"
    exclude-result-prefixes="tc">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="tc:root/tc:element2[tc:name='Balloon']">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
            <element2_2>
              <name>Edward</name>
            </element2_2>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Demo with client-side JavaScript (where in Chromium based browsers libxslt is kind of the same processor as in xsltproc):

    const xmlSource = `<?xml version="1.0"?>
    <root xmlns="http://www.tibco.com/xmlns/">
        <element1>
            <name>Hi</name>
        </element1>
        <element2>
            <name>Beer</name>
        </element2>
        <element2>
            <name>Balloon</name>
            <element2_1>
                <name>John</name>
            </element2_1>
        </element2> 
    </root>`;
    
    const domParser = new DOMParser();
    
    const xmlDoc = domParser.parseFromString(xmlSource, 'application/xml');
    
    const xsltSource = `<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://www.tibco.com/xmlns/"
    xmlns:tc="http://www.tibco.com/xmlns/"
    exclude-result-prefixes="tc">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="tc:root/tc:element2[tc:name='Balloon']">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
            <element2_2>
              <name>Edward</name>
            </element2_2>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>`;
    
    const xsltDoc = domParser.parseFromString(xsltSource, 'application/xml');
    
    const xsltProc = new XSLTProcessor();
    xsltProc.importStylesheet(xsltDoc);
    
    const resultDoc = xsltProc.transformToDocument(xmlDoc);
    
    //console.log(resultDoc);
    
    console.log(new XMLSerializer().serializeToString(resultDoc));