0

I have xml entries as follows:

<employee>
<component name="address">
<property name="city" value="Bangalore">
<property name="state" value="Karnataka">
<component/>
<component name="info">
<property name="name" value="Alok"></property>
<property name="age" value="25"></property>
</component>
</employee>

So for entry in info field, I want to add an element with position as engineer like below:

<property name="position" value ="engineer"></property>

I am trying to achieve this by doing

<xsl:template match="/employee/component[@name='info']">
  <xsl:if test="not(property[@name='position'])">
  <xsl:element name="property">
  <xsl:attribute name="position">position</xsl:attribute>
  <xsl:attribute name="value">engineer</xsl:attribute>
  </xsl:element>
  </xsl:if>
  </xsl:template>

But what it is doing is completely removing the info block and adding line

Need suggestions on correct way of implementing this.

1 Answer 1

2

The problem is in your template, you are creating a new element, but you are tell XSLT to also copy the existing component node and its child nodes. You are effectively saying when you find a matching component node, create a new node instead.

What you need to do, is add code to copy the existing node, like so

<xsl:template match="/employee/component[@name='info']">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
        <xsl:if test="not(property[@name='position'])">
            <xsl:element name="property">
                <xsl:attribute name="position">position</xsl:attribute>
                <xsl:attribute name="value">engineer</xsl:attribute>
            </xsl:element>
        </xsl:if>
    </xsl:copy>
</xsl:template>

This can be simplified though by moving the condition in the xsl:if to the template match instead. Also, there is no need to use xsl:element and xsl:attribute here, just write out the new element directly.

Try this XSLT

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

    <xsl:template match="/employee/component[@name='info'][not(property[@name='position'])]">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
            <property position="position" name="engineer" />
        </xsl:copy>
    </xsl:template>

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

Note the use of the Identity Template in the transformation to copy all other existing nodes.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot Tim. It is doing what I wanted to do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.