I want to convert:
<ppx xmlns="http://www.p.com/ppx/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.p.com/ppx/1 http://www.p.com/ppx/1/ppx.xsd">
<p></p></ppx>
into:
<ppx xmlns="http://www.p.com/ppx/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ppxx="http://www.m.com/mExt/v1"
xmlns:ppxtpx="http://www.m.com/mExt/v3"
xsi:schemaLocation="http://www.p.com/ppx/1 http://www.p.com/ppx/1/ppx.xsd
http://www.m.com/mExt/v1 http://www.m.com/mExt/v1/ppxv1.xsd
http://www.m.com/mExt/v3 http://www.m.com/mExt/v3/ppxv3.xsd">
<p></p></ppx>
I need to add a few namespace declarations and their associated schemaLocations to an existing XML file without changing anything else in that XML.
In principle it's easy: it just needs a standard "modified identity template" pattern:
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="ppx">
<ppx xmlns="http://www.p.com/ppx/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ppxx="http://www.m.com/mExt/v1"
xmlns:ppxtpx="http://www.m.com/mExt/v3"
xsi:schemaLocation="http://www.p.com/ppx/1 http://www.p.com/ppx/1/ppx.xsd
http://www.m.com/mExt/v1 http://www.m.com/mExt/v1/ppxv1.xsd
http://www.m.com/mExt/v3 http://www.m.com/mExt/v3/ppxv3.xsd">
<xsl:apply-templates/>
</ppx>
</xsl:template>
However, it could get a bit more complicated depending on how much the input can vary from the example you have shown us. For example if the root element will not always be named ppx, or if the namespaces to be added are not known in advance. So you may need to explain more details of the problem
Related
Doing some work with xsl - first time I've done anything serious, and I've hit something which I can't explain. Easiest way to show it is with the identity transform:
This works:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
This doesn't (says "Unable to apply transformation on current source"):
<xsl:template match="#*|node()" xml:space='preserve'>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
This does:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="node()" xml:space='preserve'/>
</xsl:copy>
</xsl:template>
OK, I can see what's happening. But I don't understand why. Why does xml:space not want to play nicely with attributes? Just curious.
BTW, this is using the xsl translator that's built into Notepad++. Perhaps I shouldn't trust it?
What are you trying to accomplish? xml:space="preserve" tells XML-consuming applications that you want to preserve whitespace-only text nodes that are descendants of the element that xml:space is an attribute of. In this example, you have xml:space as an attribute of <xsl:apply-templates>, but <xsl:apply-templates> has no whitespace-only text node descendants, so xml:space has no possible effect.
I think you wanted to preserve whitespace-only text nodes from the input XML document (not from the XSLT stylesheet). In that case, you need xml:space to be in the input XML document, not in the XSLT stylesheet. The stylesheet can have xsl:preserve-space-elements="*", but that's already the default, unless you have xsl:strip-space-elements set.
Yes, I would be inclined to wonder whether the XSLT processor used by Notepad++ (libxml) is doing something illegit. As a good diagnostic, try a respected processor like Saxon and see if you get any errors.
Either that, or just remove xml:space from your stylesheet, since it won't do you any good even if the processor doesn't throw an error.
Suggestion:
Just use
<xsl:output method="html" indent="yes"/>
as the first child of <xsl:stylesheet>.
The indent="yes" will prevent all the output elements from being crammed together on one line, so you can read the results.
Whitespace is not preserved for attributes according to specification - it is highlighted in this posting. Preserving attribute whitespace in XSLT
I'm trying to create an XSLT library for the common task of passing through most of the content of XML data with small alterations.
The include file currently looks like this (pass-through.xslt):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- change default behaviour to copying all attributes and nodes -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- copy element but add a child node -->
<xsl:template name="append">
<xsl:param name="element"/>
<xsl:param name="appendage"/>
<xsl:element name="{name($element)}">
<xsl:copy-of select="$element/namespace::*"/>
<xsl:copy-of select="$element/#*"/>
<xsl:apply-templates select="$element/node()"/>
<xsl:copy-of select="$appendage"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
You could then create a stylesheet which included it and not have to worry repeating yourself over and over (calling stylesheet).
<xsl:stylesheet version="1.0"
xmlns:ns="http://example/namespace"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:include href="pass-through.xslt"/>
<!-- only worry about the transform you want -->
<xsl:template match="element-to-be-modified">
...
</xsl:template>
</xsl:stylesheet>
If you only want to add an element to the document then call "append".
<xsl:stylesheet
This works except for namespaces on an element I'm appending to. If I have an XML file with a namespace on the root element it chokes saying there is no namespace binding for the prefix. If I add the namespace of the root element to the library then it's happy but that kind of defeats the purpose of the library if you've got to go modifying it for every use.
I'd be happy adding the xmlns:ns="uri" to the calling stylesheet but the scope of the namespace declaration seems to only extend to that stylesheet - not to the included one where the "append" template is.
I'd like to be able to transform
<ns:root xmlns:ns="http://example/namespace">
<ns:child>old child</ns:child>
</ns:root>
To
<ns:root xmlns:ns="http://example/namespace">
<ns:child>old child</ns:child>
<ns:child>new child!</ns:child>
</ns:root>
Without having to include the identity transform boilerplate every time. I've tried an assortment of things, including adding a namespace="{namespace-uri()}" to the element in the "append" template but nothing seems to preserve the namespace prefix on the element appended to.
Replace
<xsl:element name="{name($element)}">
by
<xsl:element name="{name($element)}" namespace="{namespace-uri($element)}">
If, when you call your named template "append" you are already positioned on the element you are copying/modifying, then there really is no need to pass the current element as a parameter. You can just use xsl:copy here
<xsl:template name="append">
<xsl:param name="appendage"/>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:copy-of select="$appendage"/>
</xsl:copy>
</xsl:template>
In your example, you would just call it like this
<xsl:template match="ns:root">
<xsl:call-template name="append">
<xsl:with-param name="appendage"><ns:child>new child!</ns:child></xsl:with-param>
</xsl:call-template>
</xsl:template>
Using xsl:copy should copy your element and retain the namespace.
I have to write an XSLT without knowing the input XML. So I want to start by writing an XSLT that will simply return the input XML without any transformation. Can I do that?
Look at this:
http://mrhaki.blogspot.com/2008/07/copy-xml-as-is-with-xslt.html
<xsl:template match="/">
<xsl:copy-of select="."/>
</xsl:template>
What you want to do is known as the Identity Transform. To be general, you need to ensure that all attribute and non-attribute nodes are copied, recursively:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note that the identity transform does not guarantee that the output is identical on the surface level (i.e. some hash calculation might yield a different result, for instance). E.g. attributes could be reordered - this has no impact on the infoset or validity.
I need to transform an XML file to another XML file, where the source file has a dynamic namespace set to xmlns="whatever". My XSLT runs fine without the namespace being in the file, but I get no output with the namespace. How can I cause the schema of the source file to be applied to the destination file?
All help is appreciated and thanks in advance!
EDIT:
I'm trying to copy the namespace uri over to the resulting file:
<xsl:param name="schema">
<xsl:value-of select="namespace-uri()" />
</xsl:param>
<xsl:element name="root" namespace="$schema">
I have verified that schema is holding the correct value, but the problem is that the program appears to take this too literally:
<root xmlns="$schema">
Is this the right way to go about this?
EDIT x2:
I've implemented Alejandro's suggestion of:
<xsl:element name="root" namespace="{$schema}"/>
And that works for the most part, except for the fact that I have to put the namespace on every element or else I get the following structure in the result:
<root xmlns="NAMESPACE">
<foo xmlns="">
etc.
Is there a way to blanket all of the elements with this namespace, other than putting namespace={$schema} on every single line? Bounty and accept for the best answer!
EDIT x3:
Better example:
If I do:
<xsl:element name="root" namespace="{namespace-uri()}>
<xsl:element name="foo">
<xsl:element name="bar">
<!--etc-->
</xsl:element>
</xsl:element>
</xsl:element>
I get:
<root xmlns="NAMESPACE">
<foo xmlns="">
<bar>
<!--etc-->
</bar>
</foo>
<root>
I would like to have them all under namespace NAMESPACE, so I did:
<xsl:element name="root" namespace="{namespace-uri()}>
<xsl:element name="foo" namespace="{namespace-uri()}>
<xsl:element name="bar" namespace="{namespace-uri()}>
<!--etc-->
</xsl:element>
</xsl:element>
</xsl:element>
However this is ugly and tedious to type. Is there an easier way to blanket the namespace over all elements? (hopefully this clarifies what I need)
Suppose you have this XML input:
<root xmlns="survivors">
<louis/>
<francis/>
</root>
Meaning that every element is under default namespace wich its URI is "survivors".
As Welbog wrote you can select francis element with:
/*/*[local-name()='francis']
or
/*[local-name()='root']/*[local-name()='francis']
But, that also select francis element from these XML inputs:
<root xmlns="survivors" xmlns:n="no-survivors">
<louis/>
<n:francis/>
</root>
or
<root xmlns="survivors">
<louis/>
<francis xmlns="no-survivors"/>
</root>
You could also strengthen the predicate with some namespace URI. But, wich one? An option could be the default namespace for root element like:
/*/*[local-name()='francis'][namespace-uri()=namespace-uri(/*)]
Surely this make XPath expression very verbose.
In XSLT 2.0 you could use xsl:xpath-default-namespace attribute like:
<xsl:value-of select="/root/francis" xpath-default-namespace="survivors"/>
But that's not good for your case because you don't know the URI in advance.
EDIT: xsl:element 's attributes are AVT (Attribute Value Template) so you need this:
<xsl:element name="root" namespace="{$schema}"/>
Also, I recomend you to declare the param as a string data type (not RTF like now), something like:
<xsl:param name="schema" select="namespace-uri()"/>
EDIT 2: Maybe I was not clear. You don't need xsl:element/#namespace in every case. Following your statement that every element is in only one default namespace, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[local-name()='bar']">
<xsl:element name="newbar" namespace="{namespace-uri()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
With this input:
<root xmlns="whatever">
<foo/>
<bar/>
</root>
Output:
<root xmlns="whatever">
<foo></foo>
<newbar></newbar>
</root>
Edit 2: I was showing you that when you are copying an element you are also copying the namespace needed for expand the QName. So, if want to transform this:
<root xmlns="whatever">
<foo/>
<bar/>
</root>
Into this:
<root xmlns="whatever">
<foo>
<bar/>
</foo>
</root>
You can use this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="*[1]|following-sibling::*[1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Since the namespace is part of the full name of any elements your XPath is referencing, and since you don't know the namespace of the source file in advance, you'll have to use the local names of the elements you're accessing instead of their full names.
Let's say you have a source file like this:
<root xmlns="survivors">
<louis/>
<francis/>
</root>
One way to access these elements using XSLT is to specify a namespace that matches the source file's default namespace:
<xsl:stylesheet
version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:surv="survivors"
>
<xsl:template match="surv:louis">
<!-- ETC -->
That way works when you know the namespace. When you don't know the namespace, you can ignore it using the XPath function local-name() like this:
<xsl:stylesheet
version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="*[local-name() = 'louis']">
<!-- ETC -->
Using local-name() means you can ignore the namespace in the source document. You'll have to be careful if there are multiple elements with the same local name in different namespace, though. It's not exactly a robust solution, but if you can't trust your namespace then you don't have that many options anyway.
I'd imagine that having a variable namespace is a bigger problem in and of itself. If that's under your control, you should correct it. If it's not under your control, you should push to have it corrected.
XSLT is rarely executed in a vaccum. It's almost always a part of some other application that loads the XSLT, loads the source document, and then produces the output.
Assuming the above is your scenario, I wouldn't solve this problem directly with XSLT. Instead, I'd use standard XML technologies to examine the source XML document and discover the default namespace. Then, I'd load the XSLT document and use string substitution or some other technique to inject the resultant namespace at the appropriate point in the transform. Then I'd go ahead and run the transform normally.
This will make your XSLT much more natural to write and maintain. I'm a pro with XSLT and use it constantly, and this is how I would solve it. I couldn't imagine the ugliness of a stylesheet that had to use local-name() comparisons constantly. What a pain. (Of course, in much more complex scenarios there may be no choice. Fortunately yours isn't one of them.)
If you don't have this option, I sympathize.
I have a small question regarding XSLT template overriding.
For this segment of my XML:
<record>
<medication>
<medicine>
<name>penicillin G</name>
<strength>500 mg</strength>
</medicine>
</medication>
</record>
In my XSLT sheet, I have two templates in the following order:
<xsl:template match="medication">
<xsl:copy-of select="." />
</xsl:template>
<xsl:template match="medicine/name">
<text>!unauthorized information!</text>
</xsl:template>
What I want to do is to copy everything under the medication element to the output other than the "name" element (or any other element that I explicitly define). The final xml will be shown to the user in RAW XML form. In other words, the result I want is:
<record>
<medication>
<medicine>
<text>! unauthorized information!</text>
<strength>500 mg</strength>
</medicine>
</medication>
</record>
Whereas I am getting the same XML as input, i.e. without the element replaced by text. Any ideas why the second template match is not overriding the name element in the first one? Thanks in advance
--
Ali
Template order does not matter. The only case it possibly becomes considered (and this is processor-dependent) is when you have an un-resolvable conflict, i.e. an error condition. In that case, it's legal for the XSLT processor to recover from the error by picking the one that comes last. However, you should never write code that depends on this behavior.
In your case, template priority isn't even the issue. You have two different template rules, one matching <medication> elements and one matching <name> elements. These will never collide, so it's not a question of template priority or overriding. The issue is that your code never actually applies templates to the <name> element. When you say <xsl:copy-of select="."/> on <medication>, you're saying: "perform a deep copy of <medication>". The only way any of the template rules will fire for descendant nodes is if you explicitly apply templates (using <xsl:apply-templates/>.
The solution I have for you is basically the same as alamar's, except that it uses a separate processing "mode", which isolates the rules from all other rules in your stylesheet. The generic match="#* | node()" template causes template rules to be recursively applied to children (and attributes), which gives you the opportunity to override the behavior for certain nodes.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- ...placeholder for the rest of your code... -->
<xsl:template match="/record">
<record>
<xsl:apply-templates/>
</record>
</xsl:template>
<!-- end of placeholder -->
<xsl:template match="medication">
<!-- Instead of copy-of, whose behavior is to always perform
a deep copy and cannot be customized, define your own
processing mode. Rules with this mode name are isolated
from the rest of your code. -->
<xsl:apply-templates mode="copy-medication" select="."/>
</xsl:template>
<!-- By default, copy all nodes and their descendants -->
<xsl:template mode="copy-medication" match="#* | node()">
<xsl:copy>
<xsl:apply-templates mode="copy-medication" select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!-- But replace <name> -->
<xsl:template mode="copy-medication" match="medicine/name">
<text>!unauthorized information!</text>
</xsl:template>
</xsl:stylesheet>
The rule for "medicine/name" overrides the rule for "#* | node()", because the format of the pattern (which contains a "/") makes its default priority (0.5) higher than the default priority of "node()" (-1.0).
A complete but concise description of how template priority works can be found in "How XSLT Works" on my website.
Finally, I noticed you mentioned you want to display "RAW XML" to the user. Does that mean you want to display, for example, the XML, with all the start and end tags, in a browser? In that case, you'd need to escape all markup (e.g., "<" for "<"). Check out the XML-to-string utility on my website. Let me know if you need an example of how to use it.
Add
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
to your <xsl:template match="medicine/name">
And remove <xsl:template match="medication"> altogether!
<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="medicine/name">
<text>!unauthorized information!</text>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>