I am trying to reorder elements in a XML using XSLT 1.0. Below is small snippet of the XML
<RIMSDB1 xmlns="http://kiris.nps21.org/xsd">
<ROW>
<ReportID>1</ReportID>
<WKYMD>20160610</WKYMD>
<RunSystemDate>20160610032048</RunSystemDate>
</ROW>
<ROW>
<ReportID>2</ReportID>
<WKYMD>27869</WKYMD>
<RunSystemDate>495876043985778649</RunSystemDate>
</ROW>
This is the XSLT I am using to transform it.
<xsl:template match="*/ROW">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:apply-templates select="WKYMD" />
<xsl:apply-templates select="RunSystemDate" />
<xsl:apply-templates select="ReportID" />
</xsl:copy>
Issue is when I transform I get no change in order but I remove xmlns="http://kiris.nps21.org/xsd" from
<RIMSDB1 xmlns="http://kiris.nps21.org/xsd">
I get correct transformation, which is:
<RIMSDB1>
<ROW>
<WKYMD>20160610</WKYMD>
<RunSystemDate>20160610032048</RunSystemDate>
<ReportID>1</ReportID>
</ROW>
<ROW>
<WKYMD>27869</WKYMD>
<RunSystemDate>495876043985778649</RunSystemDate>
<ReportID>2</ReportID>
</ROW>
Can anyone shed some light on what is happening of if there is a better way to reorder.
Thanks in advance.
When your source document contains xmlns="http://kiris.nps21.org/xsd" then all of your elements (without a namespace prefix, or overriding namespace declaration) will inherit that namespace and are bound to the namespace http://kiris.nps21.org/xsd. It is sometimes difficult to notice or understand when there is no namespace-prefix.
You should adjust your XSLT to declare that namespace with a prefix and then adjust the select and match expressions to use the namespace prefix in order to properly address those elements.
For example:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:k="http://kiris.nps21.org/xsd"
version="1.0">
<xsl:template match="*/k:ROW">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:apply-templates select="k:WKYMD" />
<xsl:apply-templates select="k:RunSystemDate" />
<xsl:apply-templates select="k:ReportID" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Related
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'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 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 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.
I've got the following XML structure :
<Article>
<id>1</id>
<line>L11</line>
<line>L12</line>
<line>L13</line>
</Article>
<Article>
<id>2</id>
<line>L21</line>
<line>L22</line>
<line>L23</line>
</Article>
I want to use XSLT to iterate over all the lines of only one article at a time so that I can achieve the following structure : (every article is converted into an order, and its lines are converted to line structures in the new structure)
<orders>
<order>
<id>1</id>
<order_line>L11</order_line>
<order_line>L12</order_line>
<order_line>L13</order_line>
</order>
<order>
<id>2</id>
<order_line>L21</order_line>
<order_line>L22</order_line>
<order_line>L23</order_line>
</order>
</orders>
With XSLT, try to think of the task at hand as a set of patterns or rules you have to match, and the actions to be taken each time such a pattern is encountered. You can usually let the runtime worry about looping, etc, to discover patterns.
In your specific case, you've described two patterns you want special logic. Every time there is the element Article, you wish to apply the rule to change its name to order.
Every time the element line is encountered as a child of Article, replace it with order_line. For any other pattern, you just want to copy the contents as they were in the original document:
<!-- Match element Article, and whenever it's encountered, insert an 'order' element, and copy the contents of Article -->
<xsl:template match="Article">
<order> <xsl:apply-templates/> </order>
</xsl:template>
<!-- Match element 'line', and whenever it's encountered, insert an 'order_line' element, and copy the contents -->
<xsl:template match="Article/line">
<order_line> <xsl:apply-templates/> </order_line>
</xsl:template>
<!-- Match any other element we haven't specified explicity, and copy it verbatim -->
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
This is a straightforward transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<orders>
<xsl:apply-templates/>
</orders>
</xsl:template>
<xsl:template match="Article">
<order>
<xsl:apply-templates/>
</order>
</xsl:template>
<xsl:template match="line">
<order_line>
<xsl:apply-templates/>
</order_line>
</xsl:template>
<xsl:template match="id">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
EDIT: A general root element rule.
This complete XSLT 1.0 transformation:
<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:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<orders>
<xsl:apply-templates/>
</orders>
</xsl:template>
<xsl:template match="Article">
<order>
<xsl:apply-templates/>
</order>
</xsl:template>
<xsl:template match="line">
<order_line>
<xsl:apply-templates/>
</order_line>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document (wrapped into a single top element to make it well-formed):
<t>
<Article>
<id>1</id>
<line>L11</line>
<line>L12</line>
<line>L13</line>
</Article>
<Article>
<id>2</id>
<line>L21</line>
<line>L22</line>
<line>L23</line>
</Article>
</t>
produces the wanted, correct result:
<orders>
<order>
<id>1</id>
<order_line>L11</order_line>
<order_line>L12</order_line>
<order_line>L13</order_line>
</order>
<order>
<id>2</id>
<order_line>L21</order_line>
<order_line>L22</order_line>
<order_line>L23</order_line>
</order>
</orders>
Explanation:
The identity rule copies every node "as-is".
The identity rule is overriden by three templates, each of which matches a particular kind of element (the top element, Article and line) and simply renames the element, respectively to: orders, order and order_line.
This is just of the top of my head but I think you can do it this way:
<orders>
<xsl:for-each select="//Article">
<order>
<id><xsl:value-of select="./id"/></id>
<xsl:for-each select="./line">
<order_line><xsl:value-of select="child::text()" /></order_line>
</xsl:for-each>
</order>
</xsl:for-each>
</orders>
Anyway, it should be enough to get you started. There is only a for-each loop in XSLT though, no for loop with a counter.
Your source XML file is not well-formed. For clarity, my response assumes you've wrapped it in a element. This is a pretty trivial XML transformation so I've included some comments explaining what each line of code is doing.
<!-- change the template match xpath to whatever the root xpath is in your document -->
<xsl:template match="root">
<orders>
<!-- loop over each Article -->
<xsl:for-each select="Article">
<order>
<!-- pass id through -->
<xsl:copy-of select="id"/>
<!-- loop over each line -->
<xsl:for-each select="line">
<!-- create an <order_line> node -->
<order_line>
<!-- get the value of the current <line> -->
<xsl:value-of select="."/>
</order_line>
</xsl:for-each>
</order>
</xsl:for-each>
</orders>
</xsl:template>
Try this, but I am not a xslt expert :)
<?xml version="1.0" encoding="utf-8"?>
<xsl:template match="/" name="A">
<orders>
<xsl:for-each select="Articles/Article">
<order>
<id>
<xsl:value-of select="id" />
</id>
<xsl:for-each select="line">
<order_line>
<xsl:value-of select="node()"></xsl:value-of>
</order_line>
</xsl:for-each>
</order>
</xsl:for-each>
</orders>
</xsl:template>
The correct way to do "looping" in XSLT is to let the XSLT processor walk the document tree for you. Anything else is fighting against the nature of XSLT.
Here's a (near-) complete solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<orders><xsl:apply-templates/></orders>
</xsl:template>
<xsl:template match="Article">
<order>
<id><xsl:value-of select="id"/></id>
<xsl:apply-templates select="line"/>
</order>
</xsl:template>
<xsl:template match="Article/line">
<order_line><xsl:value-of select="."/></order_line>
</xsl:template>
</xsl:stylesheet>