I have an XML like this:
<table name="tblcats">
<row>
<Id>1741</Id>
<Industry>Oil & Gas - Integrated</Industry>
<ParentId>1691</ParentId>
</row>
<row>
<Id>1690</Id>
<Industry>Commodities</Industry>
<ParentId>1691</ParentId>
</row>
<row>
<Id>1691</Id>
<Industry>Capital Goods</Industry>
<ParentId>0</ParentId>
</row>
</table>
I want to create a Treeview from this XML so that table is parent node and then nodes ParentId 0 is second parent and then child nodes with Parent Id greater than 0
Like this:
+Table
+Capital Goods
Commodities
Oil & Gas - Integrated
How can I do this? Please suggest
Regards,
Asif Hameed
A fairly simplistic approach would be to use the standard ASP.NET controls XmlDataSource and TreeView and use an XSLT transform file to transform the XML you have into something that the TreeView control likes.
So, assuming you have the XML above in a file called cats.xml, the ASP.NET page markup would look like:
<asp:XmlDataSource ID="CatsXml" runat="server" DataFile="~/cats.xml" TransformFile="~/cats.xslt"></asp:XmlDataSource>
<asp:TreeView ID="CatsTree" runat="server" DataSourceID="CatsXml">
<DataBindings><asp:TreeNodeBinding TextField="name" ValueField="id" /></DataBindings>
</asp:TreeView>
and the XSLT file (cats.xslt) would be:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="table">
<table id="-1" name="Table">
<xsl:for-each select="/table/row[ParentId = 0]">
<industry>
<xsl:attribute name="id">
<xsl:value-of select="Id"/>
</xsl:attribute>
<xsl:attribute name="name">
<xsl:value-of select="Industry"/>
</xsl:attribute>
<xsl:call-template name="industry-template">
<xsl:with-param name="pId" select="Id" />
</xsl:call-template>
</industry>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="industry-template">
<xsl:param name="pId" />
<xsl:for-each select="/table/row[ParentId = $pId]">
<industry>
<xsl:attribute name="id">
<xsl:value-of select="Id"/>
</xsl:attribute>
<xsl:attribute name="name">
<xsl:value-of select="Industry"/>
</xsl:attribute>
<xsl:call-template name="industry-template">
<xsl:with-param name="pId" select="Id" />
</xsl:call-template>
</industry>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Stuart.
Related
I'm not very good at XSLT, so hopefully someone can help :)
I'm trying to convert an HTML template (called from C# code), replacing placeholders with data from an XML file.
The (simplified) HTML template looks like:
<html>
<body>
Dear $firstName $lastName,
</body>
</html>
The XML file looks like:
<inputXml>
<firstName>Joske</firstName>
<lastName>Vermeulen</lastName>
</inputXml>
And the XSLT I came up with so far looks like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
version="1.0"
xmlns:s0="http://www.w3.org/1999/xhtml">
<xsl:output omit-xml-declaration="yes" method="text" version="1.0" />
<xsl:variable name="templateMessage" select="document('stream:///TemplateMessage')" />
<xsl:variable name="inputData" select="/" />
<xsl:template match="/">
<xsl:apply-templates select="$templateMessage/*/node()" />
</xsl:template>
<xsl:template match="*/*">
<xsl:value-of select="$inputData//*[name()=name(current())]"/>
</xsl:template>
</xsl:stylesheet>
For some reason the output of the transformation is just empty, because the last match is probably not correct. (If I omit the last match, I get the original HTML template as output).
Anyone sees what I did wrong?
Ok, found a solution.
The template has placeholders like this:
<html>
<body>
Dear <span class="placeholder">firstName</span> <span class="placeholder">lastName</span>,
</body>
</html>
And the XSLT looks like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
version="1.0"
xmlns:s0="http://www.w3.org/1999/xhtml">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<!-- This is the XML data to use to replace the placeholders in the HTML template with -->
<xsl:variable name="inputData" select="document('stream:///InputData')" />
<xsl:variable name="placeholders">
<list>
<placeholder id="firstName" value="{$inputData/inputXml/firstName}" />
<placeholder id="lastName" value="{$inputData/inputXml/lastName}" />
</list>
</xsl:variable>
<!-- Take the HTML template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Replace every placeholder in the HTML template with the value from the XML data defined by its XPATH -->
<xsl:template match="span[#class='placeholder']">
<xsl:variable name="this" select="node()"/>
<xsl:value-of select="msxsl:node-set($placeholders)/list/placeholder[#id = $this]/#value" />
</xsl:template>
</xsl:stylesheet>
For just simple replacing the $firtstname and $lastname from the xml file I tried something like this and it works. Let me know if something is not clear. Just sharing with you in case it helps.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
version="2.0">
<xsl:variable name="templateMessage" select="document('MyXmldata.xml')" />
<xsl:variable name="inputData" select="/" />
<xsl:template match="/">
<html>
<body>
<xsl:variable name="firstName" select="$templateMessage/inputXml/firstName" />
<xsl:variable name="lastName" select="$templateMessage/inputXml/lastName" />
<xsl:variable name="StringValue" select="node()" />
<xsl:variable name="StringValue1" select="replace($StringValue,'\$firstName',$firstName)" />
<xsl:variable name="StringValue2" select="replace($StringValue1,'\$lastName',$lastName)" />
</body>
</html>
</xsl:template>
May be this code can help you .
i am replacing 
 with br tag to show new line while transforming xml using xsl. i want to replace blank spaces to its according code that may   or something else at the same time.sample code is below. please suggest for me what should i do.
while xml file may be as ------------
<?xml version="1.0" encoding="iso-8859-1"?><?xml-stylesheet type="text/xsl"
href="task.xsl"?><Nodes><sNode><Word><![CDATA[1
2.............3............4............5
3]]></Word></sNode></Nodes>
since blank spaces ommitted automatically so here ........ represents blank spaces.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<table>
<xsl:for-each select="Nodes/sNode">
<tr>
<td>
<xsl:call-template name="replace-string-with-element">
<xsl:with-param name="text" select="Word"/>
<xsl:with-param name="replace" select="'
'"/>
<xsl:with-param name="with" select="'br'"/>
</xsl:call-template>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template name="replace-string-with-element">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="with"/>
<xsl:choose>
<xsl:when test="contains($text,$replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:element name="{$with}"/>
<xsl:call-template name="replace-string-with-element">
<xsl:with-param name="text" select="substring-after($text,$replace)"/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="with" select="$with"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
You could use xsl:character-map as follows:
<?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" exclude-result-prefixes="fn xs">
<xsl:character-map name="replaceChars">
<xsl:output-character character="
" string="br"/>
</xsl:character-map>
<xsl:output method="xml" version="1.0" encoding="UTF-8" use-character-maps="replaceChars" indent="yes"/>
<!-- Implement your templates -->
</xsl:stylesheet>
It is even possible to save all characters in a xsl:character-map in an external XSLT and use <xsl:import href="characterFile.xslt" />
To implement this in your stylesheet use the following XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:character-map name="replaceChars">
<xsl:output-character character="
" string="br"/>
</xsl:character-map>
<xsl:output method="html" use-character-maps="replaceChars"/>
<xsl:template match="/">
<html>
<body>
<table>
<xsl:for-each select="Nodes/sNode">
<tr>
<td>
<xsl:value-of select="Word" />
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
I tried for following code but its not working. I have taken one string and write XSLT in it and load it XslCompiledTransform object.
<xsl:sequence select=
"sum(//Item/(cost * related_id/Item/quantity))"/>
Source XML:
<AML>
<Item>
<cost>
40
</cost>
<related_id>
<Item>
<quantity>2</quantity>
</Item>
</related_id>
</Item>
<Item>
<cost>
50
</cost>
<related_id>
<Item>
<quantity>10</quantity>
</Item>
</related_id>
</Item>
</AML>
As I said in the comments, xsl:sequence and that XPath syntax you're trying to use aren't available in XSLT 1.0 (which XslCompiledTransform uses), but you can achieve a sum of formulas by using a recursive template:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes"/>
<xsl:template match="/">
<xsl:call-template name="SumSet">
<xsl:with-param name="items" select="/*/Item" />
</xsl:call-template>
</xsl:template>
<xsl:template name="SumSet">
<xsl:param name="items" />
<xsl:if test="$items">
<xsl:variable name="currentValue">
<xsl:apply-templates select="$items[1]" />
</xsl:variable>
<xsl:variable name="remainderSum">
<xsl:call-template name="SumSet">
<xsl:with-param name="items" select="$items[position() > 1]" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$currentValue + $remainderSum"/>
</xsl:if>
<xsl:if test="not($items)">0</xsl:if>
</xsl:template>
<xsl:template match="Item">
<xsl:value-of select="cost * related_id/Item/quantity"/>
</xsl:template>
</xsl:stylesheet>
When this is run on your input XML, the result is:
580
That would be the generic approach, but since you've mentioned that you're using XslCompiledTransform, you can use msxsl:node-set() to simplify this task a bit:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="text" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="values">
<xsl:apply-templates select="/*/Item" />
</xsl:variable>
<xsl:value-of select="sum(msxsl:node-set($values)/*)"/>
</xsl:template>
<xsl:template match="Item">
<itemValue>
<xsl:value-of select="cost * related_id/Item/quantity"/>
</itemValue>
</xsl:template>
</xsl:stylesheet>
This also produces the value 580 when run on your input XML.
I have an ecommerce cart which generates the XML at runtime. I have access to the XSL stylesheet which styles the actual XML.
I would like to display product category images in the menu. Is there an example of running a query within an xsl file that you could show me?
I cant see where its pulling the category data from?
can you help?
<?xml version="1.0" standalone="yes" ?>
<!-- ###################################################################################################### -->
<!-- Copyright AspDotNetStorefront.com, 1995-2009. All Rights Reserved. -->
<!-- http://www.aspdotnetstorefront.com -->
<!-- For details on this license please visit the product homepage at the URL above. -->
<!-- THE ABOVE NOTICE MUST REMAIN INTACT. -->
<!-- ###################################################################################################### -->
<package version="2.1" displayname="Categories" debug="false" includeentityhelper="true">
<PackageTransform>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aspdnsf="urn:aspdnsf" exclude-result-prefixes="aspdnsf">
<xsl:output method="html" omit-xml-declaration="yes"/>
<xsl:param name="CategoryID">
<xsl:choose>
<xsl:when test="/root/System/PageName = 'showmanufacturer.aspx' or /root/System/PageName = 'showsection.aspx' or /root/System/PageName = 'showdistributor.aspx' or /root/System/PageName = 'showvector.aspx' or /root/System/PageName = 'showgenre.aspx'">0</xsl:when>
<xsl:when test="/root/System/PageName = 'showcategory.aspx' and boolean(/root/QueryString/categoryid)">
<xsl:value-of select="/root/QueryString/categoryid"/>
</xsl:when>
<xsl:when test="(/root/System/PageName = 'showcategory.aspx' or /root/System/PageName = 'showproduct.aspx') and boolean(/root/Cookies/LastViewedEntityInstanceID) and /root/Cookies/LastViewedEntityName = 'Category'">
<xsl:value-of select="/root/Cookies/LastViewedEntityInstanceID"/>
</xsl:when>
<xsl:otherwise>
0
</xsl:otherwise>
</xsl:choose>
</xsl:param>
<xsl:param name="AncestorID">
<xsl:for-each select="/root/EntityHelpers/Category//Entity[EntityID = $CategoryID]">
<xsl:value-of select="ancestor::*/EntityID"/>
</xsl:for-each>
</xsl:param>
<xsl:param name="ParentID">
<xsl:for-each select="/root/EntityHelpers/Category//Entity[EntityID = $CategoryID]">
<xsl:value-of select="parent::*/EntityID"/>
</xsl:for-each>
</xsl:param>
<xsl:template match="/">
<xsl:element name="ul">
<xsl:attribute name="class">
<![CDATA[menuul]]>
</xsl:attribute>
<xsl:apply-templates select="/root/EntityHelpers/Category/Entity">
<xsl:with-param name="prefix" select="''"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template match="Entity">
<xsl:param name="prefix"></xsl:param>
<xsl:param name="eName" select="aspdnsf:GetMLValue(Name)" />
<xsl:choose>
<xsl:when test="Published=1">
<li class="menuli">
<xsl:value-of select="$prefix" />
<!--<xsl:if test="number(ParentEntityID) != 0">
<span class="catMark">>></span>�
</xsl:if>-->
<a href="{concat('c-',EntityID,'-',SEName,'.aspx')}">
<xsl:if test="EntityID = $CategoryID or descendant::Entity/EntityID = $CategoryID">
<xsl:attribute name="class">MenuSelected</xsl:attribute>
</xsl:if>
<xsl:value-of select="$eName" disable-output-escaping="yes"/>
</a>
<xsl:if test="count(child::Entity)>0">
<ul class="submenuul">
<xsl:apply-templates select="Entity">
<xsl:with-param name="prefix" select="concat($prefix, '��')"/>
</xsl:apply-templates>
</ul>
</xsl:if>
</li>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
</PackageTransform>
</package>
If you are using ASP.NET you can use standard ASP.NET XML Control.
On page load assign to it your xml and xsl files like below:
protected void Page_Load(object sender, EventArgs e)
{
Xml1.DocumentSource = "~/App_Data/YourXmlFile.xml";
Xml1.TransformSource = "~/App_Data/YourXslStyleSheetFile.xsl";
}
You can also use DocumentContent property your xml is stored not in files but, i.e. in database.
See example here
The accepted answer is true to ASP.Net, but not to Aspdotnetstorefront. The XML file referenced in the question is only generated if debug is enabled and it should not be generated on any production environments (you will end up with write conflicts).
The XML behind the XSLT is generated for you at runtime. The category data (and other entity data) is included because you have includeentityhelper="true". EntityHelper caches category (and other entity) data to reduce DB queries. If you need data in addition to this, you should use the XMLPackage <query> node documented here (under 'SQL Queries'):
http://manual.aspdotnetstorefront.com/p-157-xml-packages.aspx
You'll also find the '' node used in most of the XMLPackages included in the default build.
I'm trying to apply an xlst transformation using the following file. This is very basic, but I wanted to build off of this when I get it working correctly.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="/">
<div>
<h2>Station Inventory</h2>
<hr/>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="StationInventory">
<h5><xsl:value-of select="station-name" /></h5>
<xsl:apply-templates select="detector"/>
</xsl:template>
<xsl:template match="detector">
<span>
<xsl:value-of select="detector-name" />
</span>
<br/>
</xsl:template>
</xsl:stylesheet>
Here is some xml I'm using for the source.
<StationInventoryList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.dummy-tmdd-address">
<StationInventory>
<station-id>9940</station-id>
<station-name>Zone 9940-SEB</station-name>
<station-travel-direction>SEB</station-travel-direction>
<detector-list>
<detector>
<detector-id>2910</detector-id>
<detector-name>1999 West Smith Exit SEB</detector-name>
</detector>
<detector>
<detector-id>9205</detector-id>
<detector-name>CR-155 Exit SEB</detector-name>
</detector>
<detector>
<detector-id>9710</detector-id>
<detector-name>Pt of View SEB</detector-name>
</detector>
</detector-list>
</StationInventory>
</StationInventoryList>
Any ideas what I'm doing wrong? The simple intent here is to make a list of station, then make a list of detectors at a station. This is a small piece of the XML. It would have multiple StationInventory elements.
I'm using the data as the source for an asp:xml control and the xslt file as the transformsource.
var service = new InternalService();
var result = service.StationInventory();
invXml.DocumentContent = result;
invXml.TransformSource = "StationInventory.xslt";
invXml.DataBind();
Any tips are of course appreciated. Have a terrific weekend.
Cheers,
~ck
Replace by
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" xmlns:st="http://www.dummy-tmdd-address">
<xsl:template match="/">
<div>
<h2>Station Inventory</h2>
<hr/>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="st:StationInventory">
<h5><xsl:value-of select="st:station-name" /></h5>
<ul>
<xsl:apply-templates select="st:detector-list/st:detector"/>
</ul>
</xsl:template>
<xsl:template match="st:detector">
<li>
<xsl:value-of select="st:detector-name" />
</li>
</xsl:template>
</xsl:stylesheet>
because detector is child of detector-list not station inventory and there is a namespace
There are two obvious problems:
All elements in the XML document are in the default namespace, but in the XSLT code they are referenced as belonging to "no namespace".
The element <StationInventory> doesn't have any <detector> children.
Solution:
In the XSLT stylesheet below the above two problems are corrected:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="http://www.dummy-tmdd-address">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<div>
<h2>Station Inventory</h2>
<hr/>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="d:StationInventory">
<h5><xsl:value-of select="d:station-name" /></h5>
<xsl:apply-templates select="d:detector-list/d:detector"/>
</xsl:template>
<xsl:template match="d:detector">
<span>
<xsl:value-of select="d:detector-name" />
</span>
<br/>
</xsl:template>
</xsl:stylesheet>
The result now is a complete output, that most probably was wanted:
<div xmlns:d="http://www.dummy-tmdd-address">
<h2>Station Inventory</h2>
<hr />
<h5>Zone 9940-SEB</h5>
<span>1999 West Smith Exit SEB</span>
<br />
<span>CR-155 Exit SEB</span>
<br />
<span>Pt of View SEB</span>
<br />
</div>