[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 236
  • Last Modified:

Filtering on multiple params in XSL

Gertone helped me a lot with this and I thought I had it, but I'm still stumped.  I'm trying to use XSL to filter on multiple elements from the following XML file.  

<NODES>
  <APPLICANT>
    <NAME>Sean McGowan</NAME>
    <PREFERRED></PREFERRED>
    <FLAGGED>1</FLAGGED>
  </APPLICANT>
  <APPLICANT>
    <NAME>Carol Gillette</NAME>
    <PREFERRED>1</PREFERRED>
    <FLAGGED></FLAGGED>
</NODES>
 
I'm passing parameters to XSL with the names of the elements I want to match and the values they should match.  The hard part is that I want a rows to display whenever EITHER of the filter elements matches the desired value.   Here are the params:

  <xsl:param name="filter1" />
  <xsl:param name="filter1value" />
  <xsl:param name="filter2" />
  <xsl:param name="filter2value" />

And here is my select statement:

<xsl:for-each select="//APPLICANT[
         *      [(name() = $filter1 and . = $filter1value) or (string-length($filter1) = 0)]
                [(name() = $filter2 and . = $filter2value) or (string-length($filter2) = 0)]
]">

I pass the parameters $filter1 = "FLAGGED" &  $filter1value = "1", for example.
This works fine when either of the parameters are set, but returns no rows when both are set by passing $filter2 = "PREFERRED" &  $filter2value = "1", too .  I could use an OR statement instead, such as:

<xsl:for-each select="//APPLICANT[
         *      [(name() = $filter1 and . = $filter1value) or (string-length($filter1) = 0)]
         |*    [(name() = $filter2 and . = $filter2value) or (string-length($filter2) = 0)]
]">

But this returns true for all rows when one of the parameters isn't set.  I need to be able to filter on either or both parameters.  I think the problem with the AND method is that the name() function is looking at the XML elements one at a time.  If name() = $filter1 in the first predicate, it won't in the second.  What can I do?
0
etd_onlineguys
Asked:
etd_onlineguys
  • 3
1 Solution
 
Geert BormansCommented:
is this what you want

<xsl:for-each select="//APPLICANT[
         *     [(name() = $filter1 and . = $filter1value) and not(string-length($filter1) = 0)]
         |*    [(name() = $filter2 and . = $filter2value) and not(string-length($filter2) = 0)]
]">
0
 
Geert BormansCommented:
etd_onlineguys,

ignore my previous suggestion

I think we have to break tis stuff apart to really see what you need
so if I go wrong somewhere, just tell me

for only one filter this would work
            select="//APPLICANT[
            *[(name() = $filter1 and . = $filter1value) or (string-length($filter1) = 0)]
            ]">
applicants would be selected if $filter1 was given the matching value
or when the filter would be left open

the problem arises when you use two filters
one filter would be selective and the other not, leading to no filtering
so we have to disable the empty filter test, if one of the other filters is set

would be something like this
           select="//APPLICANT[
             *[(name() = $filter1 and . = $filter1value) or ((string-length($filter1) = 0) and (string-length($filter2) = 0))]
            ]">

with two filters this works
        <xsl:for-each
            select="//APPLICANT[
            *[(name() = $filter1 and . = $filter1value) or ((string-length($filter1) = 0) and (string-length($filter2) = 0))]
          |  *[(name() = $filter2 and . = $filter2value) or ((string-length($filter1) = 0) and (string-length($filter2) = 0))]
            ]">

now maybe you want to have three filters
this can become clumsy, but we can rewrite this
because basically if no filter is passed, you want to select all the rows, otherwise you apply the filters

        <xsl:for-each
            select="//APPLICANT[
            *[(name() = $filter1 and . = $filter1value)]
          |  *[(name() = $filter2 and . = $filter2value)]
            | * [(string-length($filter1) = 0) and (string-length($filter2) = 0)]
            ]">

does that job and is pretty extensible

Now I am not sure about having two filters specified
    <xsl:param name="filter1" >NAME</xsl:param>
    <xsl:param name="filter1value" >Sean McGowan</xsl:param>
    <xsl:param name="filter2" >PREFERRED</xsl:param>
    <xsl:param name="filter2value" >2</xsl:param>

do you want all the applicants that have the name Sean... plus all the applicants that have a preferred of 2 (union)
the above solution would give you that

if you want all the applicants that have the name Sean... and  a preferred of 2 (intersection)
you need this

        <xsl:for-each
            select="//APPLICANT[
           *[name() = $filter1] = $filter1value and *[name() = $filter2] = $filter2value
            or *[name() = $filter1] = $filter1value and (string-length($filter2) = 0)
            or *[name() = $filter2] = $filter2value and (string-length($filter1) = 0)
            or ((string-length($filter1) = 0) and (string-length($filter2) = 0))
            ]">

and that is a bit less extensible, but still OK for three if you want

cheers

Geert


0
 
etd_onlineguysAuthor Commented:
Geert,
I thank you for this elegant and extensible solution.  You make it look easy.
David
0
 
Geert BormansCommented:
you are welcome David
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now