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.
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.
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.
I have been searching for the solution to convert XML into CSV, but I cannot find one which matches my case as XML structure is different
XML structure looks like
<VWSRecipeFile>
<EX_Extrusion User="ABC" Version="1.0" Description="" LastChange="41914.7876341204">
<Values>
<C22O01_A_TempFZ1_Set Item="A_TempFZ1_Set" Type="4" Hex="42700000" Value="60"/>
<C13O02_A_TempHZ2_Set Item="A_TempHZ2_Set" Type="4" Hex="43430000" Value="195"/>
<C13O03_A_TempHZ3_Set Item="A_TempHZ3_Set" Type="4" Hex="43430000" Value="195"/>
</Values>
</EX_Extrusion>
</VWSRecipeFile>
Expected CSV Format
A_TempFZ1_Set,A_TempHZ2_Set,A_TempHZ3_Set
60,195,195
i can achieve the new expected csv format, but don't know if it is the best way to do it, any suggestion is appreciated
'
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:template match="/VWSRecipeFile">
<xsl:for-each select="EX_Extrusion/Values/*">
<xsl:value-of select="concat(#Item,',')" />
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="EX_Extrusion/Values/*">
<xsl:value-of select="concat(#Value,',')" />
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>'
Thanks
One way you can do this is to use XSLT, the language designed to work with XML. You surely can parse the XML with C# but I like XSLT cause it's cleaner.
You define an external XSLT file, then call it within C# to do the transform.
Edit: added new columns based on new requirements.
File C:\XmlToCSV.xslt (
is the newline character)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:template match="/VWSRecipeFile">
<xsl:variable name="User" select="EX_Extrusion/#User"/>
<xsl:variable name="Version" select="EX_Extrusion/#Version"/>
<xsl:variable name="Description" select="EX_Extrusion/#Description"/>
<xsl:variable name="LastChange" select="EX_Extrusion/#LastChange"/>
<xsl:text>Item,Type,Hex,Value,User,Version,Description,LastChange
</xsl:text>
<xsl:for-each select="EX_Extrusion/Values/*">
<xsl:value-of select="concat(#Item,',',#Type,',',#Hex,',',#Value,',',$User,',',$Version,',',$Description,',',$LastChange,'
')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Apply the transform with XslCompiledTransform:
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load("C:\\XmlToCSV.xslt");
xslt.Transform("InputFile.xml", "OutputFile.csv");
Adjust it based on your needs.
Basic idea would be to iterate though values nodes and select the attributes you want for each node and keep writing them to a file with comma separator. Simply name the file as .csv. If you want something ready made, check this out.
XSLT is one way to do it. Alternatively you can use, Cinchoo ETL - an open source library available to parse xml, produce CSV the way you want it.
string xml = #"<VWSRecipeFile>
<EX_Extrusion User=""ABC"" Version=""1.0"" Description="""" LastChange=""41914.7876341204"">
<Values>
<C22O01_A_TempFZ1_Set Item=""A_TempFZ1_Set"" Type=""4"" Hex=""42700000"" Value=""60""/>
<C13O02_A_TempHZ2_Set Item=""A_TempHZ2_Set"" Type=""4"" Hex=""43430000"" Value=""195""/>
<C13O03_A_TempHZ3_Set Item=""A_TempHZ3_Set"" Type=""4"" Hex=""43430000"" Value=""196""/>
</Values>
</EX_Extrusion>
</VWSRecipeFile>";
StringBuilder sb = new StringBuilder();
using (var p = ChoXmlReader.LoadText(xml).WithXPath("/Values/*"))
{
using (var w = new ChoCSVWriter(sb)
.WithFirstLineHeader()
)
w.Write(p.ToDictionary(r => r.Item, r => r.Value).ToDynamic());
}
Console.WriteLine(sb.ToString());
Output:
A_TempFZ1_Set,A_TempHZ2_Set,A_TempHZ3_Set
60,195,196
Disclaimer: I'm the author of this library.
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 xml in which i have stored some html under comments like this
<root>
<node>
<!--
Mail me
-->
</node>
</root>
now in my Transform Xslt code of mine i am giving XPathNavigator which is pointing to node and in xslt i am passing the comment value of as a parameter.
assuming $href to be Mail me
in xslt i am doing <xsl:value-of select="$href" disable-output-escaping="yes">
but $href is still escaped the result of xslt transformation comes up with < >
Does any one know whats wrong with it any help in this regard would be highly appericiated.
Thanks
Regards
Azeem
When part of the comment the node looses its special meaning - thus "href" is not a node so you cannot use it to select stuff.
You can select comments like this:
<xsl:template match="/">
<xsl:value-of select="/root/node/comment()" disable-output-escaping="yes"/>
</xsl:template>
This will produce based on your XML input:
cristi:tmp diciu$ xsltproc test.xsl test.xml
<?xml version="1.0"?>
Mail me
As diciu mentioned, once commented the text inside is no longer XML-parsed.
One solution to this problem is to use a two-pass approach. One pass to take out the commented node and place it into normal XML, and a second pass to enrich the data with your desired output: Your Text Here.
A second, single-pass approach would be to extract the text you need from the comment (in this case the email address) via a regular expression (or in our case just pulling from the XML), and then create the markup needed around it.
<xsl:template match="ahrefmail/comment()">
<xsl:element name="a">
<xsl:attribute name="href" select="../../mail"/>
<xsl:attribute name="class" select="'text'"/>
<xsl:text>Mail Me!</xsl:text>
</xsl:element>
</xsl:template>
This assumes you already have an identity template in place
i did tried what u just said didn't worked the xml i am using is
<?xml version="1.0" ?>
<root>
<institution id="1">
<data>
<ahrefmail>
<!--
<a href='mailto:ibank#abibbankuk.com' class='text'></a>
-->
</ahrefmail>
<mail>
ibank#abibbankuk.com
</mail>
</data>
</institution>
<institution id="2">
<data>
<ahrefmail>
<!--
<a href='mailto:ibank#abibbankuk2.com' class='text'></a>
-->
</ahrefmail>
<mail>
ibank#abibbankuk2.com
</mail>
</data>
</institution>
</root>
in xslt i am doing
where $id is passed as parameter == 1
the ahrefmail node is still escaped with lt & gt
Thanks
Regards
Azeem