How to get a particular node from xml to xslt - c#

The XML has repetitive node OptionCd
<AdjusterParty AdjusterPartyIdRef="20130000074-001">
<Option>
<OptionCd>SAL</OptionCd>
<OptionValue>N</OptionValue>
</Option>
<Option>
<OptionCd>SUB</OptionCd>
<OptionValue>N</OptionValue>
</Option>
</AdjusterParty>
The xslt is:
<w:p >
<w:r>
<w:t>
<xsl:value-of select ="OptionCd"/>
</w:r>
</w:p>
The requirement is based on the parameter value passed in the function I would either populate the OptionCD SAL or SUB. So how to set the value in OptionCD.
The C# code is
function node(int count)
{
}
is there something like based on the count, I can tell the xslt to fetch that node, say count is
1, then could I let the xslt know it need to print the first OptionCd and so on.
Thanks
Updated the code which I use to read the xml and xslt from a template document and then generates the word document with the values.
string rootPath = #"C:\ExampleWordProcessingML\Docs";
string xmlDataFile = rootPath + #"\Original.xml";
string xsltFile = rootPath + #"\Transactions.xslt";
string templateDocument = rootPath + #"\Transactions.docx";
string outputDocument = rootPath + #"\MyTransactions.docx";
//Create a writer for the output of the Xsl Transformation.
StringWriter stringWriter = new StringWriter();
XmlWriter xmlWriter = XmlWriter.Create(stringWriter);
//Create the Xsl Transformation object.
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(xsltFile);
//Transform the xml data into Open XML 2.0 Wordprocessing format.
transform.Transform(xmlDataFile, xmlWriter);
//Create an Xml Document of the new content.
XmlDocument newWordContent = new XmlDocument();
newWordContent.LoadXml(stringWriter.ToString());
//Copy the Word 2007 source document to the output file.
System.IO.File.Copy(templateDocument, outputDocument, true);
//Use the Open XML SDK version 2.0 to open the output
// document in edit mode.
using (WordprocessingDocument output =
WordprocessingDocument.Open(outputDocument, true))
{
//Using the body element within the new content XmlDocument
// create a new Open Xml Body object.
Body updatedBodyContent =
new Body(newWordContent.DocumentElement.InnerXml);
//Replace the existing Document Body with the new content.
output.MainDocumentPart.Document.Body = updatedBodyContent;
//Save the updated output document.
output.MainDocumentPart.Document.Save();
}

You need to define a parameter in your XSLT, inside the xsl:stylesheet element:
<xsl:param name="count" select="1" />
And then you can use something like this in your XSLT:
<xsl:value-of select="/AdjusterParty/Option[$count]/OptionCd"/>
although this might be a bit safer, in case count was passed in as a string value:
<xsl:value-of select="/AdjusterParty/Option[number($count)]/OptionCd"/>
In order to pass the count value into your XSLT, you can do this:
// Create the XsltArgumentList.
XsltArgumentList argList = new XsltArgumentList();
argList.AddParam("count", "", count);
//Transform the xml data into Open XML 2.0 Wordprocessing format.
transform.Transform(xmlDataFile, argList, xmlWriter);

is there something like based on the count, I can tell the xslt to
fetch that node, say count is 1, then could I let the xslt know it
need to print the first OptionCd and so on.
In XSLT, you can use:
<xsl:value-of select="/AdjusterParty/Option[2]/OptionCd"/>
to return the value "SUB" from your XML example. If you pass a parameter named "count" to the stylesheet at runtime, then
<xsl:value-of select="/AdjusterParty/Option[$count]/OptionCd"/>
will select the n-th Option node to get the OptionCd value from (where n = $count parameter).
--
Note: what you posted as XSLT is not.

Related

Appending to specific location in XML file

I'm trying to append raw XML data part of a List<string> to a XML file as follows:
XmlDocument doc = new XmlDocument();
XmlNode docnode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docnode);
doc.AppendChild(doc.CreateProcessingInstruction("xml-stylesheet", "type='text/xsl' href=''"));
XmlElement Ver = doc.CreateElement("Run");
Ver.SetAttribute("version", "3.0");
XmlElement elem = doc.CreateElement("List");
elem.SetAttribute("Name", ObjectName_string);
doc.AppendChild(Ver);
doc.DocumentElement.AppendChild(elem);
doc.Save(#"1.xml");
List<string> data = Event_table.Rows.OfType<DataRow>().Select(dr=>dr.Field<string>(0)).ToList();
using (var writer = new StreamWriter(#"1.xml", append:true))
{
writer.WriteLine("<cmList>");
foreach (var row in data)
{
writer.WriteLine(row);
}
writer.WriteLine("</cmList>");
}
XML FILE: This is how the final result should be
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type='text/xsl' href=''?>
<Run version="3.0" startTime="3.0" endTime="3.0">
<List Name="ABCD" />
<cmList>
**// My Raw data should come here from List<string> data**
</cmList>
</Run>
How can I append the RAW XML data List<string> data in between the element <cmList> I tried doeing a write.BaseStream.Seek but that gives me an error:
Unable seek backward to overwrite data that previously existed in a
file opened in Append mode.
I wrote a blog many moons ago that specifically addressed how to get data from a DataSet/DataTable into XML (for use in Excel specifically). It appears that this may help you. Just exclude the instructions that tell the XML file to open in Excel. It is in VB.NET, so you will have to use the analogous C# code.
DataSet to XML Blog
Maybe I don't understand something in your example but why don't you append raw data before saving xml to file?
XmlDocument doc = new XmlDocument();
XmlNode docnode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docnode);
doc.AppendChild(doc.CreateProcessingInstruction("xml-stylesheet", "type='text/xsl' href=''"));
XmlElement Ver = doc.CreateElement("Run");
Ver.SetAttribute("version", "3.0");
XmlElement elem = doc.CreateElement("List");
elem.SetAttribute("Name", ObjectName_string);
XmlElement cmList = doc.CreateElement("cmList");
List<string> data = Event_table.Rows.OfType<DataRow>().Select(dr => dr.Field<string>(0)).ToList();
StringBuilder builder = new StringBuilder();
foreach (var row in data)
{
builder.AppendLine(row);
}
cmList.Value = builder.ToString();
elem.AppendChild(cmList);
Ver.AppendChild(elem);
doc.AppendChild(Ver);
doc.Save(#"1.xml");
// do something with file 1.xml

How to get data from an XML File in C# using XMLDocument class?

Good Evening All, and happy weekend!.
I have been trying all day to understand how to parse my simple XML file so I can understand it enough to write a personal project I want to work on.
I have been reading articles on this site and others but cannot get past where I am :(
My XML Document is ...
<XML>
<User>
<ID>123456789</ID>
<Device>My PC</Device>
</User>
<History>
<CreationTime>27 June 2013</CreationTime>
<UpdatedTime>29 June 2013</UpdatedTime>
<LastUsage>30 June 2013</LastUsage>
<UsageCount>103</UsageCount>
</History>
<Configuration>
<Name>Test Item</Name>
<Details>READ ME</Details>
<Enabled>true</Enabled>
</Configuration>
</XML>
I am trying to get the value in the details element (READ ME). Below is my code
// Start Logging Progress
Console.WriteLine("Test Application - XML Parsing and Creating");
Console.ReadKey();
// Load XML Document
XmlDocument MyDoc = new XmlDocument(); MyDoc.Load(#"E:\MyXML.XML");
// Select Node
XmlNode MyNode = MyDoc.SelectSingleNode("XML/Configuration/Details");
// Output Node Value
Console.WriteLine(String.Concat("Details: ", MyNode.Value));
// Pause
Console.ReadKey();
My console application is running and outputing "Target: " but not giving me the detail within the element.
Can somebody see why this is happening, and perhaps give me advice if I am completely off the wheel? I have no previous knowledge in reading XML files; hence where I am now :)
Thanks! Tom
With the your XPATH expression
// Select Node
XmlNode MyNode = MyDoc.SelectSingleNode("XML/Configuration/Details");
your are selection an element so the type of the MyNode will be XmlElement but the Value of an XmlElement is always null (see on MSDN) so you need to use XmlElement.InnerText or XmlElement.InnerXml isntead.
So the changed your code to
// Output Node Value
Console.WriteLine(String.Concat("Details: ", MyNode.InnerText));
Or you can select the content of an element with using the XPATH text() function, in this case MyNode will be XmlText where you get its value with Value:
// Select Node
XmlNode MyNode = MyDoc.SelectSingleNode("XML/Configuration/Details/text()");
// Output Node Value
Console.WriteLine(String.Concat("Details: ", MyNode.Value));
As a sidenote if you are anyway learning XML manipulation in C# you should check out LINQ to XML which is another/newer way to working with XML in C#.
Just for interest, a little-known "simple" syntax is this:
XmlDocument myDoc = new XmlDocument();
myDoc.Load(#"D:\MyXML.XML");
string details = myDoc["XML"]["Configuration"]["Details"].InnerText;
Note that this (and the XPath approach) could go pop if your XML doesn't conform to the structure you're expecting, so you'd ideally put some validation in there as well.
U can use Xpath library for that (u must include "System.Xml.XPath"):
XmlDocument document = new XmlDocument();
document.Load("MyXml.xml");
XPathNavigator navigator = document.CreateNavigator();
foreach (XPathNavigator nav in navigator.Select("//Details"))
{
Console.WriteLine(nav.Value);
}
the above code iterate over every node called (Details) extracting information and print it.
If you want to retrieve a particular value from an XML file
XmlDocument _LocalInfo_Xml = new XmlDocument();
_LocalInfo_Xml.Load(fileName);
XmlElement _XmlElement;
_XmlElement = _LocalInfo_Xml.GetElementsByTagName("UserId")[0] as XmlElement;
string Value = _XmlElement.InnerText;
Value contains the text value

Xml within an Xml

I basically want to know how to insert a XmlDocument inside another XmlDocument.
The first XmlDocument will have the basic header and footer tags.
The second XmlDocument will be the body/data tag which must be inserted into the first XmlDocument.
string tableData = null;
using(StringWriter sw = new StringWriter())
{
rightsTable.WriteXml(sw);
tableData = sw.ToString();
}
XmlDocument xmlTable = new XmlDocument();
xmlTable.LoadXml(tableData);
StringBuilder build = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(build, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
writer.WriteStartElement("dataheader");
//need to insert the xmlTable here somehow
writer.WriteEndElement();
}
Is there an easier solution to this?
Use importNode feature in your document parser.
You can use this code based on CreateCDataSection method
// Create an XmlCDataSection from your document
var cdata = xmlTable.CreateCDataSection("<test></test>");
XmlElement root = xmlTable.DocumentElement;
// Append the cdata section to your node
root.AppendChild(cdata);
Link : http://msdn.microsoft.com/fr-fr/library/system.xml.xmldocument.createcdatasection.aspx
I am not sure what you are really looking for but this can show how to merge two xml documents (using Linq2xml)
string xml1 =
#"<xml1>
<header>header1</header>
<footer>footer</footer>
</xml1>";
string xml2 =
#"<xml2>
<body>body</body>
<data>footer</data>
</xml2>";
var xdoc1 = XElement.Parse(xml1);
var xdoc2 = XElement.Parse(xml2);
xdoc1.Descendants().First(d => d.Name == "header").AddAfterSelf(xdoc2.Elements());
var newxml = xdoc1.ToString();
OUTPUT
<xml1>
<header>header1</header>
<body>body</body>
<data>footer</data>
<footer>footer</footer>
</xml1>
You will need to write the inner XML files in CDATA sections.
Use writer.WriteCData for such nodes, passing in the inner XML as text.
writer.WriteCData(xmlTable.OuterXml);
Another option (thanks DJQuimby) is to encode the XML to some XML compatible format (say base64) - note that the encoding used must be XML compatible and that some encoding schemes will increase the size of the encoded document (base64 adds ~30%).

XML Transformation Error - The specified node cannot be inserted as the valid child of this node, because the specified node is the wrong type

I keep getting the above error when trying to transform XML that's generated in code to a plain text output.
My .xsl file is:
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>Test</xsl:text>
</xsl:template>
</xsl:transform>
And my C# method code is:
if( !File.Exists( xslPath ) )
throw new Exception( "XSL File (" + xslPath + ") does not exist" );
XslTransform docXsl = new XslTransform();
docXsl.Load( xslPath );
XmlDocument docXml = new XmlDocument();
XmlElement emailNode = docXml.CreateElement("Email");
docXml.AppendChild( emailNode );
XmlResolver xres = null;
XmlReader xr = docXsl.Transform( docXml, null, xres );
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load( xr );
return xmldoc.OuterXml;
The XML generated is very simple, just
<Email/>
If I remove the node from the XSL, then I do not get the error.
I cannot find out why this is happening. Any help would be greatly appreciated.
Thanks
What is it that you want to achieve? If you use .NET 2.0 or later you shouldn't use XslTransform at all, rather XslCompiledTransform. And if you want plain text output then it does not make any sense to try to load that transformation result into an XmlDocument as that object model is meant to contain a well-formed XML document and not some plain text. So that is the reason I think you get the error, your transformation result is plain text with a single text node and then you try to load that single text node into an XmlDocument which looks for a root element which does not exist.
Consider to tell is which .NET version you use/target and what kind of output you want (e.g. plain text file or string with the transformation result), then we can point you to the right overload of the Transform method of XslCompiledTransform http://msdn.microsoft.com/en-us/library/system.xml.xsl.xslcompiledtransform.transform.aspx to get that result.
For instance if your transformation input is an XmlDocument or other object implementing IXPathNavigable you can use http://msdn.microsoft.com/en-us/library/ms163435.aspx to transform to a StringWriter to get an String result as follows:
XslCompiledTransform proc = new XslCompiledTransform();
proc.Load(xslPath);
string result;
using (StringWriter sw = new StringWriter())
{
proc.Transform(docXml, null, sw);
result = sw.toString();
}
return result;

Selecting values from an xml document object with XPATH in code behind (c#)

I am trying to select specific values from a xml document using XPath. The xml is stored into a string varibale "tmp". This xml is the result of a query performed on a external API.
sample XML contents:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Results>
<Checks>
<Check id="wbc">
<Linespeed>6000 </Linespeed>
<Provider>BT WBC </Provider>
</Check>
<Check id="adsl">
<Linespeed>2048 </Linespeed>
<Provider>BT ADSL </Provider>
</Check>
</Checks>
</Results>
Using XPATH in code behind I want to be able to select the and only for id=adsl, then store the value in a string variable for later use. I want to achieve this withouth the use of a separate xslt stylesheet.
Here is the code I have written for this but I am getting an error:
//Creating an XPATH epression
String strExpression1;
strExpression1 = "Results/Checks/Check[#id = 'adsl']/Linespeed";
//Loading the xml document
XmlDocument doc;
doc = new XmlDocument();
doc.LoadXml(tmp);
//Create an XmlNamespaceManager to resolve the default namespace.
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("bk", "urn:schemas-microsoft-com:xslt");
//Selecting Linespeed from Check id='adsl'
XmlNode Check;
XmlElement root = doc.DocumentElement;
Check = root.SelectSingleNode(strExpression1, nsmgr);
//Assigning the the results of the XPATH expression to the variable Linespeedval
string Linespeedval = Check.ToString();
//Adding a control to display the xpath results of the "tmp" xml objectt
AvailabilityCheckerResults2.Controls.Add(new LiteralControl(Linespeedval));
Any assistance will be greately appreciated! Thanks in advance!
strExpression1 = "/Results/Checks/Check[#id = 'adsl']/Linespeed";
//or strExpression1 = "//Checks/Check[#id = 'adsl']/Linespeed";
//doc has no namespace
Check = root.SelectSingleNode(strExpression1);
....
string Linespeedval = Check.InnerText;
Take a look at this article. It has step by step instruction to parse xml using xpath.
How to query XML with an XPath expression by using Visual C#

Categories

Resources