I want to write an XSLT template that matches all elements of one namespace except one element. For example I want to match all elements foo:*, but not foo:bar.
Is that possible to define this in a selector or do I have to write an xsl:if condition within the xsl:template (and how can I test the local name of the element)?
To do this, you can just have a template that matches foo:bar that does nothing with it like so:
<xsl:template match="foo:bar" />
To match other foo elements, you can use a more general template
The XSLT processor should match the more specific template first, and so foo:bar will be ignored, and all other foo elements matched by the other template.
So, for example, given this input XML
<foo:root xmlns:foo="foo.com">
<foo:bar>No match</foo:bar>
<foo:pie>Match</foo:pie>
</foo:root>
When you apply the following XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:foo="foo.com">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="foo:bar" />
<xsl:template match="foo:*">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you wanted to do different processing on foo:bar, just add code to the relevant template.
The following is output, without any sign of foo:bar
<foo:root xmlns:foo="foo.com">
<foo:pie>Match</foo:pie>
</foo:root>
XSLT 1.0:
<xsl:template match="foo:*[not(local-name()='bar')]">
<!--do stuff-->
</xsl:template>
XSLT 2.0:
<xsl:template match="foo:*[. except self::foo:bar]">
<!--do stuff-->
</xsl:template>
Related
I have an requirement in which I am processing the message based on the root element tag and for that I have created 3 different template match based on the root tag element. I was wondering how to process the message if the client is sending different message which is not matching the root tag element.
Input:
<?xml version="1.0"?>
<process1 xmlns="http://www.openapplications.org/oagis/10" systemEnvironmentCode="Production" languageCode="en-US">
<Appdata>
<Sender>
</Sender>
<Receiver>
</Receiver>
<CreationDateTime/>
</Appdata>
</process1>
2nd message: Everything will be same except root tag will be process2, process3
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/*[local-name()='proces1']">
<operation>dosomthing</operation>
</xsl:template>
<xsl:template match="/*[local-name()='process2']">
<operation>dosomthing2</operation>
</xsl:template>
<xsl:template match="/*[local-name()='process2']">
<operation>blah blah</operation>
</xsl:template>
</xsl:stylesheet>
My question here is I want to process message in case if it's not matching the 3 templates process1,process2,process3.
Can anyone please give advise how to achieve that?
First off, don't use local-name(). It's easy to declare and use the proper namespace, do it.
Secondly, just make a template that is less specific to catch any document element with a name that you did not anticipate (see the 4th template below):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:oagis="http://www.openapplications.org/oagis/10"
>
<xsl:template match="/oagis:process1">
<operation>dosomething1</operation>
</xsl:template>
<xsl:template match="/oagis:process2">
<operation>dosomething2</operation>
</xsl:template>
<xsl:template match="/oagis:process3">
<operation>dosomething3</operation>
</xsl:template>
<xsl:template match="/*" priority="0">
<!-- any document element not mentioned above -->
</xsl:template>
</xsl:stylesheet>
Note: If the first three templates all do the same, you can collapse them into one.
<xsl:template match="/oagis:process1|/oagis:process2|/oagis:process3">
<operation>dosomething</operation>
</xsl:template>
I am using XSLT 1.0, and using xsltproc on OS X Yosemite.
The source content is HTML; the target content is XML.
The issue is a fairly common one. I want all "uninteresting"
nodes simply to be discarded from the output. I've seen catch-all
directives like this:
<xsl:template match="node()|script"/>
<xsl:template match="*">
<xsl:apply-templates/>
</xsl:template>
This is close to what I need. But unfortunately, it's too strong when I need to add another template that visits one of the text nodes caught by node(). For example, suppose I added this template:
<xsl:template match="a/div[#class='location']/br">
<xsl:text> </xsl:text>
</xsl:template>
which simply replaces certain <br/> elements with spaces.
Well, node() precludes this latter template from taking effect,
because the relevant text node containing the line-break is discarded
already!
Well, to correct the issue, here's what I have done in lieu of the catch-all node():
<xsl:template match="html/head|div[#id='banner_parent']|button|ul|div[#id='feed_title']|span|div[#class='submit_event']|script"/>
But this is precisely the problem: I am now piecing together a template
whose matching criteria is likely to be error-prone when the source
content changes.
Is there a simpler directive that would accomplish the same thing? I'm aiming for something like this:
<xsl:template match="node()[not(locations)]|script"/>
Thanks.
If i understood correctly, you want only some nodes in the output and the rest you dont care abour, in this example I try to catch only li elements and throw the rest away.. not sure if this is what you want though http://xsltransform.net/gWmuiKk
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<!-- Lets pretend li is interesting for you -->
<xsl:template match="li">
<xsl:text>Interesting Node Only!
</xsl:text>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
</xsl:transform>
I'm trying to match the first bar element that occurs as a descendant of a foo element in an xsl match pattern and struggling. Initial attempt:
<xsl:template match="//foo//bar[1]">
...
</xsl:template>
fails because there are several bar elements that match. So:
<xsl:template match="(//foo//bar)[1]">
...
</xsl:template>
but that fails to compile.
Tricky. I don't know how efficient or otherwise this would be, but you could turn the pattern on its head and move the logic into a predicate (which is allowed to use axes other than child, attribute and //):
<xsl:template match="foo//bar[not(preceding::bar/ancestor::foo)]">
(any bar inside a foo provided there isn't another bar-inside-a-foo before it). Alternatively you could try a key trick similar to the way Muenchian grouping works, which may be more efficient
<!-- trick key - all matching nodes will end up with the same key value - all
we care about is finding whether a particular node is the first such node
in the document or not. -->
<xsl:key name="fooBar" match="foo//bar" use="1" />
<xsl:template match="foo//bar[generate-id() = generate-id(key('fooBar', 1)[1])]">
You cannot do this with match expressions. In fact, you can do this with match expressions, just not in every XSLT processor, as it seems. See comments.
I'd use an <xsl:if>.
<xsl:template match="foo//bar">
<xsl:if test="generate-id() = generate-id(ancestor::foo[1]//bar)">
<!-- ... -->
</xsl:if>
</xsl:template>
This ensures that only the first descendant <bar> per <foo> (!) is processed any further.
NB: When given a node-set, generate-id() returns the ID of the first node in the set.
Alternative solution is based on the "rule of thumb" use advanced XPATH with "select" not when you "match". Match XML with templates simply by name like foo, not even //foo.
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar>bar1<bar>bar2</bar></bar>
<bar>bar3</bar>
</foo>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="foo">
<xsl:apply-templates select="descendant::bar[1]"/>
</xsl:template>
<xsl:template match="bar">
<!--only the first bar was selected --><xsl:value-of select="text()"/>
</xsl:template>
</xsl:stylesheet>
My requirement is to convert the following array
<array>
<value>755</value>
<value>5861</value>
<value>4328</value>
</array>
to this array.
<array>
<int>755</int>
<int>5861</int>
<int>4328</int>
</array>
Following is my XSLT code to do the transformation & it works. Is it the correct way because in one post I saw the use of "identity template". but I haven't used it.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/array">
<xsl:element name="array">
<xsl:for-each select="value">
<xsl:element name="int">
<xsl:value-of select="." />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Your current method works. It is more of a "pull" style stylesheet. The "push" style uses apply-templates.
You could shorten it a bit by using element literals, which makes it a little easier to read:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/array">
<array>
<xsl:for-each select="value">
<int>
<xsl:value-of select="." />
</int>
</xsl:for-each>
</array>
</xsl:template>
</xsl:stylesheet>
A solution using the identity template and a custom template for the value element:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<!--identity template, which copies every attribute and
node(element, text, comment, and processing instruction)
that it matches and then applies templates to all of it's
attributes and child nodes (if there are any) -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--Specialized template that matches the value element.
Because it is more specific than the identity template above it has
a higher priority and will match when the value element is encountered.
It creates an int element and then applies templates to any attributes
and child nodes of the value element -->
<xsl:template match="value">
<int>
<xsl:apply-templates select="#*|node()"/>
</int>
</xsl:template>
</xsl:stylesheet>
You can do:
<xsl:template match="/array">
<array>
<xsl:for-each select="value">
<int>
<xsl:value-of select="." />
</int>
</xsl:for-each>
</array>
</xsl:template>
Here is a quote from the accepted answer of your link:
XSL cannot replace anything. The best you can do is to copy the parts you want to keep, then output the parts you want to change instead of the parts you don't want to keep.
That is where the identity template comes into play: it copies everything not targetted by another matching template. The upshot is, that if your base XML contains other content than just the array, then you should also include the identity template in your xslt. But if you are sure that your xml will contain no other content, then you don't need it.
I currently have a xml file like this:
<aaa>
<b>I am a <i>boy</i></b>.
</aaa>
How can I get the exact string as: <b>I am a <i>boy</i></b>.? Thanks.
You have to tell XSLT that you want to copy elements through as well. That can be done with an additional rule. Note that I use custom select clauses on my apply-templates elements to select attributes as well as all node-type objects. Also note that the rule for aaa takes precedence, and does not copy the aaa element itself to the output.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="aaa">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
<aaa>
<b>I am a <i>boy</i></b>.
</aaa>
How can I get the exact string as:
<b>I am a <i>boy</i></b>.?
The easiest/shortest way to do this in your case is to output the result of the following XPath expression:
/*/node()
This means: "Select all nodes that are children of the top element."
Of course, there are some white-space-only text nodes that we don't want selected, but XSLT can take care of this, so the XPath expression is just as simple as shown above.
Now, to get the result with an XSLT transformation, we use the following:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy-of select="/*/node()"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document, the wanted result is produced:
<b>I am a <i>boy</i></b>.
Do note:
The use of the <xsl:copy-of> xslt instruction (not <xsl:value-of>), which copies nodes, not string values.
The use of the <xsl:strip-space elements="*"/> XSLT instruction, directing the XSLT processor to ignore any white-space-only text node in the XML document.