How to exclude child node in the output using xslt 1.0? - templates

See my xsltfiddle:
http://xsltfiddle.liberty-development.net/gWmuiKr/3
In the output on row one there is a date stamp that I don't want in it. It is in the source xml under
<analystePurchaseInvoices>
<currentDate>2018-12-03T14:50:08</currentDate>
</analystePurchaseInvoices>
I want that date out. But I don't know how.

Your stylesheet has only one template, and that template matches the purchaseInvoice element. You have no template matching the root analystePurchaseInvoices element or its other child currentDate. Therefore these elements are processed by the built-in templates rules. This results in the text contained in currentDate being copied to the output.
You can avoid this in a number of ways. For example, you could add an empty template to handle currentDate:
<xsl:template match="currentDate"/>
Or you could exclude currentDate from being processed at all by:
<xsl:template match="/analystePurchaseInvoices">
<xsl:apply-templates select="purchaseInvoice"/>
</xsl:template>

Related

XSLT 1.0 - change the order of lines

I want to change the order of line cac:InvoiceLine depending on this node:
cac:AdditionalItemProperty/cbc:Value
All InvoiceLines that have Item type=RC must be gruop at the end of lines, and all that have CU must be on the top.
If the mentioned values are the only ones you are concerned about, then it seems like you could just sort alphabetically by that value; see xsl:sort. You could just put this inside the xsl:for-each or xsl:apply-templates where you process your invoice lines:
<xsl:sort select="cac:AdditionalItemProperty/cbc:Value" />
On the other hand, if you only want to output only the line items with the mentioned values, you could select them separately. For example, assuming you have a template which matches your invoice lines, you'd first apply it to the 'CU' ones and then to the 'RC' ones:
<xsl:apply-templates select="cac:InvoiceLine[cac:AdditionalItemProperty/cbc:Value='CU']" />
<xsl:apply-templates select="cac:InvoiceLine[cac:AdditionalItemProperty/cbc:Value='RC']" />

XSLT 2.0 using key with except returns unexpected result

NB: title changed to reflect the problem better.
My xml documents contain an element <tei:seg #type #xml:id #corresp> which wrap little 'stories'. The attribute #corresp allows me to connect these stories to a master story. For example, these seg are all connected by their #corresp:
doc1.xml//seg[#type='dep_event' #corresp='#JKL' #xml:id='doc1-05']
doc2.xml//seg[#type='dep_event' #corresp='#JKL' #xml:id='doc2-06']
doc6.xml//seg[#type='dep_event' #corresp='#JKL' #xml:id='doc6-03']
My objective is: when the XSLT template finds a #corresp, find other seg in other documents with the same #corresp and output their respective `#xml:id``
So, in the above example, if the current seg was #xml:id='doc1-05', the template outputs a list: Corresponds to doc2-06, doc6-03
Until I can solve the current problems with XSLT collection() in eXist-DB, I'm falling back on my previous solution: a 'TEI corpus' xml document which maintains a master list of all related tei-xml documents via xi:include. This way I provide a single document node whereby the processor can access and search all the xml documents.
So, I declare the corpus document:
<xsl:variable name="corpus" select="doc('ms609_corpus.xml')"/>
Then create a key for the #corresp:
<xsl:key name="correspkey" match="//tei:seg[#type='dep_event' and #corresp]" use="#corresp"/>
Then I use the key with the doc() to search:
<xsl:when test="tei:seg[#type='dep_event' and #corresp]">
<xsl:variable name="correspvar"
select="data(self::seg[#type='dep_event' and #corresp]/#corresp)"/>
<xsl:text>Corresponds to </xsl:text>
<xsl:value-of select="data($corpus/(key('correspkey',$correspvar) except $correspvar)/#xml:id)" separator=", "/>
</xsl:when>
It returns the results, but the except should exclude the current #corresp. Yet it is included in the results.
The except operator works on sequences of nodes based on node identity, see https://www.w3.org/TR/xpath20/#combining_seq defining
The except operator takes two node sequences as operands and returns a
sequence containing all the nodes that occur in the first operand but
not in the second operand ... All these operators eliminate duplicate
nodes from their result sequences based on node identity
Based on that I think you simply want
<xsl:value-of select="$corpus/(key('correspkey', current()/#corresp) except current())/#xml:id)" separator=", "/>
Using data on nodes which atomizes nodes to values and then trying to use except which works on nodes doesn't seem to make sense to me.

how to use two conditions in select conditions in xslt when using Apply template

<xsl:apply-templates mode="block2sequence" select="NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][position()=1] and NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType][position()=1]"/>
why cant i use two conditions in above select condition, can any one suggest me
<xsl:apply-templates mode="block2"
select="NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][position()=1] "/>
why cant i use two conditions in above select condition
I guess this is to mean, "why can't the two conditions be specified in the same predicate?"
The answer is that the expression:
NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId and position() = 1]
isn't equivalent at all to the 1st expression above.
The first expression selects the first Table child of NewDataSet such that the string value of its CTD_CTD_PKG_ID child is equal to the string value of $PackageId. In this case we don't know which child (at which position) of NewDataSet will be selected -- any child that happens to be the first with the specified properties, will be selected.
On the other side, the latter expression selects the first Table child of NewDataSet only if the string value of its CTD_CTD_PKG_ID child is equal to the string value of $PackageId. In this case, if anything is selected, it would be the first Table child.
If you want an equivalent expression to the first one, that has only one predicate, one such expression is:
NewDataSet/Table
[CTD_CTD_PKG_ID =$PackageId
and
not(preceding-sibling::Table[CTD_CTD_PKG_ID =$PackageId ])
]
Update: The OP has published a code snippet:
<xsl:apply-templates mode="block2sequence" select=
"NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][position()=1]
and
NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType][position()=1]"/>
This code will cause an error thrown at compile time by the XSLT processor.
The value of the select attribute is a boolean (expr1 and expr2), however templates in XSLT 1.0 and XSLT 2.0 can only be applied on nodes. A boolean isn't a node -- hence the error.
Solution:
My first guess is that you want templates to be applied on both nodes. If this is so, then use:
<xsl:apply-templates mode="block2sequence" select=
"NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][1]
|
NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType][1]"/>
My second guess is that you want templates applied only on the first of the two nodes. If this is so, then use:
<xsl:apply-templates mode="block2sequence" select=
"(NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId]
|
NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType]
)
[1]
"/>
Notes:
Please, learn how to ask a question -- provide all relevant data and explain -- in the question, not in subsequent comments.
Did you know that [1] is equivalent to [position()=1] and is shorter?
You can use two conditions and your expression looks perfectly correct. If it is failing with an error, please tell us the error. If it is not selecting what you want, then (a) show us your source document, and (b) tell us what you want to be selected.
(You know, your question gives so little information, you don't give the impression that you really want an answer.)

XSL beginner question

I want to understand, in general, what this means:
<xsl:template match="foo:barLists[#mode = 'Dummy Filter']"
mode="dummy-filter-cache" priority="2">
I'm looking for some insight to what this does so I may learn a bit about XSL
<xsl:template
This element defines a template. We'll give it data later with an apply-template element
match="foo:barLists[#mode = 'Dummy Filter']"
This template uses the element barLists in the namespace foo which has an attribute of mode which is set to "Dummy Filter". i.e. <foo:barList mode="Dummy Filter"> .... </foo:barList>
mode="dummy-filter-cache"
This tempalte has a mode of "dummy-filter-cache". I have no idea what that means. w3schools.com only says about mode: "Optional. Specifies a mode for this template"
priority="2">
This tempate has a priority of 2. If there's another template which also matches that element with a priority of 1, that one wins.
In general, you are matching specific nodes with specific attributes in an XML file.
I suggest you look at a tutorial on XSL.
mode attribute of xsl:template allows you to create several templates that have same match attribute. With mode you can choose which one of these templates gets applied in different cases. This might be useful if you need to apply the same content several times with different formatting at some of these times.
A template with a mode will be instantiated only when you have set the same mode on an xsl:apply-templates element whose select attribute matches the match attribute on the xsl:template element.
Let's suppose you have templates
<xsl:template match="foo">
and
<xsl:template match="foo" mode="bar">
Then <xsl:apply-templates select="foo" mode="bar"/> will match the template #2 while
<xsl:apply-templates select="foo"/> and <xsl:apply-templates/> will match the template #1.

XSL: Combining grouping and call-template

I've read with interest the techniques available on the web to extract a unique list of items from a XML file containing duplicates using XSL.
These range into 2 categories:
1) The Muenchian method (example: http://www.jenitennison.com/xslt/grouping/)
2) Or the previous-sibling look-up
These both rely on an XPath expression to select the data to group by.
However, in the XML file that I'm trying to work out, the data is not present "natively" in the XML file. I am using a xsl:template to compute some aggregated data from my elements. And I would like to group based on the aggregated data.
For example I have:
<filmsreview>
<record><data name='movie'>Star Wars</data><data name='ratings'>John:Good, Mary:Good</data></record>
<record><data name='movie'>Indiana Jones</data><data name='ratings'>John:Good, Mary:Bad, Helen:Average</data></record>
<record><data name='movie'>Titanic</data><data name='ratings'>John:Bad, Helen:Good</data></record>
</filmsreview>
I know that the structuration of data is not perfect and that by creating sub-elements I could do something easier, but I cannot change the data source easily, so let's take this as a challenge.
And I would like to build a recap where I have John's distinct ratings:
John's ratings:
Good
Bad
I have a xsl:template that can take a record element and return John's rating for this record:
Example:
<xsl:template name="get_rating">
<xsl:param name="reviewer" />
<!-- I use some string manipulation, and xsl:value-of to return the review for John-->
</xsl:template>
I can just call it under a xsl:for-each to get the exhaustive list of John's review. But I cannot combine this call with any of the methods to get unique values.
Do I have to use an intermediary XSL to convert my XML file to a more structured way? Or can I do in a single step?
Many thanks
Gerard
Hmm... This should be possible using xslt variables and the nodeset method, perhaps something like this:
<xsl:variable name="_johnsRatings">
<xsl:apply-templates select="/" mode="johnsRatings" />
</xsl:variable>
<xsl:variable name="johnsRatings" select="msxsl:node-set($_johnsRatings)" />
<xsl:template match="/" mode="johnsRatings">
<Ratings>
<xsl:for-each select="/filmsReview/record/data[#name='ratings']">
<Rating><xsl:call-template name="get_rating" /></Rating>
</xsl:for-each>
</Ratings>
</xsl:template>
At this point, it should be possible to query the $johnsRatings variable using standard XPath queries, and you can use either of the two methods you mentioned above to retrieve unique values from it...
Hope that helps
EDIT:
I don't know what XSLT engine you are using, I assumed you have access to the msxsl:node-set() function. However, most XSLT processors have similar methods, so you might have to search around for an equivalent method in your processor