What are the difference between // .// when we set path?
And the second question if I want to get some information of parent node in the for-each block how I can get it?
// is short for descendant axis.
If you say //para, it selects all para elements in the whole document.
When you say .//para, all the para elements which are descendants of the context node are selected.
For a demonstration, consider this XML:
<l1>
<para>1</para>
<l2>
<para>2</para>
<l3>
<para>3</para>
</l3>
</l2>
</l1>
and this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/l1">
<output>
<xsl:apply-templates select="l2"/>
</output>
</xsl:template>
<xsl:template match="l2">
<para-all>
<xsl:copy-of select="//para"/>
</para-all>
<para-context>
<xsl:copy-of select=".//para"/>
</para-context>
<parent>
<xsl:value-of select="name(parent::*)"/>
</parent>
</xsl:template>
</xsl:stylesheet>
And the output you will get is:
<?xml version="1.0" encoding="utf-8"?>
<output>
<para-all>
<para>1</para>
<para>2</para>
<para>3</para>
</para-all>
<para-context>
<para>2</para>
<para>3</para>
</para-context>
<parent>l1</parent>
</output>
The para-all gets all the paras in the document, whereas para-context gets only those paras which are descendant of l2(the context node)
And about how to select the parent, as in the code(see parent element in XSLT).
In the example above, the context node is l2 and by using parent axis, the parent element's name is being written.
You can use either parent::* or ../
Related
I am currently learning XSLT and a bit confused about an example I found in a book. To select all nodes, except a certain node, it says to use:
<xsl:apply-templates select="node()[not(self::CHILD_B)]" />
This works, but I was wondering why the self:: part is at all needed? Why does <xsl:apply-templates select="node()[not(CHILD_B)]" /> not work? After all <xsl:apply-templates select="CHILD_B" /> would work to select the element (without the self:: part), so why do we need the self:: part when excluding it?
Examples:
test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<CHILD_A>
Test A
</CHILD_A>
<CHILD_B>
This element should be ignored.
</CHILD_B>
<CHILD_C>
Test C
</CHILD_C>
</ROOT>
test.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="ROOT">
<xsl:apply-templates select="node()[not(self::CHILD_B)]" />
</xsl:template>
</xsl:stylesheet>
Inside the predicate, the context node is changed to whatever node was selected in the expression on the left side of the predicate so node()[not(CHILD_B)] looks for any child node of the context node but then, inside the predicate to be evaluated, this child node is the context node so that not(CHILD_B) tests whether the outer child node has no CHILD_B child element on its own.
While node()[not(self::CHILD_B)] checks whether any selected (outer) child node is not a CHILD_B element.
One of the default templates in xslt is the following:
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
Why does the match pattern contains the expression for the root node "/"? Doesn't the asterisk "*" capture already all nodes in the document?
I tried to leave it out and there was no difference. Following xslt
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xhtml" indent="yes"/>
<xsl:template match="*">
<xsl:value-of select="./name()"/>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
and following xml
<?xml version="1.0" encoding="UTF-8"?>
<a>
<b>
</b>
</a>
produces the output:
<?xml version="1.0" encoding="UTF-8"?>a
b
So the root node a gets captured.
As the default axis for a location path is child, *|/ is just an abbreviation for child::*|/. And child::* does not match the root node of a document, just its children.
I'm looking for the best way to get all unique (no duplicates) nested nodes of all sibling nodes. The node I'm am interested in is "Gases". The sibling nodes are "Content". My simplified XML:
<Collection>
<Content>
<Html>
<root>
<Gases>NO2</Gases>
<Gases>CH4</Gases>
<Gases>O2</Gases>
</root>
</Html>
</Content>
<Content>
<Html>
<root>
<Gases>NO2</Gases>
<Gases>CH4</Gases>
<Gases>CO</Gases>
<Gases>LEL</Gases>
<Gases>NH3</Gases>
</root>
</Html>
</Content>
</Collection>
Desired result: NO2 CH4 O2 CO LEL NH3
I'm new to XSLT so any help would be much appreciated. I've been trying to use XPATH, similar to here, but with no luck.
This XSLT stylesheet will produce the desired output. Note that it relies on there being no duplicate Gases element inside a single Content element.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<!-- Match Gases elements whose value does not appear in a Gases element inside a previous
Content element. -->
<xsl:template match="//Gases[not(. = ancestor::Content/preceding-sibling::Content//Gases)]">
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
</xsl:template>
<!-- Need to override the built-in template for text nodes, otherwise they will still get
printed out. -->
<xsl:template match="text()"/>
</xsl:stylesheet>
My source XML looks:
<test>
<text1>Test</text1>
<text2>Test</text2>
<text2>Test</text2>
<section>
<text1>Test<bold>content</bold></text1>
<text1>Test</text1>
<text2>Test</text2>
<text2>Test</text2>
</section>
</test>
Want to extract the value of 6th node, based on the absolute number of the element (overall count). The absolute number of the element has been identified using <xsl:number level="any" from="/" count="*"/>.
The XPath expression /descendant::*[6] should give you the element you need.
<xsl:template match="/">
<xsl:copy-of select="/descendant::*[6]" />
</xsl:template>
outputs
<text1>Test<bold>content</bold></text1>
Note that this is an example of the difference between descendant:: and // - //*[6] would give you all elements that are the sixth child element of their respective parent, rather than simply the sixth element in the document in depth-first order.
This xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="allElements" select="//element()" />
<xsl:template match="/">
<output>
<xsl:value-of select="$allElements[6]" />
</output>
</xsl:template>
</xsl:stylesheet>
will result in
<?xml version="1.0" encoding="UTF-8"?>
<output>Testcontent</output>
I have an xsl stylesheet giving just about what is needed, except there are values outputted outside the tags, . Is there a way to remove them? The scenario is that the desired output is a total invoice amount for invoices that appear more than once. Each time the xslt is executed the parameter p1 contains the InvoiceNumber to total. The code below shows that parameter, p1, hardcoded to '351510'.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Invoices/Invoice[InvoiceNumber=351510][1]/InvoiceNumber">
<xsl:copy>
<xsl:apply-templates select="/Invoices/Invoice[InvoiceNumber=351510][1]/InvoiceAmount"/>
</xsl:copy>
</xsl:template>
<xsl:param name="tempvar"/>
<xsl:template name="InvTotal" match="/Invoices/Invoice[InvoiceNumber=351510][1]/InvoiceNumber">
<xsl:variable name="p1" select="351510" />
<xsl:if test="/Invoices/Invoice/InvoiceNumber[. = $p1]">
<!--<xsl:if test="$test = $p1" >-->
<InvoiceAmount>
<xsl:value-of select="sum(../../Invoice[InvoiceNumber=351510]/InvoiceAmount)"/>
</InvoiceAmount>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Here is the input:
<Invoices>
- <Invoice>
<InvoiceNumber>351510</InvoiceNumber>
<InvoiceAmount>137.00</InvoiceAmount>
</Invoice>
- <Invoice>
<InvoiceNumber>351510</InvoiceNumber>
<InvoiceAmount>363.00</InvoiceAmount>
</Invoice>
- <Invoice>
<InvoiceNumber>351511</InvoiceNumber>
<InvoiceAmount>239.50</InvoiceAmount>
</Invoice>
</Invoices>
Here is the output:
<InvoiceAmount>500</InvoiceAmount>137.00351510363.00351511239.50
Here is desired output:
<InvoiceAmount>500</InvoiceAmount>
Also, thank you goes to lwburk who got me this far.
Adding
<xsl:template match="text()"/>
should help.
I do not get the same results as you posted (only 351510137.00351510363.00351511239.50, all the text nodes), and I do not know the purpose of tempvar (unused).
Since it appears that all you need is the sum of InvoiceAmount values for a specific InvoiceNumber, just keep it simple and ignore everything else:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="invoiceNumber"/>
<xsl:template match="/">
<InvoiceAmount>
<xsl:value-of select="sum(/Invoices/Invoice[InvoiceNumber=$invoiceNumber]/InvoiceAmount)"/>
</InvoiceAmount>
</xsl:template>
</xsl:stylesheet>
You can pass the InvoiceNumber to process via the parameter invoiceNumber, or you can hardcode it if you like (see version 1).
Note: should you prefer a number format like e.g. #.00 (fixed decimals) for the sum, then you can also use the format-number(…) function.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pNum" select="351510"/>
<xsl:key name="kInvAmmtByNumber" match="InvoiceAmount"
use="../InvoiceNumber"/>
<xsl:variable name="vInvoiceAmounts" select=
"key('kInvAmmtByNumber', $pNum)"/>
<xsl:variable name="vIdInvAmount1" select=
"generate-id($vInvoiceAmounts[1])"/>
<xsl:template match="InvoiceAmount">
<xsl:if test="generate-id() = $vIdInvAmount1">
<InvoiceAmount>
<xsl:value-of select="sum($vInvoiceAmounts)"/>
</InvoiceAmount>
</xsl:if>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the provided XML file:
<Invoices>
<Invoice>
<InvoiceNumber>351510</InvoiceNumber>
<InvoiceAmount>137.50</InvoiceAmount>
</Invoice>
<Invoice>
<InvoiceNumber>351510</InvoiceNumber>
<InvoiceAmount>362.50</InvoiceAmount>
</Invoice>
<Invoice>
<InvoiceNumber>351511</InvoiceNumber>
<InvoiceAmount>239.50</InvoiceAmount>
</Invoice>
</Invoices>
produces exactly the wanted, correct result:
<InvoiceAmount>500</InvoiceAmount>
Explanation:
The wanted invoice number is passed to the transformation as the value of the external/global parameter $pNum .
We use a key that indexes all InvoiceAmount elements by their corresponding InvoiceNumber values.
Using that key we define the variable $vInvoiceAmounts that contains the node-set of all InvoiceAmount elements the value of whose corresponding InvoiceNumber element is the same as the value of the external parameter $pNum.
We also define a variable ($vIdInvAmount1) that contains a unique Id of the first such InvoiceAmount element.
There is a template that matches any InvoiceAmount element. It checks if the matched element is the first of the elements contained in the node-set $vInvoiceAmounts. If so, a InvoiceAmount element is generated with a single text-node child, whose value is the sum of all InvoiceAmount elements contained in $vInvoiceAmounts. Otherwise nothing is done.
Finally, there is a second template that matches any text node and does nothing (deletes it in the output), effectively overriding the unwanted side effect of the default XSLT processing -- the outputting of unwanted text.