XSL Using Params in a contains function - c#

Is it possible to use params as the data for a contains function?
I have a C# file that is passing information to a XSL sheet in the form of param's to make a html page that prints out the data. If I hard code the information it works but if i use params instead it returns nothing yet if i print out the information using a text tag it works so i know the value being passed in should be correct.
<xsl:param name="type"/>
<xsl:param name="filter"/>
<xsl:for-each select="london-schools/school [contains($type, '$filter')]">
that is what I am trying to do, and it just returns the table headings instead of information.
Thanks, Brandon.

Perhaps you meant:
<xsl:for-each select="london-schools/school [contains(type, $filter)]">
It's hard to tell for sure without seeing your input and the expected output - but certainly, if type is the name of a node, then it should not be prefixed by $, and if $filter is a parameter, then it should not be quoted.
Note also that XML is case-sensitive; you mention both type and Type - they are not the same.
Added:
I'm really guessing here, but consider the following:
XML
<records>
<record>
<name>Alpha</name>
<type>Bravo</type>
</record>
<record>
<name>Bravo</name>
<type>Bravo</type>
</record>
<record>
<name>Charlie</name>
<type>Alpha</type>
</record>
<record>
<name>Delta</name>
<type>Alpha</type>
</record>
</records>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="property"/>
<xsl:param name="value"/>
<xsl:template match="/records">
<xsl:copy>
<xsl:for-each select="record[contains(*[name()=$property], $value)]">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When the above stylesheet is applied to the input with parameters:
$property = "name"
$value = "Bravo"
the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<records>
<record>
<name>Bravo</name>
<type>Bravo</type>
</record>
</records>
When the parameters are:
$property = "type"
$value = "Bravo"
the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<records>
<record>
<name>Alpha</name>
<type>Bravo</type>
</record>
<record>
<name>Bravo</name>
<type>Bravo</type>
</record>
</records>

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 -->

Convert from XML to CSV

I have been searching for the solution to convert XML into CSV, but I cannot find one which matches my case as XML structure is different
XML structure looks like
<VWSRecipeFile>
<EX_Extrusion User="ABC" Version="1.0" Description="" LastChange="41914.7876341204">
<Values>
<C22O01_A_TempFZ1_Set Item="A_TempFZ1_Set" Type="4" Hex="42700000" Value="60"/>
<C13O02_A_TempHZ2_Set Item="A_TempHZ2_Set" Type="4" Hex="43430000" Value="195"/>
<C13O03_A_TempHZ3_Set Item="A_TempHZ3_Set" Type="4" Hex="43430000" Value="195"/>
</Values>
</EX_Extrusion>
</VWSRecipeFile>
Expected CSV Format
A_TempFZ1_Set,A_TempHZ2_Set,A_TempHZ3_Set
60,195,195
i can achieve the new expected csv format, but don't know if it is the best way to do it, any suggestion is appreciated
'
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:template match="/VWSRecipeFile">
<xsl:for-each select="EX_Extrusion/Values/*">
<xsl:value-of select="concat(#Item,',')" />
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="EX_Extrusion/Values/*">
<xsl:value-of select="concat(#Value,',')" />
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>'
Thanks
One way you can do this is to use XSLT, the language designed to work with XML. You surely can parse the XML with C# but I like XSLT cause it's cleaner.
You define an external XSLT file, then call it within C# to do the transform.
Edit: added new columns based on new requirements.
File C:\XmlToCSV.xslt (
is the newline character)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:template match="/VWSRecipeFile">
<xsl:variable name="User" select="EX_Extrusion/#User"/>
<xsl:variable name="Version" select="EX_Extrusion/#Version"/>
<xsl:variable name="Description" select="EX_Extrusion/#Description"/>
<xsl:variable name="LastChange" select="EX_Extrusion/#LastChange"/>
<xsl:text>Item,Type,Hex,Value,User,Version,Description,LastChange
</xsl:text>
<xsl:for-each select="EX_Extrusion/Values/*">
<xsl:value-of select="concat(#Item,',',#Type,',',#Hex,',',#Value,',',$User,',',$Version,',',$Description,',',$LastChange,'
')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Apply the transform with XslCompiledTransform:
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load("C:\\XmlToCSV.xslt");
xslt.Transform("InputFile.xml", "OutputFile.csv");
Adjust it based on your needs.
Basic idea would be to iterate though values nodes and select the attributes you want for each node and keep writing them to a file with comma separator. Simply name the file as .csv. If you want something ready made, check this out.
XSLT is one way to do it. Alternatively you can use, Cinchoo ETL - an open source library available to parse xml, produce CSV the way you want it.
string xml = #"<VWSRecipeFile>
<EX_Extrusion User=""ABC"" Version=""1.0"" Description="""" LastChange=""41914.7876341204"">
<Values>
<C22O01_A_TempFZ1_Set Item=""A_TempFZ1_Set"" Type=""4"" Hex=""42700000"" Value=""60""/>
<C13O02_A_TempHZ2_Set Item=""A_TempHZ2_Set"" Type=""4"" Hex=""43430000"" Value=""195""/>
<C13O03_A_TempHZ3_Set Item=""A_TempHZ3_Set"" Type=""4"" Hex=""43430000"" Value=""196""/>
</Values>
</EX_Extrusion>
</VWSRecipeFile>";
StringBuilder sb = new StringBuilder();
using (var p = ChoXmlReader.LoadText(xml).WithXPath("/Values/*"))
{
using (var w = new ChoCSVWriter(sb)
.WithFirstLineHeader()
)
w.Write(p.ToDictionary(r => r.Item, r => r.Value).ToDynamic());
}
Console.WriteLine(sb.ToString());
Output:
A_TempFZ1_Set,A_TempHZ2_Set,A_TempHZ3_Set
60,195,196
Disclaimer: I'm the author of this library.

XSLT choose template

I have an application written in C# that needs to apply a template name to an xml file that is defined in an XSLT.
Example XML:
<Data>
<Person>
<Name>bob</Name>
<Age>43</Age>
</Person>
<Thing>
<Color>Red</Color>
</Thing>
</Data>
Example XSLT:
<?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">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="TargetName" />
<xsl:param name="RootPath" />
<xsl:Template Name="AgeGrabber">
<xsl:value-of select="/Person/Age" />
</xsl:Template>
<xsl:Template Name="ColorGrabber">
<xsl:value-of select="/Color" />
</xsl:Template>
</xsl:stylesheet>
Say I wanted to run the template "ColorGrabber" with path "/Data/Thing" and then run another transform with the template "AgeGrabber" with path "/Data". Is this possible? I was thinking I could pass in the path and the template name (hense the 2 params at the top) and then do some type of switch but it looks like xsl:call-template can not take a parameter as the name attribute.
How can I achieve this behaviour?
There are a number of issues with this question:
<xsl:stylesheet version="2.0" ... is specified, however, at present >NET doesn't natively support XSLT 2.0.
Thecode example is not too meaningful, because a single XML document cannot contain both /Person/Age and /Color elements -- a wellformed XML document has only a single top element and it can be either Person or Color, but not both.
In case there was a more meaningful example:
<Person>
<Age>27</Age>
<HairColor>blond</HairColor>
</Person>
one simple solution is:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pProperty" select="'Age'"/>
<xsl:template match="/">
<xsl:value-of select="/*/*[name()=$pProperty]"/>
</xsl:template>
</xsl:stylesheet>
and when this transformation is applied on the above XML document, it produces the wanted result:
27
In case the nestedness of the elements of interest can be arbitrary and/or we need to do different processing on the different elements, then an appropriate solution is to use matching templates (not named ones):
<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:param name="pProperty" select="'HairColor'"/>
<xsl:template match="Age">
<xsl:if test="$pProperty = 'Age'">
This person is <xsl:value-of select="."/> old.
</xsl:if>
</xsl:template>
<xsl:template match="HairColor">
<xsl:if test="$pProperty = 'HairColor'">
This person has <xsl:value-of select="."/> hair.
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document (above), again the correct result is produced:
This person has blond hair.
Finally, if you really want to simulate higher-order functions (HOF) in XSLT 1.0 or XSLT 2.0, see this answer: https://stackoverflow.com/a/8363249/36305 , or learn about FXSL.
Much simpler: prepare two apply-templates rules (for Age and Color elements) and conditionally send proper node to transform - //Person/Age or //Thing/Color
You got it backwards. You ought to create templates, matching the nodes you want to use.
<xsl:stylesheet>
<xsl:template match="Person|Thing">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="Person">
<xsl:value-of select="Age" />
</xsl:template>
<xsl:template match="Thing">
<xsl:value-of select="Color" />
</xsl:template>
</xsl:stylesheet>

Categories

Resources