GJDuquette
asked on
XSL to Select Unique and Sort
Hello Experts,
I am trying to create a XSL to give the output below:
Time Judge Case Defendant
09:00 AM EAM CR04003107 BLOUGH, JOE
10:00 AM EAM CR04004200 BLOUGH, FRED
I need to select only Court=CR3 and unique cases base on the first ten characters of CaseNo.
Any suggestions on how to approach this would be appreciated.
I am trying to create a XSL to give the output below:
Time Judge Case Defendant
09:00 AM EAM CR04003107 BLOUGH, JOE
10:00 AM EAM CR04004200 BLOUGH, FRED
I need to select only Court=CR3 and unique cases base on the first ten characters of CaseNo.
Any suggestions on how to approach this would be appreciated.
XML DATA:
<?xml version="1.0" encoding="ISO-8859-1"?>
<CRDocket>
<Case>
<Time>09:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR3</Court>
<CaseNo>CR04003107-01</CaseNo>
<Defendant>BLOUGH, JOE</Defendant>
</Case>
<Case>
<Time>09:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR3</Court>
<CaseNo>CR04003107-02</CaseNo>
<Defendant>BLOUGH, JOE</Defendant>
</Case>
<Case>
<Time>09:00 AM</Time>
<Judge>NAT</Judge>
<Court>CR5</Court>
<CaseNo>CR11000475-01</CaseNo>
<Defendant>DOUGH, JANE</Defendant>
</Case>
<Case>
<Time>10:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR3</Court>
<CaseNo>CR04004200-02</CaseNo>
<Defendant>BLOUGH, FRED</Defendant>
</Case>
</CRDocket>
<xsl:stylesheet version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xsi xsl xsd">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no" omit-xml-declaration="yes"/>
<xsl:key name="CaseNoKey" match="Case" use="substring(CaseNo,1,10)"/>
<xsl:template match="/CRDocket/Case[Court!='CR3']"/>
<xsl:template match="/CRDocket">
<xsl:text>Time Judge Case Defendant</xsl:text>
<xsl:for-each select="Case[generate-id() = generate-id(key('CaseNoKey', substring(CaseNo, 1, 10))[1])][Court='CR3']">
<xsl:text disable-output-escaping="yes">
</xsl:text>
<xsl:value-of select="Time"/>
<xsl:text disable-output-escaping="yes"> </xsl:text>
<xsl:value-of select="Judge"/>
<xsl:text disable-output-escaping="yes"> </xsl:text>
<xsl:value-of select="substring(CaseNo,1,10)"/>
<xsl:text disable-output-escaping="yes"> </xsl:text>
<xsl:value-of select="Defendant"/>
</xsl:for-each>
</xsl:template>
-->
</xsl:stylesheet>
for uniqyueness, I used the muenchian technique
http://www.jenitennison.com/xslt/grouping/muenchian.xml
http://www.jenitennison.com/xslt/grouping/muenchian.xml
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Very nice! How can we add sorting by time and defendant?
<xsl:for-each select="Case[generate-id() = generate-id(key('CaseNoKey', substring(CaseNo, 1, 10))[1])][Court='CR3']">
<xsl:sort select="Time,Defendant" order="ascending"/>
@sweetfa2,
I appreciate that you have a different style and share your personal preference,
but your alternative is fundamentally wrong.
First let me say that in my point of view you have two types of discriminators:
- the technical ones (from muenchian) are only there to make sure you select unique nodes
- the business ones (value of Court)
Seperating both technical discriminators from business discriminators in my opninion gives better maintenance
But I accept this is a matter of taste and you posed it as a personal reference, so no problem with that
Now, you are selecting the first unique node matching the 'case' key and after that you add the court restriction.
That works in this example. But you seem to pose it as a general approach.
What would happen if a case changes court after the first hearing
I have altered the XML for this thought experiment
<Case>
<Time>09:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR4</Court>
<CaseNo>CR04003107-01</Cas eNo>
<Defendant>BLOUGH, JOE</Defendant>
</Case>
<Case>
<Time>09:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR3</Court>
<CaseNo>CR04003107-02</Cas eNo>
<Defendant>BLOUGH, JOE</Defendant>
</Case>
You would simply miss this case given your approach.
So there is a fundamental error in there
If you would alter my code to fit your prefered style,
you should do this
<xsl:apply-templates select="Case[generate-id() = generate-id(key('case', substring(CaseNo, 1, 10))[Court = 'CR3'][1])]"/>
I appreciate that you have a different style and share your personal preference,
but your alternative is fundamentally wrong.
First let me say that in my point of view you have two types of discriminators:
- the technical ones (from muenchian) are only there to make sure you select unique nodes
- the business ones (value of Court)
Seperating both technical discriminators from business discriminators in my opninion gives better maintenance
But I accept this is a matter of taste and you posed it as a personal reference, so no problem with that
Now, you are selecting the first unique node matching the 'case' key and after that you add the court restriction.
That works in this example. But you seem to pose it as a general approach.
What would happen if a case changes court after the first hearing
I have altered the XML for this thought experiment
<Case>
<Time>09:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR4</Court>
<CaseNo>CR04003107-01</Cas
<Defendant>BLOUGH, JOE</Defendant>
</Case>
<Case>
<Time>09:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR3</Court>
<CaseNo>CR04003107-02</Cas
<Defendant>BLOUGH, JOE</Defendant>
</Case>
You would simply miss this case given your approach.
So there is a fundamental error in there
If you would alter my code to fit your prefered style,
you should do this
<xsl:apply-templates select="Case[generate-id()
Then, sweetfa2s suggestion for sort is wrong
<xsl:sort select="Time,Defendant" order="ascending"/>
is an illegal XPath expression
Here is what you should do
<xsl:sort select ="Time" order="ascending"/>
<xsl:sort select="Defendant" order="ascending"/>
will first order on Time (ascending, text data type)
and then on defendant (ascending text data type)
If you want to sort on Defendant first, you can simply swap
<xsl:sort select="Defendant" order="ascending"/>
<xsl:sort select ="Time" order="ascending"/>
This sorting can be used with the for-each approach sweetfa uses,
but it can also be used with my apply-templates approach
(for maintenance reasons I do prefer apply-templates over for-each)
<xsl:apply-templates select="Case[generate-id() = generate-id(key('case', substring(CaseNo, 1, 10))[1])]">
<xsl:sort select="Time" order="ascending"/>
<xsl:sort select="Defendant" order="ascending"/>
</xsl:apply-templates>
<xsl:sort select="Time,Defendant" order="ascending"/>
is an illegal XPath expression
Here is what you should do
<xsl:sort select ="Time" order="ascending"/>
<xsl:sort select="Defendant" order="ascending"/>
will first order on Time (ascending, text data type)
and then on defendant (ascending text data type)
If you want to sort on Defendant first, you can simply swap
<xsl:sort select="Defendant" order="ascending"/>
<xsl:sort select ="Time" order="ascending"/>
This sorting can be used with the for-each approach sweetfa uses,
but it can also be used with my apply-templates approach
(for maintenance reasons I do prefer apply-templates over for-each)
<xsl:apply-templates select="Case[generate-id()
<xsl:sort select="Time" order="ascending"/>
<xsl:sort select="Defendant" order="ascending"/>
</xsl:apply-templates>
As a final comment
In most XSLT1 processors (there is not much internal optimisation going on)
indexing with a predicate in the key has better performance than being selective later
So, on large datasets there could be a performance benefit in my approach
I also see a lot of d-o-e (disable-output-escape) in sweetfa's code.
It serves nothing and it is generally understood that d-o-e should preferably not be used
<xsl:text disable-output-escaping="y es"> </xsl:text>
in the spaces there is nothing to escape, so why add it there?
d-o-e is not a mandatory aspect of the XSLT1 recommendation, which means that a processor can still be compliant without supporting it.
The Firefox transformer for example does not support it, and still is considered a compliant XSLT processor.
So use it sparsely and only if you control the processor (don't use it in browser XSLT)
I see a lot of XSLT programmers use d-o-e by default on every xsl:text (if they do that they should also use it on every xsl:value-of to be consistent)
The main reason is that some programs override the XSLTs serialisation settings
but this should be changed in teh java/VB/C# code that is calling the XSLT, not by throwing d-o-e allover the stylesheet
In most XSLT1 processors (there is not much internal optimisation going on)
indexing with a predicate in the key has better performance than being selective later
So, on large datasets there could be a performance benefit in my approach
I also see a lot of d-o-e (disable-output-escape) in sweetfa's code.
It serves nothing and it is generally understood that d-o-e should preferably not be used
<xsl:text disable-output-escaping="y
in the spaces there is nothing to escape, so why add it there?
d-o-e is not a mandatory aspect of the XSLT1 recommendation, which means that a processor can still be compliant without supporting it.
The Firefox transformer for example does not support it, and still is considered a compliant XSLT processor.
So use it sparsely and only if you control the processor (don't use it in browser XSLT)
I see a lot of XSLT programmers use d-o-e by default on every xsl:text (if they do that they should also use it on every xsl:value-of to be consistent)
The main reason is that some programs override the XSLTs serialisation settings
but this should be changed in teh java/VB/C# code that is calling the XSLT, not by throwing d-o-e allover the stylesheet
Open in new window