mroshaw
mroshaw

Reputation: 335

XSLT: Add node, if it is missing, in a specific place in output XML

I'm trying to build a transformation that will:

For example, if "Age" is not provided for a person then it should be defaulted to "Unknown" in the node following the required "Name" node:

<People>
    <Person>
        <Name>Bob Smith</Name>
        <Age>34</Age>
        <Gender>Male></Gender>
    </Person>
    <Person>
        <Name>Jane Smith</Name>
        <Gender>Female</Gender>
    </Person>
</People>

Becomes:

<People>
    <Person>
        <Name>Bob Smith</Name>
        <Age>34</Age>
        <Gender>Male></Gender>
    </Person>
    <Person>
        <Name>Jane Smith</Name>
        <Age>Unknown</Age>
        <Gender>Female</Gender>
    </Person>
</People>

I have the following XSLT, which correctly adds the default, but it adds the default to the end of the Person section. I need the node to be added just after the Name node:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:p="http://services.ic.gov.uk/common-person/v1.0">
    <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="Person[not(Age)]">
        <Person>
            <xsl:apply-templates select="node()|@*" />
            <Age>Unknown</Age>
        </Person>
    </xsl:template>
</xsl:stylesheet>

Could anyone help me implement the additional requirement for the "Age" node to be added after the 'Name" node?

Many thanks!

Upvotes: 1

Views: 1978

Answers (1)

Ian Roberts
Ian Roberts

Reputation: 122414

If you know there will always be a Name then the simplest approach would be to move the logic into a Name template:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:p="http://services.ic.gov.uk/common-person/v1.0">

    <xsl:output omit-xml-declaration="yes" indent="yes" />
    <xsl:strip-space elements="*" />

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

    <xsl:template match="Name[not(../Age)]">
        <!-- copy the Name as usual -->
        <xsl:call-template name="ident" />
        <!-- and add Age immediately after it -->
        <Age>Unknown</Age>
    </xsl:template>
</xsl:stylesheet>

If a Person can have more than one Name you may need to make the match more specific, e.g. Name[last()][not(../Age)] to add the Age after the last occurrence of Name within the Person rather than after every one.

Upvotes: 3

Related Questions