Tamara
Tamara

Reputation: 2980

Check if node exist in document with XSLT

I'm transforming one XML file to another XML format.

Here is sample source file:

<xml>
     <title>Pride and Prejudice</title>
     <subtitle>Love Novel</subtitle>
</xml>

And here is xsl file:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
    <Product>
        <xsl:apply-templates/>
    </Product>
</xsl:template>

<xsl:template match="title">
    <TitleDetail>
        <TitleType>01</TitleType>
        <TitleElement>
            <TitleElementLevel>01</TitleElementLevel>
            <TitleText><xsl:value-of select="current()"/></TitleText>
             <!--Here Problem!!!-->
            <xsl:if test="subtitle"> 
                <Subtitle>123</Subtitle>
            </xsl:if>
        </TitleElement>
    </TitleDetail>
</xsl:template>

Idea is that if source file contains subtitle tag I need to insert "Subtitle" node to the "TitleDetail", but 'if' condition returns false. How to check if source file has subtitle information?

Upvotes: 0

Views: 8856

Answers (2)

Ian Roberts
Ian Roberts

Reputation: 122414

I would define another template

<xsl:template match="subtitle">
  <Subtitle><xsl:value-of select="."/></Subtitle>
</xsl:template>

then in the main title template apply templates to ../subtitle (i.e. navigate from the title element to the corresponding subtitle)

<TitleText><xsl:value-of select="."/></TitleText>
<xsl:apply-templates select="../subtitle" />

You don't need the if test, as the apply-templates will do nothing if its select doesn't find any matching nodes.

You will also need to exclude the subtitle element when applying templates to the children of the xml element, otherwise you will get a second copy of the Subtitle output element after the TitleDetail as well as the one inside it. The easiest way is to replace your match="/" template with the following match="/*" one instead

<xsl:template match="/*">
    <Product>
        <xsl:apply-templates select="*[not(self::subtitle)]/>
    </Product>
</xsl:template>

If you have similar special handling for other elements in other templates you can add those to the not(), i.e. select="*[not(self::subtitle | self::somethingelse)]".

Alternatively you could make use of template modes

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
    <Product>
        <xsl:apply-templates/>
    </Product>
</xsl:template>

<xsl:template match="title">
    <TitleDetail>
        <TitleType>01</TitleType>
        <TitleElement>
            <TitleElementLevel>01</TitleElementLevel>
            <TitleText><xsl:value-of select="."/></TitleText>
            <xsl:apply-templates select="../subtitle" mode="in-title" />
        </TitleElement>
    </TitleDetail>
</xsl:template>

<!-- in "in-title" mode, add a Subtitle element -->
<xsl:template match="subtitle" mode="in-title">
  <Subtitle><xsl:value-of select="."/></Subtitle>
</xsl:template>

<!-- in normal mode, do nothing -->
<xsl:template match="subtitle" />

Upvotes: 1

Andrey Shchekin
Andrey Shchekin

Reputation: 21609

If I understand the question correctly, you can try this:

<xsl:if test="following-sibling::subtitle"> 
  <Subtitle>123</Subtitle>
</xsl:if>

Upvotes: 0

Related Questions