Need to remove Note: from the start of a string using XSLT - xslt

I have been tasked with styling XML and so far nearly all of the tags I have been given are either p or ph which just display the contents of the tag on screen and I can style as required
eg:
<p outputclass="LC AStatProv">Council Regulation (EC) No 2201/2003 of 27 November 2003 Concerning Jurisdiction and the Recognition and Enforcement of Judgments in Matrimonial Matters and in Matters of Parental Responsibility, repealing Regulation (EC) No 1347/2000 (Brussels II Revised) (2003) OJ L 338/1, Art 20</p>
will display as:
Council Regulation (EC) No 2201/2003 of 27 November 2003 Concerning Jurisdiction and the Recognition and Enforcement of Judgments in Matrimonial Matters and in Matters of Parental Responsibility, repealing Regulation (EC) No 1347/2000 (Brussels II Revised) (2003) OJ L 338/1, Art 20
Today I have noticed that a new note tag has been used and this is causing "Note: " to be prepended to the beginning of each string eg:
<p outputclass="LC ACourt"><note outputclass="CaseSearchCourt">Court of Appeal</note></p>
will display as:
Note: Re A (Abduction: Interim Directions: Accommodation by Local Authority) [2010] EWCA Civ 586 [2011] 1 FLR 1
The FO produced for this is:
<fo:block><fo:inline font-weight="bold" border-right-width="0pt" border-left-width="0pt">Note: </fo:inline> Court of Appeal</fo:block>
I have tried the following code gleaned from elsewhere on this site but it didn't remove the string I wanted:
<xsl:template match="note">
<note>
<xsl:if test="contains(., 'Note:')">
<xsl:call-template name="remove">
<xsl:with-param name="value" select="."/>
</xsl:call-template>
</xsl:if>
</note>
</xsl:template>
<xsl:template name="remove">
<xsl:param name="value"/>
<xsl:value-of select="concat(substring-before($value, 'Note:'), substring-after($value, 'Note:'))"/>
</xsl:template>
I have tried to see where XSLT gets the "Note: " from but can't find it anywhere so my next thought is to try and pattern match the string and remove "Note: " from the beginning of the string. Is this a good way to go about this and how would I do this?
thanks.
EDIT: Just to summarise my rambling and confusing question.
Where it renders:
Note: Re A (Abduction: Interim Directions: Accommodation by Local Authority) [2010] EWCA Civ 586 [2011] 1 FLR 1
I just want it to render:
Re A (Abduction: Interim Directions: Accommodation by Local Authority) [2010] EWCA Civ 586 [2011] 1 FLR 1
EDIT 2 and the answer
My initial investigations were based upon grepping for Note: which wasn't being found. I have though found the following:
<xsl:template match="*[contains(#class,' topic/note ')]">
and then commented out the following lines:
<!--<xsl:text>Note</xsl:text>-->
</xsl:otherwise>
</xsl:choose>
<!--<xsl:text>: </xsl:text>-->
</fo:inline>
<xsl:text> </xsl:text>
<xsl:apply-templates/>
And I have got rid of the "Note: ".
EDIT 3:
This didn't actually work as it created an opening block but didn't close it so I ended up removing the entire <xsl:template match="*[contains(#class,' topic/note ')]"> as we have no need for any type of notes in our system

This didn't actually work as it created an opening block but didn't close it so I ended up removing the entire <xsl:template match="*[contains(#class,' topic/note ')]"> as we have no need for any type of notes in our system

Related

xsl 3.0: How to process certain child elements first in xsl:apply-templates, then the remainder (overriding document order)

Assume my xml input is a MFMATR element with a few child elements, such as: TRLIST, INTRO, and SBLIST -- in that document order. I am converting to HTML.
I have a template that matches on the MFMATR element, and wants to run xsl:apply-templates on the 3 child elements, but I want INTRO to be processed first (listed first in the HTML). The other two (TRLIST and SBLIST) should keep their relative document order, as long as INTRO comes before both of them.
So I'd like to run <xsl:apply-templates select="INTRO, *"> but not have INTRO matched twice. (Using this syntax with xsl 3.0 causes dupes for me.) I also don't want to explicitly list every tag in the select expression, so unknown tags will still be processed.
A 2nd real life example is this: <xsl:apply-templates select="TITLE, CHGDESC, *"/>. Again, right now that is causing dupes I don't want.
I am using Saxon.
So I'd like to run <xsl:apply-templates select="INTRO, *"> but not have INTRO matched twice
Try:
<xsl:apply-templates select="INTRO, * except INTRO">
This seems to work. If someone has a better answer, let me know and I will change it.
There is no DRY violation here -- no repeated element names or variable names. I want it to look clean at all the call sites I will have.
It seems idiomatic to me since the function was pulled from w3's own website!
<xsl:template match="MFMATR">
<!-- Process INTRO first, no matter where it appears -->
<xsl:variable name="nodes" select="INTRO, *"/>
<xsl:apply-templates select="kp:distinct_nodes_stable($nodes)"/>
</xsl:template>
<xsl:template match="INTRO">
<xsl:variable name="nodes" select="TITLE, CHGDESC, *"/>
<xsl:apply-templates select="kp:distinct_nodes_stable($nodes)"/>
</xsl:template>
<!-- Discard duplicate elements in $seq, but keep their ordering -->
<!-- Adapted from https://www.w3.org/TR/xpath-functions/#func-distinct-nodes-stable -->
<xsl:function name="kp:distinct_nodes_stable" as="node()*">
<xsl:param name="seq" as="node()*"/>
<xsl:sequence select="fold-left($seq, (),
function($foundSoFar as node()*, $this as node()) as node()* {
if ($foundSoFar intersect $this)
then $foundSoFar
else ($foundSoFar, $this)
}) "/>
</xsl:function>

Cycle through xsl:key, output results, add delimiter - but skipping some results

I have an xsl:key that I am using with a large text-based XML file, using XSL 3 processed by Saxon.
<xsl:key name="getcommonseg" match="seg[#master-id]" use="#master-id"/>
This key is passed against thousands of elements that look like this:
<seg id="unique_id_here" master-id="master-id_here">
The #master-id is actually one of the #ids that 'groups' a number of <seg> together. For example:
<seg id="1-A" master-id="1-A"/>
<seg id="1-G" master-id="1-A"/>
<seg id="2-G" master-id="2-G"/>
<seg id="10-Y" master-id="1-A"/>
<seg id="5-C"/>
<seg id="6-B" master-id="2-G"/>
<seg id="8-R"/>
<seg id="11-K" master-id="2-G"/>
<seg id="19-D" master-id="1-A"/>
<seg id="22-T" master-id="2-G"/>
[...]
When I am reporting any given <seg>, I use the <xsl:key> to find the rest any related <seg>. For example, if I am positioned on:
<seg id="11-K" master-id="2-G"/>
I apply the key, and then cycle through the returned records, output them separated by a comma, and reject the record that is the current record:
<template match="seg[#commonid]">
<xsl:variable name="masterid" select="./#master-id"/>
<xsl:variable name="currentid" select="./#id"/>
<xsl:value-of select="'Seg' || ./#id || 'corresponds to the following seg id(s): ' "/>
<xsl:for-each select="key('getcommonseg',$masterid)">
<xsl:if test="./#id != $currentid">
<xsl:value-of select="./#id"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:if>
</xsl:for-each>
</template>
This outputs:
Seg 11-K corresponds to the following seg id(s): 2-G, 6-B, 22-T
As you can see it doesn't output value '11-K' and adds commas appropriately. However, if the position was:
<seg id="22-T" master-id="2-G"/>
It would output:
Seg 22-T corresponds to the following seg id(s): 2-G, 6-B, 11-K,
...adding the last comma inadvertently.
How can I avoid this problem of the final comma when the last node is skipped/rejected?
Thanks in advance.
As you are using XSLT 2 or 3 you should simply use <xsl:value-of select="(key('getcommonseg', #master-id) except .)/#id" separator=", "/>, instead of "cycling".

Select substrings dependent on context

I have this piece of XML and want to get the number immediately after Chapter
<para>Insolvency Rules, r 12.12, which gives the court a broad discretion, unfettered by the English equivalent of the heads of Order 11, r 1(1) (which are now to be found, in England, in CPR, Chapter 6, disapplied in the insolvency context by Insolvency Rules, r 12.12(1)). </para>
When I used this XSLT transform
<xsl:value-of select="translate(substring-after(current(),'Chapter'), translate(substring-after(current(),'Chapter'),'0123456789',''), '')"/>
I get this output
612121
Butut I want just 6.
Please let me know how I should do it.
I don't want to use a statement like
<xsl:value-of select="substring-before(substring-after(current(),'Chapter'), ,',')"/>
as the chapter number will be different in each instance, between 1 and 15.
Try this:
<xsl:variable name="vS" select="concat(substring-after(current(),'Chapter '),'Z')"/>
<xsl:value-of select=
"substring-before(translate($vS,translate($vS,'0123456789',''),'Z'),'Z')"/>
This is based on: https://stackoverflow.com/a/4188249/2115381 Thanks to
#Dimitre Novatchev
Update: If the quantity of space after the "Chapter" is not known you can use something like this:
<xsl:variable name="vS" select="concat(substring-after(current(),'Chapter'),'Z')"/>
<xsl:value-of select=
" translate(
substring-before(translate($vS,translate($vS,' 0123456789',''),'Z'),'Z')
, ' ','')"/>

Need to hide an xref only when it appears in a p element with a specific attribute

This is a re-work/new question after I got my previous one quite wrong by asking about the wrong element so here goes...
I'm trying to match and hide a xref within the following p element with the attribute of LC LRCaseName:
<p outputclass="LC LRCaseName"><i>Naseem Akhtar v Birmingham City Council</i> [2011] EWCA Civ 383 <xref href="#Public_PUBLICLAW_PLLR_2011PLLR002">Click here for transcript</xref></p>
so that I end up with "Naseem Akhtar v Birmingham City Council [2011] EWCA Civ 383"
We have other xrefs within other p outputclasses that we need to display otherwise I would hide it with <xsl:template match="p/xref"/> as per Dimitre Novatchev 's suggestion.
This particular xref will always contain "Click here for transcript" but the href will vary.
thanks.
EDIT:
I have tried the following code which doesn't make the xref background red:
<xsl:template match="p[#outputclass='LC LRCaseName']/xref">
<fo:block background-color="red" >
<xsl:apply-templates/>
</fo:block>
</xsl:template>
but if I use this:
<xsl:template match="p[#outputclass='LC LRCaseName']">
<fo:block background-color="red" >
<xsl:apply-templates/>
</fo:block>
</xsl:template>
It does make the entire P element background red and that includes the xref portion.
EDIT 2:
I have just had a thought, this system has another xsl file that has matches for xref. I'm wondering if my changes are being over-written by this file. Let me look into this further.
EDIT 3:
Just placed this very same code
<xsl:template match="p[#outputclass='LC LRCaseName']/xref" />
into dita2fo-links.xsl rather than the current file I was working on (dita2fo-elems.xsl) and it worked straight away. Obviously the links file is processed after the elems and all my matching was being overwritten by the later template matches.
Your match selector for the xref element could include the attribute value as well, like the following one:
p[#outputclass = 'LC LRCaseName']/xref
This transformation produces the wanted output:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="p[#outputclass='LC LRCaseName']/xref"/>
</xsl:stylesheet>

XSLT line counter - is it that hard?

I have cheated every time I've needed to do a line count in XSLT by using JScript, but in this case I can't do that. I simply want to write out a line counter throughout an output file. This basic example has a simple solution:
<xsl:for-each select="Records/Record">
<xsl:value-of select="position()"/>
</xsl:for-each>
Output would be:
1
2
3
4
etc...
But what if the structure is more complex with nested foreach's :
<xsl:for-each select="Records/Record">
<xsl:value-of select="position()"/>
<xsl:for-each select="Records/Record">
<xsl:value-of select="position()"/>
</xsl:for-each>
</xsl:for-each>
Here, the inner foreach would just reset the counter (so you get 1, 1, 2, 3, 2, 1, 2, 3, 1, 2 etc). Does anyone know how I can output the position in the file (ie. a line count)?
While it is quite impossible to mark the line numbers for the serialization of an XML document (because this serialization per se is ambiguous), it is perfectly possible, and easy, to number the lines of regular text.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="numberLines"/>
</xsl:template>
<xsl:template name="numberLines">
<xsl:param name="pLastLineNum" select="0"/>
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText)">
<xsl:value-of select="concat($pLastLineNum+1, ' ')"/>
<xsl:value-of select="substring-before($pText, '
')"/>
<xsl:text>
</xsl:text>
<xsl:call-template name="numberLines">
<xsl:with-param name="pLastLineNum"
select="$pLastLineNum+1"/>
<xsl:with-param name="pText"
select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>The biggest airlines are imposing "peak travel surcharges"
this summer. In other words, they're going to raise fees
without admitting they're raising fees: Hey, it's not a $30
price hike. It's a surcharge! This comes on the heels of
checked-baggage fees, blanket fees, extra fees for window
and aisle seats, and "snack packs" priced at exorbitant
markups. Hotels in Las Vegas and elsewhere, meanwhile, are
imposing "resort fees" for the use of facilities (in other
words, raising room rates without admitting they're
raising room rates). The chiseling dishonesty of these
tactics rankles, and every one feels like another nail in
the coffin of travel as something liberating and
pleasurable.
</t>
produces the desired line-numbering:
1 The biggest airlines are imposing "peak travel surcharges"
2 this summer. In other words, they're going to raise fees
3 without admitting they're raising fees: Hey, it's not a $30
4 price hike. It's a surcharge! This comes on the heels of
5 checked-baggage fees, blanket fees, extra fees for window
6 and aisle seats, and "snack packs" priced at exorbitant
7 markups. Hotels in Las Vegas and elsewhere, meanwhile, are
8 imposing "resort fees" for the use of facilities (in other
9 words, raising room rates without admitting they're
10 raising room rates). The chiseling dishonesty of these
11 tactics rankles, and every one feels like another nail in
12 the coffin of travel as something liberating and
13 pleasurable.
A line in an XML file is not really the same as an element. In your first example you don't really count the lines - but the number of elements.
An XML file could look like this:
<cheeseCollection>
<cheese country="Cyprus">Gbejna</cheese><cheese>Liptauer</cheese><cheese>Anari</cheese>
</cheeseCollection>
Or the exact same XML file can look like this:
<cheeseCollection>
<cheese
country="Cyprus">Gbejna</cheese>
<cheese>Liptauer</cheese>
<cheese>Anari</cheese>
</cheeseCollection>
which the XSLT will interpet exactly the same - it will not really bother with the line breaks.
Therefore it's hard to show line numbers in the way you want using XSLT - it's not really meant for for that kind of parsing.
Someone correct me if I'm wrong, but I'd say you would need Javascript or some other scripting language to do what you want.
Thanks for the responses guys - yup you're totally correct, some external function is the only way to get this behaviour in XSLT. For those searching, this is how I did this when using a compiled transform in .Net 3.5:
Create a helper class for your function(s)
/// <summary>
/// Provides functional support to XSLT
/// </summary>
public class XslHelper
{
/// <summary>
/// Initialise the line counter value to 1
/// </summary>
Int32 counter = 1;
/// <summary>
/// Increment and return the line count
/// </summary>
/// <returns></returns>
public Int32 IncrementCount()
{
return counter++;
}
}
Add an instance to an args list for XSLT
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(XmlReader.Create(s));
XsltArgumentList xslArg = new XsltArgumentList();
XslHelper helper = new XslHelper();
xslArg.AddExtensionObject("urn:helper", helper);
xslt.Transform(xd.CreateReader(), xslArg, writer);
Use it in you XSLT
Put this in the stylesheet declaration element:
xmlns:helper="urn:helper"
Then use like so:
<xsl:value-of select="helper:IncrementCount()" />
Generally, position() is referring to the number of the current node relative to the entire batch of nodes that is being processed currently.
With your "nested for-each" example, consecutive numbering can easily be achieved when you stop nesting for-each constructs and just select all desired elements at once.
With this XML:
<a><b><c/><c/></b><b><c/></b></a>
a loop construct like this
<xsl:for-each "a/b">
<xsl:value-of select="position()" />
<xsl:for-each "c">
<xsl:value-of select="position()" />
</xsl:for-each>
</xsl:for-each>
will result in
11221
bccbc // referred-to nodes
but you could simply do this instead:
<xsl:for-each "a/b/c">
<xsl:value-of select="position()" />
</xsl:for-each>
and you would get
123
ccc // referred-to nodes