How can I call javascript/c# function in xslt passing xpath selection value.
Here is how I call function with manualy typed parameter:
<xsl:value-of select="cs:my('some text')"/>
Here is an example from the MSXML 4 SDK (this should be the same for MSXML 6, and is quite similar for .NEt's XslCompiledTransform -- for the latter search MSDN for <msxsl:script>)
Example This example defines a script block with a namespace
prefix of user that contains a function called xml that takes a
node-list as an argument. Later, this function, xml(nodelist) in the
user namespace, is called from the select attribute of .
XML File (customers.xml)
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="script.xsl" ?>
<customers>
<customer>
<name>John Smith</name>
<address>123 Elm St.</address>
<phone>(123) 456-7890</phone>
</customer>
<customer>
<name>Mary Jones</name>
<address>456 Oak Ave.</address>
<phone>(156) 789-0123</phone>
</customer>
</customers>
XSLT File (script.xsl)
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://mycompany.com/mynamespace">
<msxsl:script language="JScript" implements-prefix="user">
function xml(nodelist) {
return nodelist.nextNode().xml;
}
</msxsl:script>
<xsl:template match="/">
<xsl:value-of select="user:xml(.)"/>
</xsl:template>
</xsl:stylesheet>
Formatted Output
<?xml version="1.0" encoding="UTF-16"?><?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="script.xsl" ?>
<customers>
<customer>
<name>John Smith</name>
<address>123 Elm St.</address>
<phone>(123) 456-7890</phone>
</customer>
<customer>
<name>Mary Jones</name>
<address>456 Oak Ave.</address>
<phone>(156) 789-0123</phone>
</customer>
</customers>
Related
I am trying to get the output based on two separate nodes in XML using key concept of XSLT
I have below XML
<?xml version="1.0" encoding="UTF-8"?>
<mdti:Input xmlns:mdti="urn:com.workday/multiDocumentTransform/Input">
<mdti:Files xmlns:mdti="urn:com.workday/multiDocumentTransform/Input">
<mdti:EventFiles>
<mdti:File mdti:filename="first.xml" mdti:contentType="text/xml">
<wd:Report_Data xmlns:wd="urn:com.workday/bsvc">
<wd:Report_Entry>
<wd:key>1234</wd:key>
<wd:comp>ABC</wd:comp>
<wd:asof>2021-03-24T04:59:32.179-07:00</wd:asof>
<wd:emplid>33333333</wd:emplid>
<wd:worker_type>EMP</wd:worker_type>
<wd:emp_type>Regular</wd:emp_type>
<wd:orig_hire_dt>2021-11-27</wd:orig_hire_dt>
<wd:rehire_dt>2019-04-01</wd:rehire_dt>
<wd:home_host_class>M</wd:home_host_class>
<wd:service_dt>2014-11-27</wd:service_dt>
</wd:Report_Entry>
</wd:Report_Data>
</mdti:File>
<mdti:File mdti:filename="second.xml" mdti:contentType="text/xml">
<wd:Report_Data xmlns:wd="urn:com.workday/bsvc">
<wd:Report_Entry>
<wd:key>1234</wd:key>
<wd:supervisor_lname>xyz</wd:supervisor_lname>
<wd:hr_status>A</wd:hr_status>
<wd:hr_status_descr>Active</wd:hr_status_descr>
<wd:empl_status>A</wd:empl_status>
<wd:empl_status_descr>Active</wd:empl_status_descr>
<wd:ben_status>A</wd:ben_status>
<wd:home_address_change_dt>2019-07-30</wd:home_address_change_dt>
<wd:location>444</wd:location>
<wd:location_descr>Ind</wd:location_descr>
</wd:Report_Entry>
</wd:Report_Data>
</mdti:File>
</mdti:EventFiles>
</mdti:Files>
</mdti:Input>
**I am using below XSLT for my data. Please let me know if I am missing something and way to do it. The only identifier in each node is the mdti:filename **
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mdti="urn:com.workday/multiDocumentTransform/Input"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wd="urn:com.workday/bsvc"
exclude-result-prefixes="#all">
<xsl:key name="share" match="mdti:Input/mdti:Files/mdti:EventFiles/mdti:File/wd:Report_Data/wd:Report_Entry" use="mdti:Input/mdti:Files/mdti:EventFiles/mdti:File/wd:Report_Data/wd:Report_Entry/wd:key"/>
<xsl:template match="/">
<data>
<key>1234</key>
<xsl:copy-of select="key('share', wd:key)/wd:hr_status"/>
<emp_type>Regular</emp_type>
<supervisor_lname>xyz</supervisor_lname>
<hr_status>A</hr_status>
<location>444</location>
</data>
</xsl:template>
</xsl:stylesheet>
You have several mistakes.
First, <xsl:template match="/"> puts you in the context of the root node; from this context, the expression wd:key selects nothing - so your instruction <xsl:copy-of select="key('share', wd:key)/wd:hr_status"/> does nothing.
Next, if you want your key to match nodes in the second file, you should restrict it to match only nodes in the second file. Also, the use attribute must be relative to the matched node.
Furthermore, if you want the result to be in no-namespace, you cannot copy nodes from the input - at least not in XSLT 1.0.
There is more, but these are the ones that stand out immediately.
Consider the following example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mdti="urn:com.workday/multiDocumentTransform/Input"
xmlns:wd="urn:com.workday/bsvc"
exclude-result-prefixes="mdti wd">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="entry2" match="mdti:File[#mdti:filename='second.xml']/wd:Report_Data/wd:Report_Entry" use="wd:key"/>
<xsl:template match="/mdti:Input">
<root>
<xsl:for-each select="mdti:Files/mdti:EventFiles/mdti:File[#mdti:filename='first.xml']/wd:Report_Data/wd:Report_Entry">
<data>
<!-- data from file1 -->
<key>
<xsl:value-of select="wd:key"/>
</key>
<emplid>
<xsl:value-of select="wd:emplid"/>
</emplid>
<!-- data from file2 -->
<xsl:variable name="entry2" select="key('entry2', wd:key)" />
<supervisor_lname>
<xsl:value-of select="$entry2/wd:supervisor_lname"/>
</supervisor_lname>
<hr_status>
<xsl:value-of select="$entry2/wd:hr_status"/>
</hr_status>
</data>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, this will produce:
Result
<?xml version="1.0" encoding="utf-8"?>
<root>
<data>
<key>1234</key>
<emplid>33333333</emplid>
<supervisor_lname>xyz</supervisor_lname>
<hr_status>A</hr_status>
</data>
</root>
You can add fields from both branches as required.
I have a sample source xml like:
<?xml version="1.0" encoding="utf-16"?>
<shiporder orderid="orderid1">
<item>
item1
</item>
<item>
item2
</item>
</shiporder>
The assumptions for the transformation of my source xml is that I have a list of xPaths in this case /shiporder[1]/item[1] and /shiporder[1]/item[2] and the information that I have to transform the content of those nodes into Customer[1]/adress[1] and Customer[1]/adress[2]. In detail the result should look like:
<?xml version="1.0" encoding="utf-16"?>
<customer>
<adress>
item1
</adress>
<adress>
item2
</adress>
</customer>
Now I'm looking for a template which applies for each path in my source list ["/shiporder[1]/item"] and transform the inner text of that nodes into nodes with respect to ["Customer[1]/adress"].
First of all I thought to create templates like:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:func="http://www.functx.com">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/shiporder[1]">
<Customer>
<xsl:apply-templates select="item"/>
</Customer>
</xsl:template>
<xsl:template match="/shiporder[1]/item">
<adress>
<xsl:value-of select="text()"/>
</adress>
</xsl:template>
</xsl:stylesheet>
But later I was thinking about a function which combines the implicit call of the templates in a recursive way. I hope it is clear from here on other wise I could add some pseudo code what kind of function I have in mind.
Trying to build a POC that does the following:
Given a short input XML, take the values from it and insert them into a larger XML file of a known format.
So if this was my input XML:
<root>
<transaction ID="TX123" source-system="xyz" timestamp="2015-10-15T14:20:35.954Z" dest-system="abc" status="success" applicationID="some_app" originator="MQ">
</transaction>
</root>
And i'd have to take these values and insert them into this: MQ FTE Transfer Log message format
I'd have to insert the values in the transferSet node (timestamp) and metaDataSet.
What would the XSLT have to look like in order to get this done?
Thanks a bunch in advance!
Slava.
Your XLS should look like this:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<transaction>
<!-- more elements... -->
<xsl:apply-templates select="transaction"/>
</transaction>
</xsl:template>
<xsl:template match="transaction">
<transferSet>
<xsl:attribute name="startTime"><xsl:value-of select="#timestamp" /></xsl:attribute>
<metaDataSet>
<metaData key="com.ibm.wmqfte.SourceAgent"><xsl:value-of select="#source-system"/></metaData>
<!-- more elements... -->
</metaDataSet>
</transferSet>
</xsl:template>
With your source input, it should generate an XML like this:
<?xml version="1.0" encoding="UTF-8"?>
<transaction>
<transferSet startTime="2015-10-15T14:20:35.954Z">
<metaDataSet>
<metaData key="com.ibm.wmqfte.SourceAgent">xyz</metaData>
</metaDataSet>
</transferSet>
</transaction>
I working with a project in BizTalk where use xslt to convert from and edifact file to an UBL file.
The edifact file contains price values of ###.0 and that do not work. I want to change it to ###.00 using format-number. But I cannot make it work.
This is what I have made so far:
<cbc:Value>
<xsl:variable name="SumOfNodes" select="edi:PRI/edi:C509/C50902"/>
<xsl:value-of select="format-number($SumOfNodes, '0.00')"/>
</cbc:Value>
I am using this stylesheet:
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:edi="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006/MEDIAMARKT"
xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Order-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
exclude-result-prefixes="msxsl edi">
Any ideas on how to solve this??
This may be due to undeclared namespace in your XSLT. Please check whether namespaces are declared correctly in your XSLT. Unless you show the full XSLT coding with XML coding, we cannot able to provide solution. However please refer the below Sample XML and XSLT with the output
XSLT:
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="chapter">
<Value>
<xsl:variable name="num" select="number"/>
<xsl:value-of select="format-number($num,'00.00')"/>
</Value>
</xsl:template>
</xsl:stylesheet>
Sample XML
<?xml version="1.0"?>
<chapter>
<number>45</number>
</chapter>
Output
<?xml version='1.0' ?>
<Value>45.00</Value>
I have the following XML:
<?xml version="1.0" encoding="utf-8"?>
<string>
<Table>
<Rows>
<Row Id="0">
<Column Name="INS_NAME" XPath="Ins.Name">Jane</Column>
<Column Name="INS_LASTNAME" XPath="Ins.LastName">Smith</Column>
</Row>
<Row Id="1">
<Column Name="INS_NAME" XPath="Ins.Name">Joe</Column>
<Column Name="INS_LASTNAME" XPath="Ins.LastName">Miller</Column>
</Row>
<Row Id="2">
<Column Name="INS_NAME" XPath="Ins.Name">George</Column>
<Column Name="INS_LASTNAME" XPath="Ins.LastName">Ramsey</Column>
</Row>
</Rows>
</Table>
</string>
and I would like to transform it to this XML using a single XSLT:
<?xml version="1.0" encoding="utf-8"?>
<Customers>
<Customer><Name>Jane</Name><LastName>Smith</LastName></Customer>
<Customer><Name>Joe</Name><LastName>Miller</LastName></Customer>
<Customer><Name>George</Name><LastName>Ramsey</LastName></Customer>
</Customers>
I can do it with two different XSLT's:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="/" disable-output-escaping="yes" />
</xsl:template>
</xsl:stylesheet>
and then:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Customers>
<xsl:for-each select="Table/Rows/Row">
<Customer>
<Name><xsl:value-of select="Column[#Name='INS_NAME']" /></Name>
<LastName><xsl:value-of select="Column[#Name='INS_LASTNAME']" /></LastName>
</Customer>
</xsl:for-each>
</Customers>
</xsl:template>
</xsl:stylesheet>
I have been reading about multi phase transformations but I can't seem to get it. I have tried saving the first XSLT in a variable but it seems disable-output-escaping="yes" does not work when saving to a variable.
Can anybody help?
Thank you.
New information (Edit)
I am now translating the string this way:
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="stringXml">
<?xml version="1.0" encoding="utf-8"?>
<xsl:value-of select="translate(translate(/,'>','>'),'<','<')" />
</xsl:variable>
...
How can I do a transformation on the resulting XML stored in stringXML?
Final Solution (Edit)
<msxml:script implements-prefix="myLib" language="C#">
<msxml:assembly name="System.Web"/>
<msxml:using namespace="System.Web"/>
<![CDATA[
public System.Xml.XPath.XPathNodeIterator convertText(string text)
{
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.LoadXml(text);
return doc.CreateNavigator().Select("/");
}
]]>
</msxml:script>
it seems disable-output-escaping="yes" does not work when saving to a
variable.
Your observation is correct.
DOE only affects the serialization of the (final) result of the transformation and isn't applied on intermediary trees.
Here is what the W3C XSLT 1.0 specification explicitly says:
"An XSLT processor will only be able to disable output escaping if it
controls how the result tree is output. This may not always be the
case. For example, the result tree may be used as the source tree for
another XSLT transformation instead of being output."
The same negative answer holds for trying to use a variable, whose value is a string, containing a textual representation of an XML document.
I had a similar situation where I needed to parse an escaped XML inside my actual XML. I will post up my solution to also help someone else. Please also note that I am also using Saxon-PE parser.
In my situation I have the original XML that contains an escaped XML in a child node. I needed to get the inner XML inside the RootNode of the escaped XML.
Source XML:
<?xml version="1.0" encoding="utf-8"?>
<MyTestXml>
<SomeXmlStuff>
<Text1>Hello</Text1>
<Text2>World</Text2>
</SomeXmlStuff>
<SomeEscapedXml><RootNode><FirstNode>Hello</FirstNode><SecondNode>World</SecondNode><ThirdNode>Again</ThirdNode></RootNode></SomeEscapedXml>
</MyTestXml>
When you unescaped the XML, it looks like this:
<RootNode>
<FirstNode>Hello</FirstNode>
<SecondNode>World</SecondNode>
<ThirdNode>Again</ThirdNode>
</RootNode>
With the following XSLT transformation is applied on the source XML:
<?xml version='1.0' encoding='utf-8' ?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://saxon.sf.net/"
exclude-result-prefixes="xsl saxon">
<xsl:template match="/">
<MyOutput>
<xsl:call-template name="GetRootNodeInnerXml">
<xsl:with-param name="escapedXml" select="MyTestXml/SomeEscapedXml" />
</xsl:call-template>
</MyOutput>
</xsl:template>
<xsl:template name="GetRootNodeInnerXml">
<xsl:param name="escapedXml" required="yes" />
<xsl:copy-of select="saxon:parse($escapedXml)/RootNode/node()"/>
<!-- You can also use this line below if you're not using saxon parser. Just make sure your parser supports XSL 3.0 -->
<!--
<xsl:copy-of select="fn:parse-xml($escapedXml)/RootNode/node()" xmlns:fn="http://www.w3.org/2005/xpath-functions"/>
-->
</xsl:template>
</xsl:stylesheet>
This gives you the following output:
<?xml version='1.0' ?>
<MyOutput>
<FirstNode>Hello</FirstNode>
<SecondNode>World</SecondNode>
<ThirdNode>Again</ThirdNode>
</MyOutput>