0

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?

2 Answers 2

1

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" />
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for help. I tried your solution, but subtitle tag has been replaced two times: One 'Subtitle' is a descendant of 'TitleDetail' and another is a descendant of 'Product' element. I need only one 'Subtitle' tag inside 'TitleDetail'. P.S. <xsl:apply-templates select="../subtitle" /> - I use this line.
@Tamara I've added a couple of possible approaches to get around this.
0

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

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

1 Comment

Thanks for help. I forgot to mention that subtitle is not necessarily following sibling - it's just sibling.

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.