I'm looking for a way to add this node "LoanSecondaryStatusDates" at the beginning and its corresponding closing tag "LoanSecondaryStatusDates" to the end. I've produced the below using "FOR XML" in SQL server but can't figure out how to add the beginning and end tags. If this is possible using "FOR XML" an example would be great, othwerwise C# would be fine. Thanks!
Currently:
<Loans>
<Loan>
<GUID></GUID>
<AgentCompanyName></AgentCompanyName>
<LoanSecondaryStatus>
<StatusName>Name</StatusName>
<StatusDate>Date</StatusDate>
</LoanSecondaryStatus>
<LoanSecondaryStatus>
<StatusName>Name</StatusName>
<StatusDate>Date</StatusDate>
</LoanSecondaryStatus>
</Loan>
</Loans>
End result should be:
<Loans>
<Loan>
<GUID></GUID>
<AgentCompanyName></AgentCompanyName>
<LoanSecondaryStatusDates>
<LoanSecondaryStatus>
<StatusName>Name</StatusName>
<StatusDate>Date</StatusDate>
</LoanSecondaryStatus>
<LoanSecondaryStatus>
<StatusName>Name</StatusName>
<StatusDate>Date</StatusDate>
</LoanSecondaryStatus>
</LoanSecondaryStatusDates>
</Loan>
</Loans>
FOR XML Query
SELECT
[GUID]
,[AgentCompanyName],
(
SELECT
'Borrower Docs Sent/Req' as 'StatusName',
CASE WHEN t.BorrowerDocsSent IS NOT NULL THEN t.BorrowerDocsSent ELSE '' END as 'StatusDate'
FROM Encompass_loanData as t
WHERE t.[GUID] = E.[GUID]
FOR XML PATH('LoanSecondaryStatus'), TYPE
),
(
SELECT
t.BorrowerCity as 'StatusName',
t.[GUID] as 'StatusDate'
FROM Encompass_loanData as t
WHERE t.[GUID] = E.[GUID]
FOR XML PATH('LoanSecondaryStatus'), TYPE
)
From Encompass_loanData E
FOR XML PATH ('Loan'), type, root('Loans')
SQL Fiddle: http://sqlfiddle.com/#!6/d672a/2/0
The xml you have published does not have a valid root. So I added <xml> as the root.
This can be done quite easily in C# too. But this is a way to do it using xpath.
<xml>
<LoanSecondaryStatus>
<StatusName>Name</StatusName>
<StatusDate>Date</StatusDate>
</LoanSecondaryStatus>
<LoanSecondaryStatus>
<StatusName>Name</StatusName>
<StatusDate>Date</StatusDate>
</LoanSecondaryStatus>
</xml>
Here is the XSL.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<LoanSecondaryStatusDates>
<xsl:for-each select="xml/LoanSecondaryStatus">
<LoanSecondaryStatus>
<StatusName>
<xsl:value-of select="StatusName"/>
</StatusName>
<StatusDate>
<xsl:value-of select="StatusDate"/>
</StatusDate>
</LoanSecondaryStatus>
</xsl:for-each>
</LoanSecondaryStatusDates>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="utf-8"?>
<LoanSecondaryStatusDates>
<LoanSecondaryStatus>
<StatusName>Name</StatusName>
<StatusDate>Date</StatusDate>
</LoanSecondaryStatus>
<LoanSecondaryStatus>
<StatusName>Name</StatusName>
<StatusDate>Date</StatusDate>
</LoanSecondaryStatus>
</LoanSecondaryStatusDates>
Updated with SQL query
SELECT
[GUID]
,[AgentCompanyName],
(
SELECT NULL,
(
SELECT
'Borrower Docs Sent/Req' as 'StatusName',
CASE WHEN t.BorrowerDocsSent IS NOT NULL THEN t.BorrowerDocsSent ELSE '' END as 'StatusDate'
FROM Encompass_loanData as t
WHERE t.[GUID] = E.[GUID]
FOR XML PATH('LoanSecondaryStatus'), TYPE
),
(
SELECT NULL AS X
FOR XML PATH('LoanSecondaryStatusDates'), TYPE
),
(
SELECT
t.BorrowerCity as 'StatusName',
t.[GUID] as 'StatusDate'
FROM Encompass_loanData as t
WHERE t.[GUID] = E.[GUID]
FOR XML PATH('LoanSecondaryStatus'), TYPE
),
NULL
FOR XML PATH('LoanSecondaryStatusDates'),TYPE
)
FROM Encompass_loanData E
WHERE [LASTMODIFIED] >= '20160121'
FOR XML PATH ('Loan'), type, root('Loans')
Take a look at the XElement class, you'll be able to modify the xml tree easily with that.
https://msdn.microsoft.com/en-us/library/system.xml.linq.xelement(v=vs.110).aspx
http://www.dotnetperls.com/xelement
I was able to wrap the child elements with this XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Loan">
<xsl:copy>
<xsl:apply-templates select="#*|node()[not(self::LoanSecondaryStatus)]"/>
<LoanSecondaryStatusDates>
<xsl:apply-templates select="LoanSecondaryStatus"/>
</LoanSecondaryStatusDates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Related
I have this XML input:
<?xml version="1.0"?>
<xc:XmlCache xmlns:xc="XmlCache" xmlns:mp="mx.MarketParameters" xmlns:rt="mx.MarketParameters.Rates" xmlns:rtcu="mx.MarketParameters.Rates.Curve">
<xc:XmlCacheArea xc:value="MarketParameters">
<mp:nickName xmlns:mp="mx.MarketParameters" xc:value="BO">
<mp:date xc:value="20211208">
<rt:rate xmlns:rt="mx.MarketParameters.Rates">
<rtcu:curve xmlns:rtcu="mx.MarketParameters.Rates.Curve">
<rtcu:currency xc:value="USD">
<rtcu:label xc:value="USD: SOV_ZC">
<rtcu:type xc:value="Swap">
<rtcu:generator xc:value="USD S TREAS">
<rtcu:market xc:value="">
<rtcu:maturity xc:value="10Y" xc:dates="20211210-20311210" xc:type="Fields">
<mp:askConvexitySpread xc:keyFormat="N">0</mp:askConvexitySpread>
<mp:askCurveSpread xc:keyFormat="N">0.000000000</mp:askCurveSpread>
<mp:askDiscount xc:keyFormat="N">0.864632927818374</mp:askDiscount>
<mp:askDiscountBasisSpread xc:keyFormat="N"/>
<mp:askMarketQuote xc:keyFormat="N">1.446068284</mp:askMarketQuote>
<mp:askZeroCoupon xc:keyFormat="N">1.45291000006736</mp:askZeroCoupon>
<mp:askZeroCouponBasisSpread xc:keyFormat="N"/>
<mp:askZeroCouponSpread xc:keyFormat="N">0.000000000</mp:askZeroCouponSpread>
<mp:bidConvexitySpread xc:keyFormat="N">0</mp:bidConvexitySpread>
<mp:bidCurveSpread xc:keyFormat="N">0.000000000</mp:bidCurveSpread>
<mp:bidDiscount xc:keyFormat="N">0.864632927818374</mp:bidDiscount>
<mp:bidDiscountBasisSpread xc:keyFormat="N"/>
<mp:bidMarketQuote xc:keyFormat="N">1.446068284</mp:bidMarketQuote>
<mp:bidZeroCoupon xc:keyFormat="N">1.45291000006736</mp:bidZeroCoupon>
<mp:bidZeroCouponBasisSpread xc:keyFormat="N"/>
<mp:bidZeroCouponSpread xc:keyFormat="N">0.000000000</mp:bidZeroCouponSpread>
<mp:frontierDate xc:keyFormat="N">0</mp:frontierDate>
<mp:intervalSplineEta xc:keyFormat="N">-1</mp:intervalSplineEta>
<mp:midConvexitySpread xc:keyFormat="N">0</mp:midConvexitySpread>
<mp:midCurveSpread xc:keyFormat="N">0.000000000</mp:midCurveSpread>
<mp:midDiscount xc:keyFormat="N">0.864632927818374</mp:midDiscount>
<mp:midDiscountBasisSpread xc:keyFormat="N"/>
<mp:midMarketQuote xc:keyFormat="N">1.446068284</mp:midMarketQuote>
<mp:midZeroCoupon xc:keyFormat="N">1.45291000006736</mp:midZeroCoupon>
<mp:midZeroCouponBasisSpread xc:keyFormat="N"/>
<mp:midZeroCouponSpread xc:keyFormat="N">0.000000000</mp:midZeroCouponSpread>
<mp:selected xc:keyFormat="N">1</mp:selected>
</rtcu:maturity>
<rtcu:maturity xc:value="15Y" xc:dates="20211210-20361210" xc:type="Fields">
<mp:askConvexitySpread xc:keyFormat="N">0</mp:askConvexitySpread>
<mp:askCurveSpread xc:keyFormat="N">0.000000000</mp:askCurveSpread>
<mp:askDiscount xc:keyFormat="N">0.769521955887512</mp:askDiscount>
<mp:askDiscountBasisSpread xc:keyFormat="N"/>
<mp:askMarketQuote xc:keyFormat="N">1.716406662</mp:askMarketQuote>
<mp:askZeroCoupon xc:keyFormat="N">1.74466000012162</mp:askZeroCoupon>
<mp:askZeroCouponBasisSpread xc:keyFormat="N"/>
<mp:askZeroCouponSpread xc:keyFormat="N">0.000000000</mp:askZeroCouponSpread>
<mp:bidConvexitySpread xc:keyFormat="N">0</mp:bidConvexitySpread>
<mp:bidCurveSpread xc:keyFormat="N">0.000000000</mp:bidCurveSpread>
<mp:bidDiscount xc:keyFormat="N">0.769521955887512</mp:bidDiscount>
<mp:bidDiscountBasisSpread xc:keyFormat="N"/>
<mp:bidMarketQuote xc:keyFormat="N">1.716406662</mp:bidMarketQuote>
<mp:bidZeroCoupon xc:keyFormat="N">1.74466000012162</mp:bidZeroCoupon>
<mp:bidZeroCouponBasisSpread xc:keyFormat="N"/>
<mp:bidZeroCouponSpread xc:keyFormat="N">0.000000000</mp:bidZeroCouponSpread>
<mp:frontierDate xc:keyFormat="N">0</mp:frontierDate>
<mp:intervalSplineEta xc:keyFormat="N">-1</mp:intervalSplineEta>
<mp:midConvexitySpread xc:keyFormat="N">0</mp:midConvexitySpread>
<mp:midCurveSpread xc:keyFormat="N">0.000000000</mp:midCurveSpread>
<mp:midDiscount xc:keyFormat="N">0.769521955887512</mp:midDiscount>
<mp:midDiscountBasisSpread xc:keyFormat="N"/>
<mp:midMarketQuote xc:keyFormat="N">1.716406662</mp:midMarketQuote>
<mp:midZeroCoupon xc:keyFormat="N">1.74466000012162</mp:midZeroCoupon>
<mp:midZeroCouponBasisSpread xc:keyFormat="N"/>
<mp:midZeroCouponSpread xc:keyFormat="N">0.000000000</mp:midZeroCouponSpread>
<mp:selected xc:keyFormat="N">1</mp:selected>
</rtcu:maturity>
</rtcu:market>
</rtcu:generator>
</rtcu:type>
</rtcu:label>
</rtcu:currency>
</rtcu:curve>
</rt:rate>
</mp:date>
</mp:nickName>
</xc:XmlCacheArea>
</xc:XmlCache>
I am using this transform :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:mp="mx.MarketParameters" xmlns:xc="xmlCache" xmlns:rt="mx.MarketParameters.Rates" xmlns:rtcu="mx.MarketParameters.Rates.Curve">
<xsl:template match="/">
<data>
<xsl:apply-templates select="*"/>
</data>
</xsl:template>
<xsl:template match="rtcu:maturity">
<row>
<xsl:attribute name="Cle"><xsl:value-of select="../../../../../#*:value"/>;<xsl:value-of select="../../../../#*:value"/>;<xsl:value-of select="../../../#*:value"/>;<xsl:value-of select="../../#*:value"/>;<xsl:value-of select="../#*:value"/>;<xsl:value-of select="#*:value"/></xsl:attribute>
<xsl:attribute name="Source1"><xsl:value-of select="mp:askZeroCoupon"/></xsl:attribute>
<xsl:attribute name="Source2"><xsl:value-of select="mp:bidZeroCoupon"/></xsl:attribute>
<xsl:attribute name="Source3"><xsl:value-of select="mp:midZeroCoupon"/></xsl:attribute>
</row>
</xsl:template>
</xsl:stylesheet>
And the result is as expected when I use the validator here: https://www.freeformatter.com/xsl-transformer.html
<data xmlns:mp="mx.MarketParameters"
xmlns:xc="xmlCache"
xmlns:rt="mx.MarketParameters.Rates"
xmlns:rtcu="mx.MarketParameters.Rates.Curve">
<row Cle="USD;USD: SOV_ZC;Swap;USD S TREAS;;10Y"
Source1="1.45291000006736"
Source2="1.45291000006736"
Source3="1.45291000006736"/>
<row Cle="USD;USD: SOV_ZC;Swap;USD S TREAS;;15Y"
Source1="1.74466000012162"
Source2="1.74466000012162"
Source3="1.74466000012162"/>
</data>
However when I run this transform in a C# app and call the Load() function in XslCompiledTransform when I pass it this transform above, it fails with the error
Expected end of the expression, found `':'.\r\n../../../../../#* -->:<--`
It basically refuses to check the other namespaces for the attribute I'm looking for on this node.
I know that the attributes are in the xc namespace but when I use the xc: namespace to get the attribute values to build my key like so:
<xsl:value-of select="#xc:value"/>
The values are all null which results in a key that looks like this in the output:
<row Cle=";;;;;"
No bueno!
How can I output the values of all the xc:value attributes from these tags:
<rtcu:currency xc:value="USD">
<rtcu:label xc:value="USD: SOV_ZC">
<rtcu:type xc:value="Swap">
<rtcu:generator xc:value="USD S TREAS">
<rtcu:market xc:value="">
<rtcu:maturity xc:value="10Y"
into my key row:
<row Cle="USD;USD: SOV_ZC;Swap;USD S TREAS;;10Y"
while not triggering a parsing error in the c# Load() function?
There are two errors in your code:
Namespace wildcards like *:value are a feature of XPath-2.0 and above
Local name tests (namespace wildcards) were eventually added to XPath 2.0
So you can't use them in your XSLT-1.0 code
You have a typo in your xc: namespace declaration:
In your XML it's xmlns:xc="XmlCache"
In your XSLT it's xmlns:xc="xmlCache"
If you fix both, the code should work as desired.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:mp="mx.MarketParameters" xmlns:xc="XmlCache" xmlns:rt="mx.MarketParameters.Rates" xmlns:rtcu="mx.MarketParameters.Rates.Curve">
<xsl:template match="/">
<data>
<xsl:apply-templates select="*"/>
</data>
</xsl:template>
<xsl:template match="rtcu:maturity">
<row>
<xsl:attribute name="Cle"><xsl:value-of select="../../../../../#xc:value"/>;<xsl:value-of select="../../../../#xc:value"/>;<xsl:value-of select="../../../#xc:value"/>;<xsl:value-of select="../../#xc:value"/>;<xsl:value-of select="../#xc:value"/>;<xsl:value-of select="#xc:value"/></xsl:attribute>
<xsl:attribute name="Source1"><xsl:value-of select="mp:askZeroCoupon"/></xsl:attribute>
<xsl:attribute name="Source2"><xsl:value-of select="mp:bidZeroCoupon"/></xsl:attribute>
<xsl:attribute name="Source3"><xsl:value-of select="mp:midZeroCoupon"/></xsl:attribute>
</row>
</xsl:template>
</xsl:stylesheet>
BTW you can simplify one of your expressions to
<xsl:attribute name="Cle"><xsl:for-each select="ancestor-or-self::rtcu:*"><xsl:value-of select="#xc:value"/><xsl:if test="position()!=last() and position()!=1">;</xsl:if></xsl:for-each></xsl:attribute>
This concatenates the xc:value attributes of all rtcu:* ancestors. Maybe that's of some use to you.
I am developing a app with C# and trying to complete a XML that I have got from a JSON. And for the XML to be valid for my app, I need to group the elements with the same name under a father element.
For example, I got this XML
<root>
<row>
<id>0001</id>
<type>credit</type>
<investment>1000</investment>
<ppr>0.83</ppr>
<candidate>
<id>5001</id>
<name>Hugo</name>
</candidate>
<candidate>
<id>5002</id>
<name>Jack</name>
</candidate>
<candidate>
<id>5005</id>
<name>Kate</name>
</candidate>
</row>
And I need to group all the elements with the name candidate, under a father node candidates, like this
<root>
<row>
<id>0001</id>
<type>credit</type>
<investment>1000</investment>
<ppr>0.83</ppr>
<candidates>
<candidate>
<id>5001</id>
<name>Hugo</name>
</candidate>
<candidate>
<id>5002</id>
<name>Jack</name>
</candidate>
<candidate>
<id>5005</id>
<name>Kate</name>
</candidate>
</candidates>
</row>
But here is my problem: I don't know the names that I can receive from the JSON. So I need to do this comparison and complete the XML without knowing the "candidate" node name. I need this for any name that I can receive.
Also in this example the XML only has 2 levels, but it can have any number of levels. I can iterate over the XML without problem with this function:
public void findAllNodes(XmlNode node)
{
Console.WriteLine(node.Name);
foreach (XmlNode n in node.ChildNodes)
findAllNodes(n);
}
How can I make the comparison and group the nodes?
A fairly naive implementation could use LINQ to group elements by name and add a parent element for those that have more than 1 item in a group. This would be recursive, so child elements of an element were grouped until the tree was exhausted.
The naive-ness is that such a solution would break if there were mixed content elements, and it would group elements that weren't siblings (basically, both issues will result in things ending up in the wrong order). It should give you a good start, and could be enough for your purposes.
private static IEnumerable<XElement> GroupElements(IEnumerable<XElement> elements)
{
var elementsByName = elements.GroupBy(x => x.Name);
foreach (var grouping in elementsByName)
{
var transformed = grouping.Select(e =>
new XElement(e.Name,
GroupElements(e.Elements()),
e.Attributes(),
e.Nodes().OfType<XText>()));
if (grouping.Count() == 1)
{
yield return transformed.Single();
}
else
{
var groupName = grouping.Key + "s";
yield return new XElement(groupName, transformed);
}
}
}
You can use this by parsing/loading your existing XML and then transforming the root elements and creating a new document from those:
var original = XDocument.Parse(xml);
var grouped = new XDocument(GroupElements(original.Elements()));
See this fiddle for a working demo.
Here's an XSLT 2.0 solution:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="*[*]">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="node-name(.)">
<xsl:choose>
<xsl:when test="count(current-group()) > 1">
<xsl:element name="{name()}s" namespace="{namespace-uri()}">
<xsl:apply-templates select="current-group()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<row>
<id>0001</id>
<type>credit</type>
<investment>1000</investment>
<ppr>0.83</ppr>
<candidates>
<candidate>
<id>5001</id>
<name>Hugo</name>
</candidate>
<candidate>
<id>5002</id>
<name>Jack</name>
</candidate>
<candidate>
<id>5005</id>
<name>Kate</name>
</candidate>
</candidates>
</row>
</root>
Limitations
It doesn't handle mixed content (elements with children plus text content)
It drops attributes (easily fixed)
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>
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);
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 >, 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>