How to call c# function in xsl - c#

im trying to call a c# function in xsl. I have to map some values into a xml. There are 3 principal components
-xsl
<?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"
xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var"
xmlns:ns0="http://iti/serv/dataloader"
xmlns:HelpersNS0="http://ri/clus/mapperhelpers/v1.0/I/F/C/CustomComponents ">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/*[local-name()='InvokeDataLoader']">
<ns0:scriptToExecute>
<xsl:value-of select="HelpersNS0:GetDataLoaderPath()"/>
</ns0:scriptToExecute>
</xsl:template>
</xsl:stylesheet>
-xml where i get c# assembly
<?xml version="1.0" encoding="UTF-8" ?>
<ExtensionObjects>
<ExtensionObject
Namespace="http://ri/clus/mapperhelpers/v1.0/I/F/C/CustomComponents"
AssemblyName="G.T.I_Fatt.CustomComponents, Version=1.0.0.0,Culture=neutral, PublicKeyToken=6ecedb456a4a8c16"
ClassName="G.T.I_Fatt.CustomComponents.MapperHelpers" />
</ExtensionObjects>
-xml to transform
<?xml version="1.0" encoding="UTF-8" ?>
<InvokeDataLoader xmlns="http://iti/serv/dataloader">
<scriptToExecute/>
<inputFile/>
</InvokeDataLoader>
But when i try to transform i get this error: "namespace does not contain any functions"

I notice you have xmlns:HelpersNS0="http://ri/clus/mapperhelpers/v1.0/I/F/C/CustomComponents ts"> in the XSLT yet Namespace="http://ri/clus/mapperhelpers/v1.0/I/F/C/CustomComponents" (without the ts) in the other file. So the namespace does not match.

Related

How to get XslCompiledTransform to include xmlns in root element?

I'm trying to use XslCompiledTransform C# class to transform one xml file into another. However, the xmlns attribute is not being transferred.
My code:
XmlReader reader = XmlReader.Create("machine1.xml");
XmlWriter writer = XmlWriter.Create("machine2.xml");
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load("transform.xsl");
transform.Transform(reader, writer);
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns="http://schemas.datacontract.org/2004/07/CMachines" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<!-- Copy everything not subject to the exceptions below -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!-- Ignore the disabled element -->
<xsl:template match="Disabled" />
</xsl:stylesheet>
Input:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMachine xmlns="http://schemas.datacontract.org/2004/07/CMachines" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Machine>
<Name>DellM7600</Name>
<ID>1</ID>
<Type>Laptop</Type>
<Disabled>false</Disabled>
<SerialNum>47280420</SerialNum>
</Machine>
<Machine>
<Name>DellD600</Name>
<ID>2</ID>
<Type>Laptop</Type>
<Disabled>false</Disabled>
<SerialNum>53338123</SerialNum>
</Machine>
</ArrayOfMachine>
This is the actual Output:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMachine xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/CMachines" >
<Machine>
<Name>DellM7600</Name>
<ID>1</ID>
<Type>Laptop</Type>
<Disabled>false</Disabled>
<SerialNum>47280420</SerialNum>
</Machine>
<Machine>
<Name>DellD600</Name>
<ID>2</ID>
<Type>Laptop</Type>
<Disabled>false</Disabled>
<SerialNum>53338123</SerialNum>
</Machine>
</ArrayOfMachine>
This is the desired output:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMachine xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/CMachines" >
<Machine>
<Name>DellM7600</Name>
<ID>1</ID>
<Type>Laptop</Type>
<SerialNum>47280420</SerialNum>
</Machine>
<Machine>
<Name>DellD600</Name>
<ID>2</ID>
<Type>Laptop</Type>
<SerialNum>53338123</SerialNum>
</Machine>
</ArrayOfMachine>
You were previously try to use xpath-default-namespace in your XSLT, which is not supported in XSLT 1.0.
Instead, you will need to use namespace prefix, bound to the namespace specified in your XML, to match the Disabled element which is in that namespace.
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:cm="http://schemas.datacontract.org/2004/07/CMachines"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<!-- Copy everything not subject to the exceptions below -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!-- Ignore the disabled element -->
<xsl:template match="cm:Disabled" />
</xsl:stylesheet>
Note the namespace prefix used is arbitrary, as long as the namespace URI matches.

remove namespaces, attributes, Xsi from Soap response using code or XSLT

Remove namespaces, attributes, Xsi from Soap response using code or XSLT
Want to transform a soap response to a normal XML(without namespaces, atributes) using C# code (Serializer, XMLDoc, XDoc ) or XSLT.
here is the soap response.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:Magento"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:catalogProductInfoResponse>
<info xsi:type="ns1:catalogProductReturnEntity">
<product_id xsi:type="xsd:string">3459</product_id>
<sku xsi:type="xsd:string">HK-BP001</sku>
<categories SOAP-ENC:arrayType="xsd:string[0]" xsi:type="ns1:ArrayOfString"/>
<websites SOAP-ENC:arrayType="xsd:string[7]" xsi:type="ns1:ArrayOfString">
<item xsi:type="xsd:string">1</item>
</websites>
<created_at xsi:type="xsd:string">2016-04-19 01:45:35</created_at>
<has_options xsi:type="xsd:string">1</has_options>
<special_from_date xsi:type="xsd:string">2016-04-19 00:00:00</special_from_date>
<tier_price SOAP-ENC:arrayType="ns1:catalogProductTierPriceEntity[0]" xsi:type="ns1:catalogProductTierPriceEntityArray"/>
<custom_design xsi:type="xsd:string">ultimo/default</custom_design>
<enable_googlecheckout xsi:type="xsd:string">1</enable_googlecheckout>
</info>
</ns1:catalogProductInfoResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
i want transformed xml like :
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Body>
<catalogProductInfoResponse>
<info>
<product_id>3459</product_id>
<sku>HK-BP001</sku>
<categories/>
<websites>
<item>1</item>
</websites>
<created_at>2016-04-19 01:45:35</created_at>
<has_options>1</has_options>
<special_from_date>2016-04-19 00:00:00</special_from_date>
<tier_price/>
<custom_design>ultimo/default</custom_design>
<enable_googlecheckout>1</enable_googlecheckout>
</info>
</catalogProductInfoResponse>
</Body>
</Envelope>
You can use XSLT:
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>

How to transform XML using XSLT based on external parameter?

I have incoming XML message and each message have different schema. I want to transform that request into another schema using C#.Net as my XSLT processor. Here is simplified scenario of the situation I have.
Incoming request:
<?xml version="1.0" encoding="utf-8"?>
<Request xmlns="http://www.example.com/api">
<SourceId>SourceId1</SourceId>
<RequestId>RequestId1</RequestId>
<StatusEvent>
<TenderId>TenderId1</TenderId>
<EventCode>TENDER_STARTED</EventCode>
</StatusEvent>
</Request>
Translate to:
<?xml version="1.0" encoding="utf-8"?>
<TransactionStatus xmlns="http://www.example1.com/api">
<RequestId>RequestId1</RequestId>
<TransactionId>TenderId1</TransactionId>
<Event>TRANSACTION_STARTED</Event>
</TransactionStatus>
Incoming request:
<?xml version="1.0" encoding="utf-8"?>
<Response xmlns="http://www.example.com/api">
<SourceId>SourceId1</SourceId>
<RequestId>RequestId1</RequestId>
<TenderCreated>
<TenderId>TenderId1</TenderId>
</TenderCreated>
</Response>
Translate to:
<?xml version="1.0" encoding="utf-8"?>
<TransactionStarted xmlns="http://www.example1.com/api">
<RequestId>RequestId1</RequestId>
<TransactionId>TenderId1</TransactionId>
</TransactionStarted>
Here is the XSLT I'm currently using to achieve above result,
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://www.example.com/api"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="ns0 xs">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="text()"/>
<xsl:template match="ns0:StatusEvent[1]">
<TransactionStatus
xmlns="http://www.example1.com/api">
<RequestId>
<xsl:value-of select="//ns0:RequestId"/>
</RequestId>
<TransactionId>
<xsl:value-of select="ns0:TenderId"/>
</TransactionId>
<Event>
<xsl:value-of select="ns0:EventCode"/>
</Event>
</TransactionStatus>
</xsl:template>
<xsl:template match="ns0:TenderCreated[1]">
<TransactionStarted
xmlns="http://www.example1.com/api">
<RequestId>
<xsl:value-of select="//ns0:RequestId"/>
</RequestId>
<TransactionId>
<xsl:value-of select="ns0:TenderId"/>
</TransactionId>
</TransactionStarted>
</xsl:template>
</xsl:stylesheet>
So Here is the two questions I have,
For the current scenario I'm getting correct result but, is there any better way to achieve this?
For some of the incoming request, I want to select template based on external parameter, how do I achieve that?
Update: More clarification on second question,
e.g: In 2nd Incoming request I might have TenderUpdated instead of TenderCreated and for that I want to translate that into either TransactionUpdated or TransactionCanceled depends on external string parameter.
so If incoming request is like,
<?xml version="1.0" encoding="utf-8"?>
<Response xmlns="http://www.example.com/api">
<SourceId>SourceId1</SourceId>
<RequestId>RequestId1</RequestId>
<TenderUpdated>
<TenderId>TenderId1</TenderId>
</TenderUpdated>
</Response>
And parameter passed is Update, translate to
<?xml version="1.0" encoding="utf-8"?>
<TransactionUpdated xmlns="http://www.example1.com/api">
<RequestId>RequestId1</RequestId>
<TransactionId>TenderId1</TransactionId>
<Update/>
</TransactionUpdated>
And if parameter passed is Cancel , translate to
<?xml version="1.0" encoding="utf-8"?>
<TransactionCanceled xmlns="http://www.example1.com/api">
<RequestId>RequestId1</RequestId>
<TransactionId>TenderId1</TransactionId>
<Cancel/>
</TransactionCanceled>
This is simplified scenario, actual message have more xml tag and TransactionUpdated and TransactionCanceled differs much.
If you know all result elements should be in the namespace http://www.example1.com/api then you can put that on the xsl:stylesheet e.g.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.example1.com/api"
xmlns:ns0="http://www.example.com/api"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="ns0 xs">
As for the parameter, declare it as
<xsl:param name="transactionName" select="'Updated'"/>
and when you want to create an element using that parameter don't use a literal result element but xsl:element instead:
<xsl:element name="Transaction{$transactionName}">...</xsl:element>
Unfortunately in XSLT 1.0 the use of parameter or variables references inside of patterns is not allowed so to handle the condition you can only write a template matching on the element name and then you need to use xsl:choose/xsl:when to handle the different element names. Here is an example that you can hopefully extend:
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:api="http://www.example.com/api"
xmlns="http://www.example1.com/api"
exclude-result-prefixes="api">
<xsl:param name="transactionName" select="'Update'"/>
<xsl:output indent="yes"/>
<xsl:template match="api:Response">
<xsl:element name="Transaction{$transactionName}">
<xsl:apply-templates select="api:RequestId | api:TenderUpdated/api:TenderId"/>
<xsl:choose>
<xsl:when test="$transactionName = 'Update'">
<Update/>
</xsl:when>
<xsl:when test="$transactionName = 'Cancel'">
<Cancel/>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:template>
<xsl:template match="api:RequestId">
<RequestId>
<xsl:apply-templates/>
</RequestId>
</xsl:template>
<xsl:template match="api:TenderId">
<TransactionId>
<xsl:apply-templates/>
</TransactionId>
</xsl:template>
</xsl:transform>
Online at http://xsltransform.net/94rmq5R.
If there are lots of differences between the input formats then I might be tempted to handle them by different stylesheets. If that is not possible then it might make sense to branch in the template for the root and use modes on templates to distinguish the handling e.g.
<xsl:template match="api:Response">
<xsl:choose>
<xsl:when test="$transactionName = 'Update'">
<xsl:apply-templates select="." mode="update"/>
</xsl:when>
<xsl:when test="$transactionName = 'Cancel'">
<xsl:apply-templates select="." mode="cancel"/>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:template>
<xsl:template match="api:Response" mode="update">
<TransactionUpdate>
<xsl:apply-templates select="api:Foo | api:Bar" mode="update"/>
<Update/>
<TransactionUpdate>
</xsl:template>
<!-- now add templates for the other elements and for other mode(s) here -->

XslCompiledTransform fails on xsl:import

I have few XSLT files, index stylesheet imports layout using xsl:import. VS says that XSLT is valid, but Load operation raises xslt compilation exception, it says cannot import my layout file. It tries ti find it in c:\windows\system32 instead of my project dir.
Index.xsl:
<?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"
xmlns:h ="urn:helper"
exclude-result-prefixes="msxsl"
>
<xsl:import href="..\Shared\Layout.xsl"/>
<xsl:template match="/content" mode="content">
<p>Hello world</p>
</xsl:template>
</xsl:stylesheet>
and Layout.xsl:
<?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" encoding="utf-8" indent="yes" />
<xsl:template match="/xml">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<xsl:apply-templates select="/xml" mode="content" />
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Next I'm trying to apply transformation on my XML:
string viewPath = "~/Views/Home/Index.xsl";
var stylesheet = HostingEnvironment.VirtualPathProvider.GetFile(viewPath);
var xsl = new XslCompiledTransform();
using (var stream = stylesheet.Open())
using (var tmpl = XmlReader.Create(stream))
{
xsl.Load(tmpl, null, new XmlUrlResolver());
}
Is that ASP.NET? Then use
xsl.Load(MapPath(viewPath), null, new XmlUrlResolver());

XSLT lower-case using .NET

i use the following XSLT by using XMLSpy:
<?xml version="1.0" encoding="UTF-16"?>
<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">
<xsl:output method="xml" version="1.0" encoding="UTF-16" indent="yes"/>
<xsl:template match="*">
<xsl:element name="{lower-case(local-name())}">
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="* | text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{lower-case(local-name())}"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
If i try to use it in my source (XslCompiledTransform) i get an exception telling me that the function 'lower-case()' is not part of the XSLT synthax.
So i changed the transformation a little bit:
fn:lower-case
Now my exception is that the script or external object prefixed by 'http://www.w3.org/2005/xpath-functions' can not be found.
Whats the matter here? How can i fix it?
Regards
.NET does not implement XSLT 2.0/XPath 2.0.
In XPath 1.0 one can use the following expression, instead of lower-case():
translate(yourString,
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz')

Categories

Resources