0

I have an XML file that contains identical data in two elements. One element "HTMLDesc" is intended to be used on the web and includes HTML characters, like bullets. The other "Fulldesc" is intended for print purposes. We'd like to use the HTML element in all cases when both elements contain data. BUT, when the HTML element is missing or empty, I want to use the print-based element. I know this is an IF/ELSE function, but how do you write the XSLT to choose the second element when the first is empty or missing?

Here is a sample of the XML:

<dataroot>
<CaseStudies>
<H3>New Patient</H3>
<Fulldesc>Office or other outpatient visit.</Fulldesc>
<HTMLdesc><b>Office or other outpatient visit</b></HTMLdesc>
</CaseStudies>

<CaseStudies>
<H3>Old Patient</H3>
<Fulldesc>Office or other outpatient visit.</Fulldesc>
</CaseStudies>

<CaseStudies>
<H3>Young Patient</H3>
<Fulldesc>Office or other outpatient visit.</Fulldesc>
<HTMLdesc><b>Office or other outpatient visit</b></HTMLdesc>
</CaseStudies>

<CaseStudies>
<category>1</category>
<GroupNo>2</GroupNo>
<H3>Female Patient</H3>
<Fulldesc>Office or other outpatient visit.</Fulldesc>
<HTMLdesc />
</CaseStudies>
</dataroot>

Here is the basic XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>

<xsl:output method="xml"/>
<xsl:template match="/">

<dataroot>
<xsl:apply-templates select="dataroot"/>
</dataroot>
</xsl:template>

<xsl:template match="CaseStudies">
<CaseStudies>
<xsl:apply-templates select="H3"/>
<xsl:apply-templates select="HTMLdesc"/>
</CaseStudies>
</xsl:template>

<H3><xsl:value-of select="."/></H3></xsl:template>

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

</xsl:stylesheet>
1
  • Is there a missing <xsl:template match="H3">? Does Fullldesc always precede HTMLdesc? Commented Mar 20, 2014 at 16:40

3 Answers 3

1

You can decide inside an xsl:choose element whether to perform apply-templates on the HTMLdesc or Fulldesc element.

This solution also selects the Fulldesc element if HTMLdesc contains only whitespace characters.

Note that you do not really change much, except for selecting certain nodes under given conditions. In a situation like this it is always advisable to start with an identity transform (rather than typing out all elements again as is done in your stylesheet)

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

and add other templates as you need them.

Stylesheet

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

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

  <xsl:template match="category|GroupNo"/>

  <xsl:template match="CaseStudies">
      <xsl:copy>
      <xsl:apply-templates select="@*|comment()|processing-instruction()|H3"/>
          <xsl:choose>
              <xsl:when test="normalize-space(HTMLdesc) = ''">
                  <xsl:apply-templates select="Fulldesc"/>
              </xsl:when>
              <xsl:otherwise>
                  <xsl:apply-templates select="HTMLdesc"/>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Output

<?xml version="1.0" encoding="utf-8"?>
<dataroot>
   <CaseStudies>
      <H3>New Patient</H3>
      <HTMLdesc>
         <b>Office or other outpatient visit</b>
      </HTMLdesc>
   </CaseStudies>
   <CaseStudies>
      <H3>Old Patient</H3>
      <Fulldesc>Office or other outpatient visit.</Fulldesc>
   </CaseStudies>
   <CaseStudies>
      <H3>Young Patient</H3>
      <HTMLdesc>
         <b>Office or other outpatient visit</b>
      </HTMLdesc>
   </CaseStudies>
   <CaseStudies>
      <H3>Female Patient</H3>
      <Fulldesc>Office or other outpatient visit.</Fulldesc>
   </CaseStudies>
</dataroot>
Sign up to request clarification or add additional context in comments.

1 Comment

I will check your method and see if it works for me.
1

Here's another option that is similar to both of the other answers, but doesn't require an xsl:choose or multiple xsl:apply-templates.

It's basically applying templates to either:

Fulldesc if HTMLdesc is empty or doesn't exist

or

HTMLdesc if it is not empty

whichever it finds first.

XML Input

<dataroot>
    <CaseStudies>
        <H3>New Patient</H3>
        <Fulldesc>Office or other outpatient visit.</Fulldesc>
        <HTMLdesc><b>Office or other outpatient visit</b></HTMLdesc>
    </CaseStudies>

    <CaseStudies>
        <H3>Old Patient</H3>
        <Fulldesc>Office or other outpatient visit.</Fulldesc>
    </CaseStudies>

    <CaseStudies>
        <H3>Young Patient</H3>
        <Fulldesc>Office or other outpatient visit.</Fulldesc>
        <HTMLdesc><b>Office or other outpatient visit</b></HTMLdesc>
    </CaseStudies>

    <CaseStudies>
        <category>1</category>
        <GroupNo>2</GroupNo>
        <H3>Female Patient</H3>
        <Fulldesc>Office or other outpatient visit.</Fulldesc>
        <HTMLdesc />
    </CaseStudies>
</dataroot>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="CaseStudies">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[not(self::Fulldesc) and not(self::HTMLdesc)]|
                (Fulldesc[not(string(../HTMLdesc))]|HTMLdesc[string()])[1]"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

XML Output

<dataroot>
   <CaseStudies>
      <H3>New Patient</H3>
      <HTMLdesc>
         <b>Office or other outpatient visit</b>
      </HTMLdesc>
   </CaseStudies>
   <CaseStudies>
      <H3>Old Patient</H3>
      <Fulldesc>Office or other outpatient visit.</Fulldesc>
   </CaseStudies>
   <CaseStudies>
      <H3>Young Patient</H3>
      <HTMLdesc>
         <b>Office or other outpatient visit</b>
      </HTMLdesc>
   </CaseStudies>
   <CaseStudies>
      <category>1</category>
      <GroupNo>2</GroupNo>
      <H3>Female Patient</H3>
      <Fulldesc>Office or other outpatient visit.</Fulldesc>
   </CaseStudies>
</dataroot>

Comments

0

You can select the HTMLdesc and Fulldesc templates according to the contents of HTMLdesc:

<CaseStudies>
    <xsl:apply-templates select="H3"/>
    <xsl:apply-templates select="HTMLdesc[string()]"/>
    <xsl:apply-templates select="Fulldesc[../HTMLdesc[not(string())] or not(../HTMLdesc)]"/>
</CaseStudies>

The second apply-templates will select the HTMLdesc template only if it is not empty.

The third one will select the Fulldesc template (which you have to add to your stylesheet) if the HTMLdesc sibling is empty or non-existent.

Comments

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.