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 .
Related
Here my Xml
<document>
<metadata>
<title>Sign in to my account</title>
</metadata>
<topic>
<conceptuldocument>
<legacybold> Hi hello world </legacybold>
</conceptuldocument>
</topic>
</document>
I only need the title which is under the metadata data tag
all I would need is <h1> sign in to my account</h1>
but i am getting out put html as
<h1>sign in to my account</h1>Hi hello world
here my xslt :
<?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="html" indent="yes"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="metadata">
<xsl:apply-templates select="metadata"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="metadata">
<h1>
<xsl:value-of select="title"/>
</h1>
</xsl:template>
</xsl:stylesheet>
C# :
using (StringWriter sw = new StringWriter())
{
transform.Transform(xmldocument.DocumentElement,null, sw);
string html = sw.ToString();
}
output image
xml image
Xslt file image
wondering if any one can help me point what am i missing here , very new to xslt its just been a day.
Simplest fix is to change your entry template to
<xsl:template match="/">
<xsl:apply-templates select="document/metadata"/>
</xsl:template>
Note that your xsl:choose around the xsl:apply-templates is a waste of space. If the element doesn't exist, xsl:apply-templates will do nothing.
Trying and trying to get the following to work. I have an XML file (serialized C# class) similar to
<?xml version="1.0" encoding="utf-8"?>
<MyXml xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Order>
<Header>
<OrderNumber>1234</OrderNumber>
</Header>
<Line>
<Sku>abc</Sku>
<Qty>300</Qty>
</Line>
<Line>
<Sku>xyz</Sku>
<Qty>19</Qty>
</Line>
</Order>
I need to transform this to:
<?xml version="1.0" encoding="utf-8"?>
<Order Number="1234">
<Line>
<Product>abc</Product>
<Quantity>300</Quantity>
</Line>
<Line>
<Product>xyz</Product>
<Quantity>19</Quantity>
</Line>
</Order>
Making the lines children of the Order element.
Here's my most succesful attempt so far.
<?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="Header" >
<Order>
<xsl:attribute name="Number">
<xsl:value-of select="OrderNumber"/>
</xsl:attribute>
<xsl:apply-templates select="Line"/>
</Order>
</xsl:template>
<xsl:template match="Line">
<Line>
<Product>
<xsl:value-of select="Sku" />
</Product>
<Quantity>
<xsl:value-of select="Qty" />
</Quantity>
</Line>
</xsl:template>
</xsl:stylesheet>
Which produces the incorrect xml below with multiple root nodes.
<?xml version="1.0" encoding="utf-8"?>
<Order OrderNumber="1234" />
<Line><Product>abc</Product><Quantity>300</Quantity></Line>
<Line><Product>xyz</Product><Quantity>19</Quantity></Line>
There's clearly something I'm missing, but after hours of trying I cannot get the output Line elements to be children of Order at all. Can someone point me in the right direction? Also why does the indent option only seem to affect the first level of the output xml?
You have a template matching Header, but within this you do <xsl:apply-templates select="Line"/> to get the Line elements, but Line is not a child of Header, so this selects nothing.
The Line elements in your output are actually being selected due to XSLT's built-in template rules. You don't have a template matching Order, so XSLT's built-in template is used, which will select both Header and Line elements under the Order.
One solution is is to change your the template matching Header to match Order instead.
Try this XSLT
<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="Order" >
<Order Number="{Header/OrderNumber}">
<xsl:apply-templates select="Line"/>
</Order>
</xsl:template>
<xsl:template match="Line">
<Line>
<Product>
<xsl:value-of select="Sku" />
</Product>
<Quantity>
<xsl:value-of select="Qty" />
</Quantity>
</Line>
</xsl:template>
</xsl:stylesheet>
Note the use of Attribute Value Templates to simplify the creation of the Number attribute on Order.
Another solution is using xsl:apply-templates on ../Line instead of Line with a mode attribute on the template. So the changes to the template are minimal.
<?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="Header" >
<Order>
<xsl:attribute name="Number">
<xsl:value-of select="OrderNumber"/>
</xsl:attribute>
<xsl:apply-templates select="../Line" mode="sub"/> <!-- set 'mode' to 'sub' and add '../' to XPath-->
</Order>
</xsl:template>
<xsl:template match="Line" mode="sub"> <!-- use template only when 'mode' is set to 'sub' -->
<Line>
<Product>
<xsl:value-of select="Sku" />
</Product>
<Quantity>
<xsl:value-of select="Qty" />
</Quantity>
</Line>
</xsl:template>
<xsl:template match="text()" /> <!-- ignore all unmatched text() nodes -->
</xsl:stylesheet>
I am editing an xslt in C#.
It has a template "Get" defined in it.
I want to call this template and pass it into a variable.
Template:
<xsl:template name="Get">
<xsl:param name="varMonth" />
<xsl:choose>
<xsl:when test="$varMonth='JAN'">
<xsl:value-of select="'A'" />
</xsl:when>
</xsl:choose>
</xsl:template>
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<DocumentElement>
<PositionMaster>
<xsl:variable name="varName">
<xsl:value-of select="''" />
</xsl:variable>
</PositionMaster>
</DocumentElement>
</xsl:template>
</xsl:stylesheet>
Code:
I am getting a string as an input to the param of the template
string input = "A";
XmlDocument xslDoc = new XmlDocument();
xslDoc.Load("a.xslt");
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xslDoc.NameTable);
nsMgr.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
XmlElement valueOf = (XmlElement)xslDoc.SelectSingleNode("/xsl:stylesheet/xsl:template[#match = '/']/DocumentElement/PositionMaster/xsl:variable[#name = "varName"]/xsl:value-of", nsMgr);
if (valueOf != null)
{
// What should i write here to get the below modified XSLT
}
Required XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<DocumentElement>
<PositionMaster>
<xsl:variable name="varName">
<xsl:call-template name="Get">
<xsl:with-param name="Month" select="input"/>
</xsl:call-template>
</xsl:variable>
</PositionMaster>
</DocumentElement>
</xsl:template>
</xsl:stylesheet>
Am I right in understanding that you want to transform the stylesheet using C# code? That seems crazy; you are using XSLT, so you have an XML transformation language at your disposal: use it!
However, I'm confused because neither your original stylesheet nor your modified stylesheet is valid XSLT. You can't have a <DocumentElement> element as a child of xsl:stylesheet; it surely needs to be inside a template.
Transforming XSLT stylesheets using XSLT is common practice and it can be a smart approach to some problems. However, it is often done when there are better techniques available, for example adding stylesheet parameters (global xsl:param elements).
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'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>