XSLT: Howto output '<' and '>' from XSL transformation? - c#

I am building some text snippets from XML documents using XSL transformation. The text can include '<' and '>' (and other special characters) in the output.
Given the following xml as data.xml:
<SCH>
<Ship Id="1" Name="Dicke Bertha" OperatingCostsDay="10.0000000" currency="USD" />
</SCH>
and the following xsl as transformation.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:template match="/">
Schiff: <xsl:for-each select="SCH/Ship"> Id: <xsl:value-of select="#Id"/>  <xsl:value-of select="#Name"/>  (OC:<xsl:value-of select="#OperatingCostsDay"/> <xsl:value-of select="#currency"/>)
</xsl:for-each>Betriebskosten < 500 USD oder > 1 Mio. USD
</xsl:template>
</xsl:stylesheet>
I have the problem that the escape sequences < and > when loading the XSLT:
var xslDocument = XDocument.Load("transformation.xslt");
The output after the transformation is not - as expected - with the '<' and '>' characters but it has the HTML escape sequences.
Schiff: Id: 1  Dicke Bertha  (OC:10.0000000 USD)
Betriebskosten < 500 USD oder > 1 Mio. USD
What can I do here?
For completeness, here is the full code example:
class Program
{
static void Main(string[] args)
{
var xslDocument = XDocument.Load("transformation.xslt");
var compiled = new XslCompiledTransform();
using (var reader = xslDocument.CreateReader())
{
compiled.Load(reader);
}
var xml = File.ReadAllText("data.xml");
Console.WriteLine(Transform(compiled, xml));
}
public static string Transform(XslCompiledTransform xsl, string xml)
{
// allow fragments
var writerSettings = new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Auto };
var readerSettings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Auto };
var stringReader = new StringReader(xml);
var details = new StringBuilder();
using (var reader = XmlReader.Create(stringReader, readerSettings))
{
using (var writer = XmlWriter.Create(details, writerSettings))
{
xsl.Transform(reader, writer);
}
}
return details.ToString();
}
}

I am building some text snippets
If you are outputting text, then set your output method to text. Then your processor will know not to escape characters that are reserved in XML.
XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="/">
Schiff: <xsl:for-each select="SCH/Ship"> Id: <xsl:value-of select="#Id"/>  <xsl:value-of select="#Name"/>  (OC:<xsl:value-of select="#OperatingCostsDay"/> <xsl:value-of select="#currency"/>)
</xsl:for-each>Betriebskosten < 500 USD oder > 1 Mio. USD
</xsl:template>
</xsl:stylesheet>
Result:
Schiff: Id: 1  Dicke Bertha  (OC:10.0000000 USD)
Betriebskosten < 500 USD oder > 1 Mio. USD
Note also that literal text is best put inside xsl:text instructions - otherwise you're passing all the unwanted surrounding whitespace to the output.

in XSLT, you can try like
Here I have added <xsl:text disable-output-escaping="yes"><</xsl:text> and <xsl:text disable-output-escaping="yes">></xsl:text>
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="/">
Schiff: <xsl:for-each select="SCH/Ship"> Id: <xsl:value-of select="#Id"/>  <xsl:value-of select="#Name"/>  (OC:<xsl:value-of select="#OperatingCostsDay"/> <xsl:value-of select="#currency"/>)
</xsl:for-each> Betriebskosten <xsl:text disable-output-escaping="yes"><</xsl:text> 500 USD oder <xsl:text disable-output-escaping="yes">></xsl:text> 1 Mio. USD
</xsl:template>
</xsl:stylesheet>

Related

c# XML transformation

i need help for below xml. I wonder how can I bring the XML below, I've been trying for a very long time, but it didn't work. I would be grateful if you could help with this.
I can do the same thing in php, but since my data is a little big, php takes a lot of time, I read the file from the web service with c# and save it as xml, but it didn't work out as I wanted.
<ResultOfProductList xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServerCode>kenan</ServerCode>
<ClientCode>B124565</ClientCode>
<Username>karanfil</Username>
<ClientIPAddress>78.135.235.3</ClientIPAddress>
<Successful>true</Successful>
<RequestDate>2022-07-22T12:28:54.6238843+03:00</RequestDate>
<ElapsedTimeMS>17</ElapsedTimeMS>
<MethodType>GetAllProductsByParts</MethodType>
<Result EndOfProducts="false">
<CustomerCode>B124565</CustomerCode>
<CustomerName>kenan</CustomerName>
<Date>2022-07-22T12:28:54.6238843+03:00</Date>
<Brands>
<Brand ID="174" Lang="tr" BrandName="PARTSMALL-KORE" StandardName=""/>
</Brands>
<Products>
<Product ID="134898" BrandID="174" ProductCode="KR-PML-PTA-086" ProducerCode="" MinOrderAmount="1" PiecesInBox="1" Unit="PCE" New="false">
<ProductNames>
<ProductName Lang="tr">VITES HALATI ( HYUNDAI : ACCENT 95-00 )</ProductName>
</ProductNames>
<BaseOeNr>43794-22000</BaseOeNr>
<Pricing>
<ListPriceCurrency>USD</ListPriceCurrency>
<LocalCurrency>TLY</LocalCurrency>
<CurrencyRate>17.6599</CurrencyRate>
<ListPriceWoVAT>31.24</ListPriceWoVAT>
<LocalListPriceWVat>651.0004</LocalListPriceWVat>
<LocalListPriceWoVat>551.695251</LocalListPriceWoVat>
<LocalNetPriceWVat>377.580261</LocalNetPriceWVat>
<LocalNetPriceWoVat>319.983246</LocalNetPriceWoVat>
<Discount1>42</Discount1>
<Discount2>0</Discount2>
<Discount3>0</Discount3>
<Discount4>0</Discount4>
<Discount5>0</Discount5>
<Discount6>0</Discount6>
<InDiscount>false</InDiscount>
</Pricing>
<Stocks>
<Stock WarehouseID="1" Equality="Eq">0</Stock>
<Stock WarehouseID="7" Equality="Eq">0</Stock>
<Stock WarehouseID="4" Equality="Eq">0</Stock>
<Stock WarehouseID="2" Equality="Eq">0</Stock>
<Stock WarehouseID="5" Equality="Eq">0</Stock>
</Stocks>
</Product>
</Result>
</ResultOfProductList>
Desired output
<Products>
<Product>
<ID>134898</ID>
<BrandID>174</BrandID>
<BaseOeNr>43794-22000</BaseOeNr>
<ProductCode>KR-PML-PTA-086</ProductCode>
<ProductName>VITES HALATI ( HYUNDAI : ACCENT 95-00 )</ProductName>
<LocalCurrency>TLY</LocalCurrency>
<LocalNetPriceWVat>377.580261</LocalNetPriceWVat>
<Stocks>WarehouseID=1 + WarehouseID=7 + WarehouseID=4 + WarehouseID=2 + WarehouseID=5 </Stocks>
</Product>
</Products>
Here is XSLT based solution.
It is just not clear if the <Stocks> element value is what you need.
Input XML
<?xml version="1.0"?>
<ResultOfProductList xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServerCode>kenan</ServerCode>
<ClientCode>B124565</ClientCode>
<Username>karanfil</Username>
<ClientIPAddress>78.135.235.3</ClientIPAddress>
<Successful>true</Successful>
<RequestDate>2022-07-22T12:28:54.6238843+03:00</RequestDate>
<ElapsedTimeMS>17</ElapsedTimeMS>
<MethodType>GetAllProductsByParts</MethodType>
<Result EndOfProducts="false">
<CustomerCode>B124565</CustomerCode>
<CustomerName>kenan</CustomerName>
<Date>2022-07-22T12:28:54.6238843+03:00</Date>
<Brands>
<Brand ID="174" Lang="tr" BrandName="PARTSMALL-KORE" StandardName=""/>
</Brands>
<Products>
<Product ID="134898" BrandID="174" ProductCode="KR-PML-PTA-086" ProducerCode="" MinOrderAmount="1" PiecesInBox="1" Unit="PCE" New="false">
<ProductNames>
<ProductName Lang="tr">VITES HALATI ( HYUNDAI : ACCENT 95-00 )</ProductName>
</ProductNames>
<BaseOeNr>43794-22000</BaseOeNr>
<Pricing>
<ListPriceCurrency>USD</ListPriceCurrency>
<LocalCurrency>TLY</LocalCurrency>
<CurrencyRate>17.6599</CurrencyRate>
<ListPriceWoVAT>31.24</ListPriceWoVAT>
<LocalListPriceWVat>651.0004</LocalListPriceWVat>
<LocalListPriceWoVat>551.695251</LocalListPriceWoVat>
<LocalNetPriceWVat>377.580261</LocalNetPriceWVat>
<LocalNetPriceWoVat>319.983246</LocalNetPriceWoVat>
<Discount1>42</Discount1>
<Discount2>0</Discount2>
<Discount3>0</Discount3>
<Discount4>0</Discount4>
<Discount5>0</Discount5>
<Discount6>0</Discount6>
<InDiscount>false</InDiscount>
</Pricing>
<Stocks>
<Stock WarehouseID="1" Equality="Eq">0</Stock>
<Stock WarehouseID="7" Equality="Eq">0</Stock>
<Stock WarehouseID="4" Equality="Eq">0</Stock>
<Stock WarehouseID="2" Equality="Eq">0</Stock>
<Stock WarehouseID="5" Equality="Eq">0</Stock>
</Stocks>
</Product>
</Products>
</Result>
</ResultOfProductList>
XSLT
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/ResultOfProductList">
<Products>
<xsl:for-each select="Result/Products/Product">
<xsl:variable name="BrandID" select="#BrandID"/>
<Product>
<ID>
<xsl:value-of select="#ID"/>
</ID>
<!--<BrandID>
<xsl:value-of select="#BrandID"/>
</BrandID>-->
<BrandName><xsl:value-of select="../../Brands/Brand[#ID=$BrandID]/#BrandName"/></BrandName>
<BaseOeNr>
<xsl:value-of select="BaseOeNr"/>
</BaseOeNr>
<ProductCode>
<xsl:value-of select="#ProductCode"/>
</ProductCode>
<ProductName>
<xsl:value-of select="ProductNames/ProductName"/>
</ProductName>
<LocalCurrency>
<xsl:value-of select="Pricing/LocalCurrency"/>
</LocalCurrency>
<LocalNetPriceWVat>
<xsl:value-of select="Pricing/LocalNetPriceWVat"/>
</LocalNetPriceWVat>
<Stocks><xsl:value-of select="sum(Stocks/Stock)"/></Stocks>
</Product>
</xsl:for-each>
</Products>
</xsl:template>
</xsl:stylesheet>
Output XML
<Products>
<Product>
<ID>134898</ID>
<BrandName>PARTSMALL-KORE</BrandName>
<BaseOeNr>43794-22000</BaseOeNr>
<ProductCode>KR-PML-PTA-086</ProductCode>
<ProductName>VITES HALATI ( HYUNDAI : ACCENT 95-00 )</ProductName>
<LocalCurrency>TLY</LocalCurrency>
<LocalNetPriceWVat>377.580261</LocalNetPriceWVat>
<Stocks>0</Stocks>
</Product>
</Products>
c#
void Main()
{
const string SOURCEXMLFILE = #"e:\Temp\input.xml";
const string XSLTFILE = #"e:\Temp\process.xslt";
const string OUTPUTXMLFILE = #"e:\temp\output.xml";
try
{
XsltArgumentList xslArg = new XsltArgumentList();
using (XmlReader src = XmlReader.Create(SOURCEXMLFILE))
{
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(XSLTFILE, new XsltSettings(true, true), new XmlUrlResolver());
XmlWriterSettings settings = xslt.OutputSettings.Clone();
settings.IndentChars = "\t";
// to remove BOM
settings.Encoding = new UTF8Encoding(false);
using (XmlWriter result = XmlWriter.Create(OUTPUTXMLFILE, settings))
{
xslt.Transform(src, xslArg, result, new XmlUrlResolver());
result.Close();
}
}
Console.WriteLine("File '{0}' has been generated.", OUTPUTXMLFILE);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

Using XSLT to query and pull data from XML

I am trying to use XSLT to solve the following problem but am a bit stuck.
I have an ID of a tool, let's say a hammer. I need to take that ID variable, pass it into an XSLT stylesheet, use the XSLT to match all the Task elements in an XML file containing a tool with that ID .
So in one document (tasks.xml) I have the following (note the <tool><id>)
<data>
<DMs>
<task>
<DMC>TEST-4BX-AG3-00-00-0000-125B-A</DMC>
<techName>Fixing fence</techName>
<infoName>Inserting panels</infoName>
<tools>
<tool>
<id>1</id><qty>1</qty>
</tool>
<tool>
<id>4</id><qty>1</qty>
</tool>
</tools>
</task>
<task>
<DMC>TEST-4BX-AG3-00-00-0000-125B-A</DMC>
<techName>Fixing floor</techName>
<infoName>Install floorboard</infoName>
<notes>-</notes>
<tools>
<tool>
<id>89</id><qty>1</qty>
</tool>
<tool>
<id>25</id><qty>2</qty>
</tool>
</tools>
</task>
</DMs>
</data>
So assuming the hammer's ID is "1", i now need to search the Tasks.xml file and match all Tasks where the hammer is used. Only tasks that have the hammer should be included in the output.
That's the first part of the problem.
I have another file, Tools.xml, which contains all the tool information, as follows:
<data>
<tools>
<tool id="1">
<toolName>Hammer</toolName>
<toolPN>345123</toolPN>
<toolCage>-</toolCage>
</tool>
<tool id="2">
<toolName>Digital Multimeter Set</toolName>
<toolPN>Fluke Model No. 89IV</toolPN>
<toolCage>-</toolCage>
</tool>
<tool id="3">
<toolName>Digital Storage Oscilloscope</toolName>
<toolPN>Tektronix 3052B</toolPN>
<toolCage>-</toolCage>
</tool>
<tool id="4">
<toolName>Socket set</toolName>
<toolPN>737828</toolPN>
<toolCage>-</toolCage>
</tool>
</tools>
</data>
I also need to pull the tool's actual name and details from Tools.XML so the output xml file lists the tools details such as name and part number etc. instead of just an ID.
So what I want is something like this output:
<data>
<DMs>
<task>
<DMC>TEST-4BX-AG3-00-00-0000-125B-A</DMC>
<techName>Fixing fence</techName>
<infoName>Inserting panels</infoName>
<tools>
<tool id="1">
<toolName>Hammer</toolName>
<toolPN>345123</toolPN>
<toolCage>-</toolCage>
</tool>
<tool id="4">
<toolName>Socket set</toolName>
<toolPN>737828</toolPN>
<toolCage>-</toolCage>
</tool>
</tools>
</task>
<task>
I've been messing around with the XSLT but can't get it right. Everything i try seems to completely remove the tools from the output.
Here's some XSLT i've been playing with using Document() function and XSLT Key, but i'm really just trying to learn XSLT as i go and am getting frustrated.
<?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:variable name="dataModuleLookupDoc" select="document('C:\TOOLS.xml')"/>
<xsl:key name="toolIDKey" match="tool" use="#id"/>
<xsl:template match="task">
<xsl:apply-templates select="$dataModuleLookupDoc"/>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="tools"/>
<xsl:template match="tool">
<xsl:variable name="toolID" select="#ID"/>
<xsl:for-each select="$dataModuleLookupDoc">
<xsl:value-of select="key('toolIDKey',toolID)"/>
</xsl:for-each>
<xsl:text> </xsl:text>
<xsl:apply-templates/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Instead of key('toolIDKey',toolID) you want key('toolIDKey, $toolID). That should find the element in the other document, of course the <xsl:value-of select="key('toolIDKey, $toolID)"/> seems too simple an attempt to "pull the tool's actual name and details", either use xsl:copy-of if you simply want the element from the other document copied or push it through some templates with xsl:apply-templates to transform it as needed.
Try xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME1 = #"c:\temp\test1.xml";
const string FILENAME2 = #"c:\temp\test2.xml";
static void Main(string[] args)
{
XDocument xTools = XDocument.Load(FILENAME2);
Dictionary<int, Tool> toolsDict = xTools.Descendants("tool")
.Select(x => new Tool() {
id = (int)x.Attribute("id"),
name = (string)x.Element("toolName"),
pn = (string)x.Element("toolPN"),
cage = (string)x.Element("toolCage")
}).GroupBy(x => x.id, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
XDocument xTasks = XDocument.Load(FILENAME1);
List<XElement> toolTask = xTasks.Descendants("tool").ToList();
foreach (XElement tool in toolTask)
{
int id = (int)tool.Element("id");
int qty = (int)tool.Element("qty");
if(toolsDict.ContainsKey(id))
{
Tool idTool = toolsDict[id];
tool.ReplaceWith(new XElement("tool", new object[] {
new XAttribute("id", id),
new XAttribute("qty", qty),
new XElement("toolName", idTool.name),
new XElement("toolPN", idTool.pn),
new XElement("toolCage", idTool.cage)
}));
}
// <toolName>Hammer</toolName>
// <toolPN>345123</toolPN>
// <toolCage>-</toolCage>
//</tool>
}
}
}
public class Tool
{
public int id { get;set;}
public string name { get; set; }
public string pn { get; set; }
public string cage { get; set; }
}
}

How to pass document type parameter to xslt using saxon?

For sending atomic data types will use like
transformer.SetParameter(new QName("", "", customXml), new XdmAtomicValue("true"));
how to pass a XML/Node as a param to XSLT from C# ?
Can you please help me
followed your code it's working fine but i am getting only text inside the xml(what i am passing in parameter) but not Nodes
XSLT :
<xsl:param name="look-up" as="document-node()"/>
<xsl:template match="xpp:document">
<w:document xml:space="preserve">
<xsl:value-of select="$look-up"/>
</w:document>
</xsl:template>
XML
<?xml version="1.0" encoding="UTF-8"?>
<document version="1.0" xmlns="http://www.sdl.com/xpp">
//some tags
</document>
passing parameter (xml)
<Job>
</id>
</Job>
I think you should use the Processor object to construct an XdmNode, see the documentation which says:
The Processor provides a method NewDocumentBuilder which, as the name
implies, returns a DocumentBuilder. This may be used to construct a
document (specifically, an XdmNode) from a variety of sources. The
input can come from raw lexical XML by specifying a Stream or a Uri,
or it may come from a DOM document built using the Microsoft XML
parser by specifying an XmlNode, or it may be supplied
programmatically by nominating an XmlReader.
Then you can pass in the XdmNode to the SetParameter method http://saxonica.com/documentation/html/dotnetdoc/Saxon/Api/XsltTransformer.html#SetParameter%28Saxon.Api.QName,Saxon.Api.XdmValue%29 as XdmValue is a base class of XdmNode (XmlValue -> XdmItem -> XdmNode).
Here is an example that works for me with Saxon 9.5 HE on .NET:
Processor proc = new Processor();
DocumentBuilder db = proc.NewDocumentBuilder();
XsltTransformer trans;
using (XmlReader xr = XmlReader.Create("../../XSLTFile1.xslt"))
{
trans = proc.NewXsltCompiler().Compile(xr).Load();
}
XdmNode input, lookup;
using (XmlReader xr = XmlReader.Create("../../XMLFile1.xml"))
{
input = db.Build(xr);
}
using (XmlReader xr = XmlReader.Create("../../XMLFile2.xml"))
{
lookup = db.Build(xr);
}
trans.InitialContextNode = input;
trans.SetParameter(new QName("lookup-doc"), lookup);
using (XmlWriter xw = XmlWriter.Create(Console.Out))
{
trans.Run(new TextWriterDestination(xw));
}
The XSLT is
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:param name="lookup-doc"/>
<xsl:key name="map" match="map" use="key"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<xsl:copy>
<xsl:value-of select="key('map', ., $lookup-doc)/value"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
the XML documents are
<root>
<item>foo</item>
</root>
and
<root>
<map>
<key>foo</key>
<value>bar</value>
</map>
</root>
the resulting output is
<root>
<item>bar</item>
</root>

Xml for txt file records

I am having a scenario to push the text records to db to track some information on an individual.
My text file contains the data as shown below:
logonID|agentName|modify|exception|start|stop|externalID
14051286759|Jacks, Monica|1373477063|Break|01:45|02:00|USWMAJ43
14051286759|Jacks, Monica|1373477063|Break|06:10|06:25|USWMAJ43
14051286759|Jacks, Monica|1373477063|Lunch|03:45|04:30|USWMAJ43
14051286759|Jacks, Monica|1373477063|Open|00:00|01:45|USWMAJ43
14051286759|Jacks, Monica|1373477063|Open|02:00|03:45|USWMAJ43
14051286759|Jacks, Monica|1373477063|Open|04:30|06:10|USWMAJ43
14051286759|Jacks, Monica|1373477063|Open|06:25|08:30|USWMAJ43
Now i need to create an XML for Break,Lunch and open(number of open for a record may vary about 5 to 6 entries) as:
<info>
<break>
<break_1>01:45-02:00</break_1>
<break_2>06:10-06:25</break_1>
</break>
<lunch>03:45-04:30</lunch>
<open>
<open_1>00:00-01:45</open_1>
<open_2>02:00-03:45</open_2>
<open_3>04:30-06:10</open_3>
<open_4>06:25-08:30</open_4>
</open>
</info>
Thanks in Advance
XSLT
To use the xslt you pass the file location to pText parameter, for example file:///C:/data.txt :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pText" />
<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="vText" select="unparsed-text($pText)"/>
<xsl:variable name="vLines" select="tokenize($vText, '\r?\n')[position() > 1][normalize-space()]"/>
<xsl:template match="/">
<info>
<xsl:for-each-group select="$vLines" group-by="tokenize(.,'\|')[4]">
<xsl:variable name="vKey" select="translate(current-grouping-key(), $uppercase, $smallcase)"/>
<xsl:element name="{$vKey}">
<xsl:for-each select="current-group()">
<xsl:variable name="vValues" select="tokenize(.,'\|')"/>
<xsl:variable name="vPos" select="position()"/>
<xsl:element name="{$vKey}_{$vPos}">
<xsl:value-of select="concat($vValues[5],'-',$vValues[6])"/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</info>
</xsl:template>
</xsl:stylesheet>
Input:
logonID|agentName|modify|exception|start|stop|externalID
14051286759|Jacks, Monica|1373477063|Break|01:45|02:00|USWMAJ43
14051286759|Jacks, Monica|1373477063|Break|06:10|06:25|USWMAJ43
14051286759|Jacks, Monica|1373477063|Lunch|03:45|04:30|USWMAJ43
14051286759|Jacks, Monica|1373477063|Open|00:00|01:45|USWMAJ43
14051286759|Jacks, Monica|1373477063|Open|02:00|03:45|USWMAJ43
14051286759|Jacks, Monica|1373477063|Open|04:30|06:10|USWMAJ43
14051286759|Jacks, Monica|1373477063|Open|06:25|08:30|USWMAJ43
Output:
<info>
<break>
<break_1>01:45-02:00</break_1>
<break_2>06:10-06:25</break_2>
</break>
<lunch>
<lunch_1>03:45-04:30</lunch_1>
</lunch>
<open>
<open_1>00:00-01:45</open_1>
<open_2>02:00-03:45</open_2>
<open_3>04:30-06:10</open_3>
<open_4>06:25-08:30</open_4>
</open>
</info>
C#
Reference 1 and Reference 2
XsltArgumentList argsList = new XsltArgumentList();
argsList.AddParam("pText", "", "file:///C:/data.txt");
XPathDocument myXPathDoc = new XPathDocument(myXmlFile) ;
XslCompiledTransform myXslTrans = new XslCompiledTransform();
myXslTrans.Load(myStyleSheet);
XmlTextWriter myWriter = new XmlTextWriter("result.html",null);
myXslTrans.Transform(myXPathDoc,argsList,myWriter);

How can I transform XML to invalid XML using XSLT?

I need to transform a valid XML document to the OFX v1.0.2 format. This format is more or less XML, but it's technically invalid and therefore cannot be parsed as XML.
I'm having trouble getting my Xml transformation working because the .Net XslCompiledTransform object insists on interpreting the output as an XML document (which is fair enough).
**Here's my function to transform the Xml
public string Transform(XmlElement xmlElement, Dictionary<string, object> parameters)
{
string strReturn = "";
// Set the settings to allow scripts to executed.
XsltSettings settings = new XsltSettings(false, true);
// Load the XSLT Document
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(xsltFileName, settings, new XmlUrlResolver());
// arguments
XsltArgumentList args = new XsltArgumentList();
if (parameters != null && parameters.Count > 0)
{
foreach (string key in parameters.Keys)
{
args.AddParam(key, "", parameters[key]);
}
}
//Create a memory stream to write to
Stream objStream = new MemoryStream();
// Transform the xml/xslt into a Writer
XmlTextWriter xmlWriter = new XmlTextWriter(objStream, Encoding.UTF8);
// Apply the transform
xslt.Transform(xmlElement, args, xmlWriter);
objStream.Seek(0, SeekOrigin.Begin);
// Read the contents of the stream
StreamReader objSR = new StreamReader(objStream);
strReturn = objSR.ReadToEnd();
return strReturn;
}
If I escape the xml-ish tags using < and &gt, they get removed when I download the file.
Here's the start of my XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"></xsl:output>
<xsl:param name="currentdate"></xsl:param>
<xsl:template match="Transactions">
OFXHEADER:100
DATA:OFXSGML
VERSION:102
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
<OFX>
<SIGNONMSGSRSV1>
<SONRS>
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<DTSERVER><xsl:value-of select="$currentdate" />
<LANGUAGE>ENG
So can I transform my XML to a plain text string?
UPDATE:
I've changed this question. I just realised the obvious answer to the original question. Using the XslCompiledTransform object requires me to write the output to an Xml document using an XmlTextWriter. Obviously it won't parse. Apologies.
Xslt can output text; just make sure that you set the output-mode of the xslt to text, and wriet to a TextWriter (there are multiple overloads available). Writing something that is nearly xml in xslt is painful, but possible with disabling the escape rules.
Here's an example (albeit ugly) xslt for writing non-xml:
<?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="text" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:call-template name="startElement">
<xsl:with-param name="name" select="'SONRS'"/>
</xsl:call-template>
<xsl:call-template name="startElement">
<xsl:with-param name="name" select="'STATUS'"/>
<xsl:with-param name="value" select="0"/>
</xsl:call-template>
<xsl:call-template name="startElement">
<xsl:with-param name="name" select="'SEVERITY'"/>
<xsl:with-param name="value" select="'INFO'"/>
</xsl:call-template>
<xsl:call-template name="endElement">
<xsl:with-param name="name" select="'SONRS'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="startElement">
<xsl:param name="name"/>
<xsl:param name="value"/>
<xsl:text disable-output-escaping="yes"><</xsl:text>
<xsl:value-of select="$name"/>
<xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:value-of select="$value"/>
</xsl:template>
<xsl:template name="endElement">
<xsl:param name="name"/>
<xsl:text disable-output-escaping="yes"></</xsl:text>
<xsl:value-of select="$name"/>
<xsl:text disable-output-escaping="yes">></xsl:text>
</xsl:template>
</xsl:stylesheet>

Categories

Resources