13

I am new to XSLT in general so please bear with me...

With that in mind, what I am trying to do is check for a certain tag in the XML. If it is there I want to apply a template. If not, I want to add it (as a blank value). Basically always forcing it to be in the final output. How would I do this?

I had something like this...

<xsl:choose>
    <xsl:when test="@href">
        <xsl:apply-templates select="country" />
    </xsl:when>
    <xsl:otherwise>
    </xsl:otherwise>
</xsl:choose>

The top poriton of the code is what I think I have wrong. Need something in the otherwise tag and my when part is wrong i think.

<xsl:template match="country">
    <xsl:if test=". != '' or count(./@*) != 0">
        <xsl:copy-of select="."/>
    </xsl:if>
</xsl:template>

Can anyone help? Thank you in advance.

EDIT:

Yes in the end i need at the very least a <country /> tag to be in the XML. But it is possible that it does not exist at all. If it doesn't exist, I have to put it in. An example good input would be <country>US</country>

Issa Fram
  • 2,556
  • 7
  • 33
  • 63
  • Consider to post two input samples (one with the data being present, the other with the data being missing), then show us the output you want to create for both. Then we can help with the proper XSLT. I am afraid your current description "checking for a certain tag" is not very precise and it is hard to tell whether `` meets your requirements. That would check whether the context node has an `href` attribute whereas checking for a "tag" sounds you want to check for certain element node. In your `otherwise` you simply might want to put a `foo` result el – Martin Honnen Apr 26 '11 at 14:14
  • '' is wrong i believe – Issa Fram Apr 26 '11 at 14:38
  • 1
    Good question, +1. See my answer for an even shorter and simpler solution than the currently accepted one. There are absolutely no XSLT conditional instructions in my solution! – Dimitre Novatchev Apr 27 '11 at 03:45

3 Answers3

12

In the template for the parent element the country element is expected to be in use e.g.

<xsl:template match="foo">
  <xsl:if test="not(country)">
    <country>US</country>
  </xsl:if>
  <xsl:apply-templates/>
</xsl:template>

Instead of foo use the name of the parent element. And of course you could also do other stuff like copying the element, I have focused on the if check. You do not really need an xsl:choose/when/otherwise in my view, the xsl:if should suffice as the apply-templates will not do anything with child elements that don't exist.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
12

Even simpler:

<xsl:template match="foo[not(country)]">
        <country>US</country>
    <xsl:apply-templates/>
</xsl:template>

Do note:

No XSLT conditional instructions (such as <xsl:if>) are used and they are not necessary.

Very often, the presence of <xsl:if> or <xsl:choose> is an indication that the code can be refactored and significantly improved by, among other things, getting rid of the conditional instructions.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • But what would you do if you needed to provide another default element? , for instance. – JRQ Jun 09 '20 at 19:54
  • @JRQ, In this particular case there is no specific source XML document provided, so one can only guess the desired schema. Say: ` US Washington ` Or, it could be (the possibilities are infinite): ` Washington ` – Dimitre Novatchev Jun 09 '20 at 20:09
  • I can see that. But, a more general solution would be better than one which no longer works when the number of tests is greater than one. – JRQ Jun 09 '20 at 20:22
  • @JRQ, Agreed. This is why anyone can ask a new question with the more specific scenario they are interested in. – Dimitre Novatchev Jun 09 '20 at 20:31
6

You don't even need any kind of Conditional Processing. This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="item[not(country)]">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
            <country>Lilliput</country>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

With this input:

<root>
    <item>
        <country>Brobdingnag</country>
    </item>
    <item>
        <test/>
    </item>
</root>

Output:

<root>
    <item>
        <country>Brobdingnag</country>
    </item>
    <item>
        <test></test>
        <country>Lilliput</country>
    </item>
</root>