Jacob
Jacob

Reputation: 728

XSLT: Breaking out of for-each once it's found what I want it too

So here is my problem: The XML document is a template for a CV with several <employment> nodes inside a <work> node which each will contain a <title> node containing the Job title that this <work> element refers too. Also there is an education element that contains <subject> nodes that contain the names of any qualifications the person also has.

I need to search through all the <title> nodes and all the <subject> nodes, and if any ONE of the title nodes contains the words 'Web Developer', AND if any ONE of the <subject> nodes contains either the words, 'computer', 'information', or 'web', then it generates an acceptance letter for the job they're applying for, and if either one of those conditions are not met, it generates a rejection letter.

I've been trying to this the following way, but if it see's any of the words it wants more than once, it will generate several blocks of text. I'm struggling to work out how I can stop this. Is there a way to do it using the code I've already written or is there another way I should go about this? I hope I've explained this well enough. Sorry if the title is vague, I wasn't sure what to put.

Code:

<xsl:for-each select="work/employment/title">
  <xsl:choose>
    <xsl:when test="contains(.,'Web developer')">
      <xsl:for-each select="//qualifications/education/grade/subject">
        <xsl:if test="contains(.,'computer') or 
                      contains(.,'information') or 
                      contains(.,'Web')">
            Insert Acceptance Letter
        </xsl:if>
      </xsl:for-each>
    </xsl:when>
    <xsl:otherwise>
      Insert Rejection Letter
    </xsl:otherwise>
  </xsl:choose>
</xsl:for-each>

Upvotes: 2

Views: 72

Answers (3)

Buck Doyle
Buck Doyle

Reputation: 6387

Your output is repeating because you’re printing the acceptance/rejection string every time you encounter a matching <employment/>. There’s no way to break out of a <xsl:for-each/>, XSL isn’t really like that. Instead, why not just test whether there exist any matching jobs from outside?

Here’s an example:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="cv">
    <xsl:choose>
      <xsl:when test="work/employment/title[contains(., 'Web developer')] and
          (qualifications/education/grade/subject[contains(., 'computer') or 
                                     contains(., 'information') or
                                     contains(., 'Web')])">Acceptance</xsl:when>
      <xsl:otherwise>Rejection</xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Upvotes: 3

helderdarocha
helderdarocha

Reputation: 23637

You don't need for-each. You can find what you need with a single XPath expression:

contains(//work/employment/title, 'Web developer') 
and 
contains(//qualifications/education/grade/subject, 'computer') 
  or contains(//qualifications/education/grade/subject, 'information') 
  or contains(//qualifications/education/grade/subject, 'Web') 

You can use it for matching a template, for example:

<xsl:template match="work/employment/title[contains(.,'Web developer')]">
    <xsl:choose>
        <xsl:when test="//qualifications/education/grade/subject[contains(.,'computer') or contains(.,'information') or contains(.,'Web')]">
            Insert Acceptance Letter
        </xsl:when>
        <xsl:otherwise>
            Insert Rejection Letter
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Upvotes: 1

Mads Hansen
Mads Hansen

Reputation: 66714

You are thinking of this in terms of procedural code. There is no need for xsl:for-each. Rather than breaking out of a for loop when you get a hit, address the elements with the specified criteria and then evaluate whether or not they are selected.

The @test for xsl:when will evaluate to true if the selected items are present.

<xsl:choose>
    <xsl:when test="/cv/work/employment/title[contains(., 'Web developer')] 
                   and 
             /cv/qualifications/education/grade/subject[contains(.,'computer') or 
                                                     contains(.,'information') or 
                                                     contains(.,'Web')]">
        Insert Acceptance Letter
    </xsl:when>
    <xsl:otherwise>Insert Rejection Letter</xsl:otherwise>
</xsl:choose>

Upvotes: 1

Related Questions