Dynamic Namespace for Input XML - xslt

I have searched many posts on dynamically setting namespaces but they all seem to refer to setting the namespace of the output XML.
The issue I have is that the namespace (defined at root and same for all child nodes) of the input XML differs, and the same stylesheet needs to be able to handle both inputs.
For example, one input XML is:
<root xmlns="aaa">
<body>xxx</body>
</root>
And the other input XML is:
<root xmlns="bbb">
<body>yyy</body>
</root>
In the stylesheet, my XPath obviously needs to use the defined namespace, which is declared with a prefix, i.e.:
xmlns:one="aaa"
But as soon as the second input XML is being transformed, it of course fails to work.
I could define another namespace, e.g.
xmlns:two="bbb"
But the only way to use that namespace at the right time is to duplicate all the XSLT code and have the other namespace as the prefix for all the XPath (even then I would still need to identify which set of XPath to use which may be fun..)
My stylesheet currently uses the following XPath:
%lt;SOMETHING>
<xsl:value-of select="one:body" />
%lt;/SOMETHING>
As you can see it uses the "one" namespace prefix. Is there any way to just get the value of either "body" tag, regardless of namespace? As mentioned in a comment below, although I appreciate they are different elements based on namespace, I know that the information in each will be the same so I can treat them as such.
I have seen posts on using xsl:element with a namespace attribute but from what I can tell that just defines the namespace of the output XML, not the input. (To make matters worse, what I am outputting is actually escaped XML, e.g. %lt;SOMETHING> so I couldn't use xsl:element anyway).
My current solution (since posting this) is to have two extra stylesheets included in the main stylesheet. Each one is specific to either namespace "one" or namespace "two", each line of XPath uses the relevant namespace prefix.
I am hoping there is a way to avoid having two separate stylesheets that are almost identical except for the namespace prefix.
Thanks in advance.

If I get you right, you want to process the XML ignoring the elements' namespaces. Actually, the sense of namespaces is to distinct between elements from different contexts. So from an XML point of view, <one:body> has absolutely nothing to do with <two:body>, besides the fact that they happen to have the same name.
If you want to do it anyway, instead of:
<xsl:template match="one:body">
<xsl:template match="two:body">
you should match on the elements' local name only:
<xsl:template match="*[local-name()='body']">
In order to give a little more background: If you say
<xsl:template match="one:body">
then this is only a short notation of
<xsl:template match="*[namespace-uri()='aaa'][local-name()='body']">
(i.e. "match any element whose namespace is 'aaa' and whose name is 'body'")
Thus, ignoring the namespace by leaving away the
[namespace-uri()='aaa']
makes it
<xsl:template match="*[local-name()='body']">
Instead, you had better say
<xsl:template match="*[namespace-uri()='aaa' or namespace-uri()='bbb'][local-name()='body']">
or
<xsl:template match="*[namespace-uri()='aaa' or namespace-uri()='bbb' or namespace-uri()='ccc'][local-name()='body']">
and so on. If, as dret states, you know all possible namespaces in advance.

I would suggest you define both namespaces, then use paths such as:
one:body | two:body
to address the elements in the source XML.
For example, instead of:
<xsl:value-of select="one:body" />
use:
<xsl:value-of select="one:body | two:body" />

As I already wrote! Instead of
<xsl:value-of select="one:body" />
you can write
<xsl:value-of select="*[local-name()='body']" />
Or, if you have XPath 2.0, then
<xsl:value-of select="*:body" />

Related

Can we encode URL in .xsl file?

Can we encode URL in .xsl file? If yes, then how we can do that?
I have used below code:
Prompt type="label" pointer="abc.asp?mynumber={my-number}&myname={my-name}"
It works fine for other name, but if name contains &, then it is breaking.
Could someone help me to solve it?
In my experience browsers may treat & and & the same you're likely to end up with a link that doesn't work correctly anyway.
If you're not certain of what the my-name node might contain then it might be better to create a variable from it first. Then you can make sure that any ampersands are removed first.
Actually, the first thing you should try is to agree that the my-name node never contains an ampersand in the first place. But I know that sort of request is often ignored so there is a solution.
If you use a variable, you can include a find and replace to remove/replace the &. There isn't a function for this in xslt 1 but you can use the translate function instead
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/Test">
<xsl:variable name="safe-name" select="translate(my-name, '&', '|')"/>
MyName - <xsl:value-of select="$safe-name"/>
</xsl:template>
</xsl:stylesheet>
I've given this a quick test and it works with several & in the node (you can never be too careful). Hope that helps

XSLT global variable to reuse text in an attribute specifically

What is the most basic syntax in XSLT to declare a global variable for a string of text and then reference that text value into the attributes you are going to output later on the XSLT? Sounds simple - but has a specific syntax.
After two similar questions were missing the tiny detail that makes this work, I thought it might be useful to share this. Answer:
Variable declaration (near beginning of the XSL):
<xsl:variable name="defaultIconStyle" select="'Icon - Style'"/>
Note the single quotes inside the double quotes for the text string.
This method was also proposed but may be more resource/expensive:
<xsl:variable name="defaultIconStyle">Icon - Style</xsl:variable>
Calling this into an attribute value later:
(in this case, to set a character style for a tag targeted for inDesign)
<xsl:template match="note-mytype">
<xsl:copy><ph aid:cstyle="{$defaultIconStyle}"><image href="file:///myIcon.ai"/><xsl:text> </xsl:text></ph><xsl:apply-templates/></xsl:copy>
</xsl:template>

How to (nicely) template match multiple specific child elements (union) within wider XPATH

I'm trying to match a set of particular elements, but only ones which are children of another element structure (let's say it's input or select elements only somewhere inside divs with the class "special-sauce" on them). Normally, this would be easy so far as XPATH: we could parenthetically union the targeted children, like so:
div[contains(#class, 'special-sauce')//(input | select)
But this is where XSLT throws a curve ball, when we try to use this as a template match (at least in Saxon):
<xsl:template match="div[contains(#class, 'special-sauce')//(input | select)">
{"error":"The xsl file (/section-settings.xsl) could not be parsed.
Failed to compile stylesheet. 1 error
detected.","code":"TRANSFORM_ERROR","location":null,"causes":["Fatal
Error: Token \"(\" not allowed here in an XSLT pattern"]}
Basically, parentheticals aren't allowed as part of a template match at the main pathing level (they still work fine inside of conditionals/etc, obviously).
So what to do?
Well, technically, using a union can still work, but we would have to repeat the ancestor XPATH each time, since we can't parenthetically enclose the children:
<xsl:template match="div[contains(#class, 'special-sauce')//input
| div[contains(#class, 'special-sauce')//select">
This is doable (not very pretty, but sure, we can handle that! line breaks can work here to help our sanity yay) in our simple example here, but it gets problematic with more complex XPATH, especially if the parenthetical union would have been in the middle of a longer xpath, or for a lot of elements.
e.g.
div[contains(#class, 'major-mess')]/div[contains(#class, 'special-sauce')]//(dataset | optgroup | fieldset)//(button | option | label)
becomes
a crazy mess.
Ok, that quickly becomes less of an option in more complex examples. And while structuring our XSLT differently might help (intermediary matches, using modality, etc), the question remains:
How can we gracefully template match using unions of individual child elements within a larger XPATH pattern when parentheticals won't work?
An example sheet for the first example:
<div class="special-sauce">
<input class="form-control" type="text" value="" placeholder="INHERITED:" />
<select class="form-control">
<option value="INHERITED: ">INHERIT: </option>
<option value=""></option>
</select>
<div class="radio">
<label>
<input type="radio" name="param3vals" value="INHERITED: " />
INHERIT:
</label>
</div>
</div>
<div class="not-special"><input type="text" id="contact-info-include-path" size="90">
<label>contact</label>
</input></div>
<div class="sad-panda"><input type="text" id="sidenav-include-path" size="90">
<label>sidenav</label>
</input></div>
Note: this does assume that an identity transform is running as the primary method of handling the input document.
While there are other questions which could validly receive similar answers as, for example, the one I give below, I felt the context of those questions was usually more general (such that a top level union would be fine as their answer without complication), more specific in ways that didn't match, or simply too different. Hence the Q&A format.
XSLT 1.0 vs 2.0 vs 3.0
Michael Kay correctly notes in his answer below that while the original pattern attempted here doesn't work in XSLT 1.0 or 2.0, it should work in a (fully) XSLT 3.0 compatible processor. I'm currently on a system using Saxon 9.3, which is technically XSLT 2.0. I just want to call extra attention to that answer for those who are on a 3.0 system.
I looked all over and most answers to similar problems involved copying the repeated portion of the XPATH to each element and unioning it all together. But there is a better way! It's easy to forget that matching a particular element is relatively equivalent to matching that element's name within XPATH.
Use name() or local-name() instead of matching on the element directly within the template match pattern*.
Be aware of your namespace issues/needs when picking which to use. This still allows for advanced conditionals on attributes/etc of those elements.
The first match, for example, becomes:
<xsl:template match="div[contains(#class, 'special-sauce')//
element()[local-name() = ('input', 'select')]">
There's not a huge gain here in terms of space or time to write this out, but we do reduce redundancy and the associated data consistency errors that can result (all too often, especially if later making changes).
Where this really shines is the last example in the question (the mess):
<xsl:template match="div[contains(#class, 'major-mess')]/
div[contains(#class, 'special-sauce')]//
element()[local-name() = ('dataset', 'optgroup', 'fieldset')]//
element()[local-name() = ('button', 'option', 'label')]">
And since I can't remember if that's fully XSLT/XPATH 1.0 compatible by creating the element tree-fragment parenthetically for comparison, if you do need backwards compatibility the "contains() with bracketing separator tokens" (reducing chances of a false positive from another element being a substring of the full name targeted) pattern always works too:
<xsl:template match="div[contains(#class, 'major-mess')]/
div[contains(#class, 'special-sauce')]//
element()[contains('|dataset|optgroup|fieldset|'), concat('|', local-name(), '|'))]//
element()[contains('|button|option|label|', concat('|', local-name(), '|'))]">
* = "match pattern" vs "XPath"
If you're struggling with understanding why the naive approach (the first thing I attempted in the question) fails in XSLT, it helps to understand that template rules like "match" must follow XSLT patterns, which are only essentially a sub set of valid XPath expressions (which easily makes things more confusing to distinguish and remember, especially when many sources just pretend it's all XPath entirely). Note that parentheses only show up as a valid option to use as expression tokens which are only found within expressions within predicates, not within any other portion of the location path or location steps.
Final Considerations
Performance: I have no idea whether there are notable performance differences with this approach versus unioning each seperate element as a full path to each one, or whether there is even a real performance difference between addressing an element natively versus as a predicate on the anonymous element() selector. My suspicion is that while most XSLT processors can probably achieve a faster DOM tree search when a single match is written using the native path structure versus a predicate with name() function on the anonymous selector, the union cases may perform faster depending on how well the processor tries to pre-compile and optimize for logic patterns. I will leave that task for someone else to try benchmarking, because ultimately the real hurdle becomes developer sanity and maintenance issues (likelihood of incurring human errors). In complex matches, I feel that any small performance penalty will likely be easily met by the simple legibility and reduced/eliminated data redundancy of this approach.
I think that your pattern is legal in XSLT 3.0 as written. But I guess you want an XSLT 2.0 solution...
One great way that people often overlook is to use schema-aware patterns. If you want to match a choice of elements, it's quite likely that they are closely related in the schema, for example by having a common type T or by being members of a substitution group S. You can then write
div[contains(#class, 'special-sauce')//schema-element(S)
or
div[contains(#class, 'special-sauce')//element(*, T)
But I guess you want a solution that isn't schema-aware...
In that case, I don't think I can offer anything better than what you've got.
Sometimes multiple modes are the answer: for example something like
<xsl:template match="div[contains(#class, 'special-sauce')]">
<xsl:apply-templates mode="special"/>
</xsl:template>
<xsl:template match="select|input" mode="special">
Generally I think modes are greatly under-used.
Why not split this template into two or three (one for each level) with modes? Something like
<xsl:template match="div[contains(#class, 'special-sauce')">
<xsl:apply-templates select=".//select|input" mode="special-sauce"/>
</xsl:template>
<xsl:template match="select|input" mode="special-sauce">
<!-- ... -->
</xsl:template>
In my opinion this way it reads clearer.

XSLT: Test if node exists regardless if it's a child or grandchild of current node

I'm working on some xslt transformations and I've just found out that there might or might not be an extra node between my current parent and it's clildren, depending on external factors. So now I have to change my xslt code in order to deal with both of these scenarios:
scenario 1:
<parent>
<child/>
<child/>
<parent>
scenario 2:
<parent>
<nuisance>
<child/>
<child/>
</nuisance>
<parent>
I have situations in which I test="parent/child" or otherwise use this format of accessing a parent/node.
I need something like test="parent/magic(* or none)/child"
They only way I know of that can solve this problem is to use:
<xsl:choose>
<xsl:when test="parent/child">
<!-- select="parent/child"-->
</xsl:when>
<xsl:otherwise>
<!-- select="parent/*/child"-->
</xsl:otherwise>
</xsl:choose>
But this will triple my code in size and will be a lot of manual labour...
Help much appreciated!
Why not simply select the union of the two?
<xsl:apply-templates select="parent/child|parent/*/child"/>
This will select the correct nodes in both cases.
I have situations in which I
test="parent/child" or otherwise use
this format of accessing a
parent/node.
I need something like
test="parent/magic(* or none)/child"
This expression may be faster:
parent/child or parent/*/child
than the expression:
parent/child|parent/*/child
Almost any XPath engine will immediately stop the evaluation at the first occurence of parent/child or at the first occurence of parent/someElement/child
On the other side, the second expression selects the union of all parent/child and parent/*/child elements and there may be many such elements.
Even worse is:
<xsl:apply-templates select="parent/child|parent/*/child"/>
A test, as the original question needs, is very different from applying templates on all nodes that match this test. Just testing for a condition can be significantly more efficient. The OP hasn't indicated in any way that the test is in any way connected to applying templates.

XSLT: keeping whitespaces when copying attributes

I'm trying to sort Microsoft Visual Studio's vcproj so that a diff would show something meaningful after e.g. deleting a file from a project. Besides the sorting, I want to keep everything intact, including whitespaces. The input looks like
space<File
spacespaceRelativePath="filename"
spacespace>
...
The xslt fragment below can add the spaces around elements, but I can't find out how to deal with those around attributes, so my output looks like
space<File RelativePath="filename">
xslt I use for the msxsl 4.0 processor:
<xsl:for-each select="File">
<xsl:sort select="#RelativePath"/>
<xsl:value-of select="preceding-sibling::text()[1]"/>
<xsl:copy>
<xsl:for-each select="text()|#*">
<xsl:copy/>
</xsl:for-each>
Those spaces are always insignificant in XML, and I believe that there is no option to control this behavior in a general way for any XML/XSLT library.
XSLT works on a tree representation of the input XML. Many of the irrelevant detail of the original XML has been abstracted away in this tree - for example the order of attributes, insignificant whitespace between attributes, or the distinction between " and ' as an attribute delimiter. I can't see any conceivable reason for wanting to write a program that treats these distinctions as significant.