Select the value of the attribute - xslt

<Surcharge>
<Rentalplus desc="Rental plus">75.00</Rentalplus>
<Gasket desc="Seals and gasket">50.00</Gasket>
<WearandTear desc"Wear and Tear">100.00</WearandTear>
</Surcharge>
from the above xml i want to extract the "desc". keep in mind i have different tag names under the node.
Thanks for the help

How about a minimalist solution ?
//#desc
Or more precise
/Surcharge//#desc
Or even more precise
/Surcharge/*[self::Rentalplus|self::Gasket|self::WearandTear]/#desc

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:apply-templates select="*/#desc"/>
</xsl:template>
</xsl:stylesheet>
Exploits built-in rules. Result will be:
Rental plusSeals and gasketWear and Tear

Use:
/*/*/#desc
This selects all desc attributes of all children of the top element of the XML document.
Never use the // abbreviation when the structure of the document is well-known. Using the // abbreviation may result in significantly slow evaluation, because it causes traversal of the whole XML document.

Should be something like this:
//#desc
See syntax from the w3schools site http://www.w3schools.com/xsl/xpath_syntax.asp

Related

XPath to select element based on child value

Trying to select an element based on the value of one of it's childrens
Im trying to do the same but it doesnt work.
XML:
<customize>
<name>InstallationZeitfenster</name>
<dataType rvcd="2">Alphanumerisch</dataType>
<value>Nachmittag</value>
</customize>
<customize>
<name>InstallationGeplant</name>
<dataType rvcd="3">Datum</dataType>
<value>06.11.2019</value>
</customize>
</customize>
I want the text of the "value" node, with the node name = "InstallationGeplant" in it. Thats what i tried to do.
<xsl:value-of select="/*[local-name()='deviceManagement']/*[local-name()='deviceLocation']/*[local-name()='deviceInstallation']/*[local-name()='deviceInfo']/*[local-name()='device']/*[local-name()='customize']/*[local-name()='customize']/*[local-name()='name']/text() = 'InstallationGeplant'/*[local-name()='value']"/>
Has anyone some idea what i should do?
I have to access like this in to the nodes because of the namespaces.
Thanks for the help
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:value-of select="descendant::value[preceding-sibling::name[1]='InstallationGeplant']"/>
</xsl:template>
</xsl:stylesheet>
You could do simply:
<xsl:value-of select="customize[name='InstallationGeplant']/value"/>
This is from the context of the parent customize; your attempt suggests that the structure is deeper than what you show.
have to access like this in to the nodes because of the namespaces
No, you do not have to and should not. See: XSLT Transform doesn't work until I remove root node

XSLT: for-each loop with key not returning all nodes

I am a novice XSLT developer. I have been asked to fix an issue on a project where the original developer is no longer with us. In the XSLT, there is a for-each loop using a key and a count
<xsl:for-each select="ns0:BOM[count(. | key('subsat', ns0:BomText01)[1]) = 1][ns0:BomText01]">
...
This is the key:
<xsl:key name="subsat" match="ns0:Parts/ns0:BOM[ns0:FindNum!='0']" use="ns0:BomText01" />
In the XML file being transformed, there are two sibling nodes that represent sub-parts:
<ns0:BOM referentId="10000:65091335:65359080">
<ns0:BomText01>3069260-303-SUB0027</ns0:BomText01>
<ns0:ItemNumber>My_part_1</ns0:ItemNumber>
<ns0:ItemType>Part</ns0:ItemType>
<ns0:Qty>67</ns0:Qty>
</ns0:BOM>
<ns0:BOM referentId="10000:65102551:86713230">
<ns0:BomText01>3069260-303-SUB0027</ns0:BomText01>
<ns0:ItemNumber>My_part_2</ns0:ItemNumber>
<ns0:ItemType>Part</ns0:ItemType>
<ns0:Qty>67</ns0:Qty>
</ns0:BOM>
However, the loop is only picking up the first node (My_part_1). I suspect it's because of the count=1 but I really don't know. And I don't know how to modify it. Ideas? If I need to include more data, let me know.
Assuming that the relevant part of your XSLT looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="ns0" version="1.0">
<xsl:key name="subsat" match="ns0:BOM[ns0:FindNum!='0']" use="ns0:BomText01"/>
<xsl:template match="ns0:Parts">
<xsl:for-each
select="ns0:BOM[count(. | key('subsat', ns0:BomText01)[1]) = 1][ns0:BomText01]">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
It will only print the first of the elements because it is selecting the BOM elements which have an unique BomText01 value. That's the expected result.
If the BomText01 is an ID field (as it seems it is) and you expected to get both result (perhaps, because their ItemNumber contains different values), the error is possibly in your source (which assigned equal IDs when it should not have done so).
If you change one of those values in the source, you should be able to select both and verify this.

Counting elements that are generated in XSLT1

I'm trying to count the elements my transformation generates (must use XLST1). For example, my transformation creates:
<Parent>
<ElementX Att1="2"/>
<ElementY Att1="1"/>
<ElementZ Att1="6"/>
</Parent>
I need to print 3 within the same transformation, because there are 3 child elements.
Can this be done?
Thanks.
It would help a lot if you provide some extract of your XSLT.
I cn't give you a XSLT code without it. I'll try to give some "way" to the answer :
One solution could be to store the output into a nodeset (use the XSLT 1.0 extension which provides the nodeset() function) and apply the XPath count() function on this variable. After that just output your variable with xsl:value-of, and your count result the same way.
Here is a demo how to do this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="count(ext:node-set($vrtfPass1)/*/*)"/>
</xsl:template>
<xsl:template match="/*">
<Parent>
<ElementX Att1="2"/>
<ElementY Att1="1"/>
<ElementZ Att1="6"/>
</Parent>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used in this Demo), the wanted, correct result is produced:
3
Explanation:
A general way to process the result of the transformation (in a single transformation), is to organize it in two passes where we save the result of the first pass in a variable.
In the second pass we access the result and do the additional processing.
Do note that in XSLT 1.0 if the variable that captures the result of the first pass is of the infamous RTF (Result Tree Fragment) type and needs to be converted to a regular tree in order of any nodes inside this tree to be accessible (xsl:copy-of and string() are still allowed on an RTF).
This conversion to a regular tree is done by an extension function, which most often has the name node-set and always belongs to a vendor-defined namespace. In this demo we are using the node-set() extension function that belongs to the EXSLT namespace -- because most XSLT 1.0 processors implement EXSLT.
For more information on multi-pass processing, see this: Two phase processing: Do not output empty tags from phase-1 XSLT 2.0 processing

selecting one of several elements that differ in attribute

I'm an experienced programmer, but a novice to XSLT and am finding it quite baffling. I apologize if this is a question that's been asked before, but I'm so frustrated by XSLT that I'm not even sure what to search for...
I have a problem that if a certain XML element appears only once, I want its contents output, but if it occurs more than once, I want only the contents of those that have a certain attribute.
For example, suppose I have one XML file (call it "file 1") that contains
<food>
<snack>Chips</snack>
<snack type="nuts">Peanuts</snack>
</food>
and another XML file ("file 2") that contains
<food>
<snack>Cheese puffs</snack>
</food>
I need an XSLT that outputs only "Peanuts" (but not "Chips") upon processing file 1, but still outputs "Cheese puffs" for file 2 (i.e. I can't just select only those elements that have a "type" attribute, that would be too easy).
This is probably simple, but I'm stuck...
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:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[snack/#type]/snack[not(#type)]"/>
</xsl:stylesheet>
produces the wanted results in both cases.
Explanation:
The identity rule/template copies every node "as-is".
The second template is overriding the identity template for any snack element without a type attribute that has sibling snack elements that have a type attribute. This template has empty body, which effectively "deletes" the matched element from (being copied to) the output.
if a certain XML element appears only once, I want its contents output, but if it occurs more than once, I want only the contents of those that have a certain attribute.
A direct translation of that would be
if (count(snack) = 1) then snack else snack[#type='nuts']
which is valid XPath 2.0 syntax - if you need to do it in 1.0 then it translates fairly directly (though verbosely) into an equivalent xsl:choose.
If you want something even more concise than the above, you can also write in XPath 2.0
(snack[#type='nuts'], snack)[1]
which builds a list containing first the snacks with type='nuts', then all the snacks, and then selects the first item from this list.

XSLT: deep child copy

My need: I want to deep copy all the childs of a single selected node without actually copying it. Example: from
<father><son i="1" /><son i="2" /><son i="0"><lastNode /></son></father>
i wish to extract
<son i="1" /><son i="2" /><son i="0"><lastNode /></son>
I know that i can do this with a cycle for-each and then a xsl:copy-of. I am wondering if there is a simpler expression to achieve the same result. Some idea?
Follow-up. My question missed a couple of points. I should had said that all the childs means "all the possible childs", including textnodes; another verification that a better question already contains the answer. Second, what I have learned from you - the community - is that I was enough dumb to try to solve by XSL what in facts was more a XPATH issue. Thanks to all of you for this insight
Cheers.
Try select all children..
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:copy-of select="father/*"/>
</xsl:template>
</xsl:stylesheet>
E.G.
Given input
<father><son i="1" /><son i="2" /><niceSon /><son i="0"><lastNode /></son></father>
It outputs
<son i="1" /><son i="2" /><niceSon /><son i="0"><lastNode /></son>
<xsl:copy-of select="father/node()" />
Use e.g. <xsl:copy-of select="father/son"/>