I'm writing an XSLT transform where I'd like all namespace prefixes to be defined on the root element. By default MS seems to create a new prefix definition on the first element in the XML hierarchy to use that schema; meaning the same schema may be referenced on multiple elements should those elements not be related to a shared ancestor of the same schema.
By coding the root element as such, all works as desired:
<!-- ... -->
<ns0:root xmlns:ns0="http://some/schema" xmlns:ns1 = "http://another/schema">
<!-- rest of XSLT; including calls to other templates -->
</ns0:root>
<!-- ... -->
However I can't find any way to code this using xsl:element; e.g.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://some/schema"
xmlns:ns1 = "http://another/schema"
>
<!-- ... -->
<xsl:element name="ns0:root">
<xsl:attribute name="ns1" namespace="http://www.w3.org/2000/xslns/">http://another/schema</xsl:attribute>
<!-- rest of XSLT; including calls to other templates -->
</xsl:element>
<!-- ... -->
Is it possible to declare namespace prefixes against an xls:element for schemas other than that element itself?
Full Example
XML
<Demo xmlns="http://some/schema">
<a>Hello</a>
<b>World</b>
</Demo>
XSLT
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://some/schema"
xmlns:ns1 = "http://another/schema"
exclude-result-prefixes="xsl"
>
<xsl:output method="xml" indent="yes" version="1.0"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="/ns0:Demo/ns0:a">
<xsl:element name="ns1:z">
<xsl:value-of select="./text()" />
</xsl:element>
</xsl:template>
<xsl:template match="/ns0:Demo/ns0:b">
<xsl:element name="ns1:y">
<xsl:value-of select="./text()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result
<Demo xmlns="http://some/schema">
<ns1:z xmlns:ns1="http://another/schema">Hello</ns1:z>
<ns1:y xmlns:ns1="http://another/schema">World</ns1:y>
</Demo>
Desired Result
<Demo xmlns="http://some/schema" xmlns:ns1="http://another/schema">
<ns1:z>Hello</ns1:z>
<ns1:y>World</ns1:y>
</Demo>
or
<ns0:Demo xmlns:ns0="http://some/schema" xmlns:ns1="http://another/schema">
<ns1:z>Hello</ns1:z>
<ns1:y>World</ns1:y>
</ns0:Demo>
Your minimal example doesn't explain why you need to use xsl:element instead of xsl:copy and/or literal result elements but as XSLT 1.0 has no xsl:namespace instruction (https://www.w3.org/TR/xslt20/#creating-namespace-nodes) your only way is copying the namespace node from the stylesheet root, as in
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://some/schema"
xmlns:ns1 = "http://another/schema"
exclude-result-prefixes="xsl"
>
<xsl:output method="xml" indent="yes" version="1.0"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
<xsl:copy-of select="document('')/*/namespace::*[. = 'http://another/schema']"/>
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="/ns0:Demo/ns0:a">
<xsl:element name="ns1:z">
<xsl:value-of select="./text()" />
</xsl:element>
</xsl:template>
<xsl:template match="/ns0:Demo/ns0:b">
<xsl:element name="ns1:y">
<xsl:value-of select="./text()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
(or any other node having that, such as parameter or variable, but that way you additionally might to convert a result tree fragment to a node set first with exsl:node-set or ms:node-set).
As for why literal result elements and xsl:element give you different results, well, https://www.w3.org/TR/xslt#literal-result-element says:
The created element node will also have a copy of the namespace nodes
that were present on the element node in the stylesheet tree ...
while https://www.w3.org/TR/xslt#section-Creating-Elements-with-xsl:element does not say that.
It is important to understand that although they are represented in XML documents via namespace-declaration attributes, in XPath's and XSLT's data model, the in-scope namespaces for each element are modeled via namespace nodes, not attribute nodes. Moreover, distinct elements do not share namespace nodes; each gets its own set. When using the XML output method, an XSLT processor is responsible for producing namespace declaration attributes that correctly represent the namespace nodes present in the result tree.
That fully explains why Section 7.1.3 of the XSLT 1.0 spec explicitly disallows creating a namespace declaration via an xsl:attribute element:
XSLT processors may make use of the prefix of the QName
specified in the name attribute when selecting the prefix used for
outputting the created attribute as XML; however, they are not
required to do so and, if the prefix is xmlns, they must not do so.
Thus, although it is not an error to do:
<xsl:attribute name="xmlns:xsl" namespace="whatever">http://www.w3.org/1999/XSL/Transform</xsl:attribute>
it will not result in a namespace declaration being output.
(Emphasis added.) If creating a namespace declaration that way were permitted then it would allow for the result document to express namespace nodes that were not actually present in the result tree.
An element in the result tree can obtain a namespace node in any of these ways:
result elements created via xsl:copy or xsl:copy-of receive copies of the original element's namespace nodes.
result elements created via literal result elements in the stylesheet tree get copies of all the namespace nodes of the stylesheet element, whether declared directly on that element or on an ancestor element, with some exceptions.
result elements created via xsl:element stylesheet elements are not explicitly specified to receive any namespace nodes, but in practice, to correctly implement the spec they need to receive a namespace node for the namespace, if any, of the element's name.
Because only elements have namespace nodes, it follows (but is not explicitly specified) that each element must also receive a namespace node for each namespace to which one of its attributes' names belongs, if that namespace differs from that of the element's own name.
a namespace node itself can be copied to the result tree, as demonstrated by the other answer.
There is no reason to expect that an XSLT processor would create additional namespace nodes in the result tree beyond those. In particular, although that might afford the possibility of a simpler XML serialization of the result tree, the tree itself would be strictly more complex.
One way, then, to ensure that the <Demo> element in your result document carries a namespace declaration for a namespace other than that element's own, any obtained by copying the result element from the input tree, or that of an attribute of the element, is to use a literal result element:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://some/schema"
xmlns:ns1 = "http://another/schema">
<xsl:output method="xml" indent="yes" version="1.0"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/ns0:Demo">
<ns0:Demo>
<xsl:apply-templates select="#* | node()" />
<ns0:Demo>
</xsl:template>
<!-- ... -->
</xsl:stylesheet>
On the other hand, if you must create the element via an xsl:element element -- which should only be necessary if its name needs to be computed -- then you'll need to copy a namespace node from the input tree.
Related
I'm trying to select a node that contains all of the sub-nodes AS IS using a xsl stylesheet. I have a section in my xml doc that contain a dynamic amount of sub-nodes, each with 2 pieces of data, and I just want to grab as they are in put them in my new xml doc (I am transforming other pieces of data in the xml doc).
here is my xml doc i'm trying to transform:
<?xml version="1.0" encoding="utf-8"?>
<Active_Booking_Release_Report>
<Inmate_Data>
<bunch of fields i've already gotten to work />
....
</Inmate_Data>
<Charges_Data>
<charge>
<OffenseStatute>12345</OffenseStatute>
<OffenseOffenseDescription>For Court Only</OffenseOffenseDescription>
</charge>
<charge>
<OffenseStatute>67890</OffenseStatute>
<OffenseOffenseDescription>For Court Only</OffenseOffenseDescription>
</charge>
</Charges_Data>
</Active_Booking_Release_Report>
and my stylesheet
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Match the root node -->
<xsl:template match="/">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="/">
<xsl:copy>
<booking>
<inmateData>
<xsl:apply-templates select="*|#*|text()|comment()"/>
</inmateData>
</booking>
</xsl:copy>
</xsl:template>
a whole bunch of statements for <Inmate_Data>
and the Charges section
<xsl:template match="//Active_Booking_Release_Report/Charges_Data">
<xsl:copy-of select="node()"/>
</xsl:template>
</xsl:stylesheet>
and my desired output:
<booking>
<inmateData>
<transformed fields/>
<charge>
<OffenseStatute>12345</OffenseStatute>
<OffenseOffenseDescription>For Court Only</OffenseOffenseDescription>
</charge>
<charge>
<OffenseStatute>67890</OffenseStatute>
<OffenseOffenseDescription>For Court Only</OffenseOffenseDescription>
</charge>
</Inmate Data>
</booking>
I'm loading and transforming the xml in a c# console app, and when I inspect the object containing the transformed data, everything is listed there except for the charges section.
Well, firstly, you've got two template rules that match "/" and only one of them is going to match, and in XSLT 1.0 it's pretty implementation-dependent what happens when two templates match.
Secondly, your desired output isn't well-formed; I guess it was intended as a sketch, but it's not really clear what you wanted.
Thirdly, you haven't shown us the actual output.
Finally, you've left a whole load of your stylesheet code out: "a whole bunch of statements".
I'd suggest you fix the obvious error of the two duplicate template rules, then show us a complete listing of a runnable stylesheet, source document, and desired output, so we can actually see where you've gone wrong.
Hey all I'm trying to write an xslt template that uses a msxsl to create a hyperlink from a web.config appSetting. Every time I try to run the code, it tells me that I have declared the c# method in the script twice. The code is as follows:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:files="urn:my-script" >
<msxsl:script implements-prefix="files" language="CSharp">
<msxsl:assembly name="System.Configuration"/>
<msxsl:using namespace="System.Configuration"/>
<![CDATA[
public string LinkFile()
{
string link = System.Configuration.ConfigurationManager.AppSettings["fileUrl"];
return link;
}
]]>
</msxsl:script>
<xsl:template name="GenerateLinkFile">
<xsl:param name="fileName"/>
<xsl:param name="fileId"/>
<xsl:choose>
<xsl:when test="$fileName = ''">
<xsl:value-of select="$fileName"/>
</xsl:when>
<xsl:otherwise>
<a href="files:LinkFile()">
<xsl:value-of select="$fileName"/>
</a>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The error I'm getting is as follows at runtime when it tries to generate the hyperlink:
System.Xml.Xsl.XslLoadException: Type 'System.Xml.Xsl.CompiledQuery.Script1' already defines a member called 'LinkFile' with the same parameter types.
I ran your XSLT against a sample XML file and it ran well; this led me to believe you are probably calling this XSLT multiple times from other XSLT files.
The best way to handle this is that, if you have a root transform calling other transforms, to include it from there, so it is only referenced once; the aim is to ensure that the function is only included once throughout your transforms, otherwise you will encounter the error you are seeing.
Alternatively, call this transformation independently - a common approach is to apply XSLT's to the source document in turn, performing sets of transformations one at a time.
I have an application written in C# that needs to apply a template name to an xml file that is defined in an XSLT.
Example XML:
<Data>
<Person>
<Name>bob</Name>
<Age>43</Age>
</Person>
<Thing>
<Color>Red</Color>
</Thing>
</Data>
Example 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:param name="TargetName" />
<xsl:param name="RootPath" />
<xsl:Template Name="AgeGrabber">
<xsl:value-of select="/Person/Age" />
</xsl:Template>
<xsl:Template Name="ColorGrabber">
<xsl:value-of select="/Color" />
</xsl:Template>
</xsl:stylesheet>
Say I wanted to run the template "ColorGrabber" with path "/Data/Thing" and then run another transform with the template "AgeGrabber" with path "/Data". Is this possible? I was thinking I could pass in the path and the template name (hense the 2 params at the top) and then do some type of switch but it looks like xsl:call-template can not take a parameter as the name attribute.
How can I achieve this behaviour?
There are a number of issues with this question:
<xsl:stylesheet version="2.0" ... is specified, however, at present >NET doesn't natively support XSLT 2.0.
Thecode example is not too meaningful, because a single XML document cannot contain both /Person/Age and /Color elements -- a wellformed XML document has only a single top element and it can be either Person or Color, but not both.
In case there was a more meaningful example:
<Person>
<Age>27</Age>
<HairColor>blond</HairColor>
</Person>
one simple solution is:
<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="pProperty" select="'Age'"/>
<xsl:template match="/">
<xsl:value-of select="/*/*[name()=$pProperty]"/>
</xsl:template>
</xsl:stylesheet>
and when this transformation is applied on the above XML document, it produces the wanted result:
27
In case the nestedness of the elements of interest can be arbitrary and/or we need to do different processing on the different elements, then an appropriate solution is to use matching templates (not named ones):
<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="pProperty" select="'HairColor'"/>
<xsl:template match="Age">
<xsl:if test="$pProperty = 'Age'">
This person is <xsl:value-of select="."/> old.
</xsl:if>
</xsl:template>
<xsl:template match="HairColor">
<xsl:if test="$pProperty = 'HairColor'">
This person has <xsl:value-of select="."/> hair.
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document (above), again the correct result is produced:
This person has blond hair.
Finally, if you really want to simulate higher-order functions (HOF) in XSLT 1.0 or XSLT 2.0, see this answer: https://stackoverflow.com/a/8363249/36305 , or learn about FXSL.
Much simpler: prepare two apply-templates rules (for Age and Color elements) and conditionally send proper node to transform - //Person/Age or //Thing/Color
You got it backwards. You ought to create templates, matching the nodes you want to use.
<xsl:stylesheet>
<xsl:template match="Person|Thing">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="Person">
<xsl:value-of select="Age" />
</xsl:template>
<xsl:template match="Thing">
<xsl:value-of select="Color" />
</xsl:template>
</xsl:stylesheet>
I have an xslt stylesheet that needs to call a C# XSLT extension function in order to process a collection of elements. The code looks a little like this:
Xslt:
<xsl:apply-templates mode="MyTemplate" select="myextension:GetSomeCollection(#someattribute)" />
<xsl:template mode="MyTemplate" match="myroot">
<xsl:value-of select="xpath" />...<xsl:value-of select="/xpath" />
</xsl:template>
Extension method:
public XPathNavigator GetSomeCollection(string Attribute)
{
XmlDocument doc = new XmlDocument()
//etc...
return doc.CreateNavigator();
}
The extension method returns a XPathNavigator as thats the only way I can see for an extension method to return any sort of collection.
The problem I'm having is that my template (the one with mode="MyTemplate") needs to be able to access xml nodes in the root / input document to the xslt stylesheet (as well as nodes in the node set returned by the extension method), however the template only seems to have access to the xml fragment returned by GetSomeCollection - xpath expressions starting / just resolve to the start of that fragment.
I can see why this is (the template is processing an xml fragment, however that fragment belongs to a different document), however I can't see how to get around it. There doesn't seem to be any way to get the extension method to produce an xml fragment belonging to the original document.
What can I do?
Use a variable?
<xsl:variable name="root" select="/"/>
<xsl:template mode="MyTemplate" match="myroot">
<xsl:value-of select="$root/..."/>
<xsl:value-of select="xpath" />...<xsl:value-of select="/xpath" />
</xsl:template>
or a parameter:
<xsl:apply-templates mode="MyTemplate"
select="myextension:GetSomeCollection(#someattribute)">
<xsl:with-param name="foo" select="...some local query..."/>
</xsl:apply-templates>
<xsl:template mode="MyTemplate" match="myroot">
<xsl:param name="foo"/>
<xsl:value-of select="$foo/..."/>
<xsl:value-of select="xpath" />...<xsl:value-of select="/xpath" />
</xsl:template>
FIRST EDIT
I'm fetching the Child 1 tag into a DropDownList in a C# form, Plz suggest the best practise code (C#) for deleting the Parent tag & all it's child tags in an XML file.
Example:
<Parents>
<Parent>
<Child 1>Something</Child 1>
<Child 2>Something</Child 2>
<Child 3>Something</Child 3>
<Child 4>Something</Child 4>
</Parent>
<Parent>
<Child 1>Something 1</Child 1>
<Child 2>Something 1</Child 2>
<Child 3>Something 1</Child 3>
<Child 4>Something 1</Child 4>
</Parent>
</Parents>
--- Previous Question ---
How can I insert the following stylesheet tag into my new xml file which is created using C# code????
<?xml-stylesheet type="text/xsl" href="issuez.xsl"?>
C# code to create the xml file:-
new XDocument(
new XElement("issues",
new XElement("issue",
new XElement("cat", comboBox1.Text),
new XElement("desc", richTextBox1.Text),
new XElement("end", dateTimePicker1.Text),
new XElement("att", textBox2.Text)
)
)
).Save(path);
First, make sure that dates in your XML are represented in the canonical YYYY-MM-DD format, and times as HH:MM:SS, so that XSLT (which, in 1.0, doesn't have date or time datatypes) can compare and sort them.
Second, use Steve Muench's technique for grouping. You generate a key on the items' dates, using xsl:key. The key() function can then be used to find a list of all items on a given date.
Using that key, you can build a list of the distinct dates that appear in the items. This is the Muenchian technique: find each item that's the first item in the list that key() returns for that item's date. This technique guarantees that you're always get one and only one item for each distinct date value. You then sort those items, and use their dates to drive the actual production of your output.
A minimal example:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="dates" match="/data/newsitem" use="#date"/>
<xsl:template match="/">
<output>
<!-- find exactly one newsitem node for each distinct #date in the document -->
<xsl:for-each select="/data/newsitem[generate-id() = generate-id(key('dates', #date)[1])]">
<xsl:sort select="#date" order="descending"/>
<xsl:call-template name="newsitems_for_date">
<xsl:with-param name="date" select="#date"/>
</xsl:call-template>
</xsl:for-each>
</output>
</xsl:template>
<xsl:template name="newsitems_for_date">
<xsl:param name="date"/>
<h1>
<xsl:value-of select="$date"/>
</h1>
<xsl:apply-templates select="/data/newsitem[#date=$date]">
<xsl:sort select="#time" order="descending"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="newsitem">
<p>
newsitem for <xsl:value-of select="#date"/>
</p>
</xsl:template>
</xsl:stylesheet>
This is the sort of thing that the back end producing the XML should handle. XSLT isn't the best place for lots of logic. Better to embed all that in the XML after you query for the news items. Just send them to the client in the proper form so they don't have to work so hard.
An XSLT stylesheet allows global parameters that can be set before a transformation is run. So with XSLT 1.0 and .NET's XslCompiledTransform if you need the current date in your stylesheet you can define a global parameter
<xsl:param name="current-date"/>
and set that before running the transformation by creating an XsltArgumentList, setting the parameter to a value and format you want/need and then pass that XsltArgumentList as the second argument to the Transform method. Then in your stylesheet you can compare the date in an XML input element or attribute to the parameter.
As you use .NET a different option is to use XSLT 2.0; Microsoft does not support that but with Saxon 9 there is a third party solution. XSLT/XPath 2.0 have a function named current-date, that way you don't need a parameter.
If you use the XSLT processor from PHP, you can use PHP-functions inside your XSLT script. All you need is call registerPhpFunctions before transformation. The result value in the right order can be used for sorting.