I want to apply an XSLT Stylesheet to an XML Document using C# and write the output to a File.
I found a possible answer here: http://web.archive.org/web/20130329123237/http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=63
From the article:
XPathDocument myXPathDoc = new XPathDocument(myXmlFile) ;
XslTransform myXslTrans = new XslTransform() ;
myXslTrans.Load(myStyleSheet);
XmlTextWriter myWriter = new XmlTextWriter("result.html",null) ;
myXslTrans.Transform(myXPathDoc,null,myWriter) ;
Edit:
But my trusty compiler says, XslTransform is obsolete: Use XslCompiledTransform instead:
XPathDocument myXPathDoc = new XPathDocument(myXmlFile) ;
XslCompiledTransform myXslTrans = new XslCompiledTransform();
myXslTrans.Load(myStyleSheet);
XmlTextWriter myWriter = new XmlTextWriter("result.html",null);
myXslTrans.Transform(myXPathDoc,null,myWriter);
Based on Daren's excellent answer, note that this code can be shortened significantly by using the appropriate XslCompiledTransform.Transform overload:
var myXslTrans = new XslCompiledTransform();
myXslTrans.Load("stylesheet.xsl");
myXslTrans.Transform("source.xml", "result.html");
(Sorry for posing this as an answer, but the code block support in comments is rather limited.)
In VB.NET, you don't even need a variable:
With New XslCompiledTransform()
.Load("stylesheet.xsl")
.Transform("source.xml", "result.html")
End With
Here is a tutorial about how to do XSL Transformations in C# on MSDN:
http://support.microsoft.com/kb/307322/en-us/
and here how to write files:
http://support.microsoft.com/kb/816149/en-us
just as a side note: if you want to do validation too here is another tutorial (for DTD, XDR, and XSD (=Schema)):
http://support.microsoft.com/kb/307379/en-us/
i added this just to provide some more information.
This might help you
public static string TransformDocument(string doc, string stylesheetPath)
{
Func<string,XmlDocument> GetXmlDocument = (xmlContent) =>
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xmlContent);
return xmlDocument;
};
try
{
var document = GetXmlDocument(doc);
var style = GetXmlDocument(File.ReadAllText(stylesheetPath));
System.Xml.Xsl.XslCompiledTransform transform = new System.Xml.Xsl.XslCompiledTransform();
transform.Load(style); // compiled stylesheet
System.IO.StringWriter writer = new System.IO.StringWriter();
XmlReader xmlReadB = new XmlTextReader(new StringReader(document.DocumentElement.OuterXml));
transform.Transform(xmlReadB, null, writer);
return writer.ToString();
}
catch (Exception ex)
{
throw ex;
}
}
I would like to share this small piece of code which reads from Database and transforms using XSLT. On the top I also have used xslt-extensions which makes it little different than others.
Note: This is just a draft code and may need cleanup before using in production.
var schema = XDocument.Load(XsltPath);
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
using (var command = new SqlCommand(Sql, connection))
{
var reader = command.ExecuteReader();
var dt = new DataTable(SourceNode);
dt.Load(reader);
string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Environment.NewLine;
using (var stringWriter = new StringWriter())
{
dt.WriteXml(stringWriter, true);
xml += stringWriter.GetStringBuilder().ToString();
}
XDocument transformedXml = new XDocument();
var xsltArgumentList = new XsltArgumentList();
xsltArgumentList.AddExtensionObject("urn:xslt-extensions", new XsltExtensions());
using (XmlWriter writer = transformedXml.CreateWriter())
{
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(schema.CreateReader());
xslt.Transform(XmlReader.Create(new StringReader(xml)), xsltArgumentList, writer);
}
var result = transformedXml.ToString();
}
}
XsltPath is path to your xslt file.
ConnectionString constant is pointing to your database.
Sql is your query.
SourceNode is node of each record in source xml.
Now the interesting part, please note the use of urn:xslt-extensions and new XsltExtensions() in above code. You can use this if need some complex computation which may not be possible in xslt. Following is a simple method to format date.
public class XsltExtensions
{
public string FormatDate(string dateString, string format)
{
DateTime date;
if (DateTime.TryParse(dateString, out date))
return date.ToString(format);
return dateString;
}
}
In XSLT file you can use it as below;
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ext="urn:xslt-extensions">
...
<myTag><xsl:value-of select="ext:FormatDate(record_date, 'yyyy-MM-dd')"/></myTag>
...
</xsl:stylesheet>
Related
I am trying to create a xml document of following format:
<![CDATA[<Caption xmlns="http:happy.x.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.happybus.tv/yy/happybus.xsd">
<TemplateID>xxxxx</TemplateID>
<CaptionOptions>
<CaptionField>
<Field>xxx</Field>
<Text>xxx</Text>
</CaptionField>
<CaptionField>
<Field>xxxx</Field>
<Text>""</Text>
</CaptionField>
</CaptionOptions>
</Caption>]]>
Here is the code that I wrote
XmlDocument xml2 = new XmlDocument();
XmlElement e = xml2.CreateElement("Caption");
e.InnerText ="Hello";
XmlElement template = xml2.CreateElement("TemplateID");
template.InnerText = "#TemplateID";
XmlElement captionOptions = xml2.CreateElement("CaptionOptions");
XmlElement captionField = xml2.CreateElement("CaptionField");
XmlElement fieldId = xml2.CreateElement("FieldID");
fieldId.InnerText = "#FieldID";
XmlElement textstring = xml2.CreateElement("TextString");
textstring.InnerText = "#TextString";
captionField.AppendChild(fieldId);
captionField.AppendChild(textstring);
captionOptions.AppendChild(captionField);
e.AppendChild(template);
e.AppendChild(captionOptions);
xml2.AppendChild(e);
StringWriter string_writer2 = new StringWriter();
XmlTextWriter xml_text_writer2 = new XmlTextWriter(string_writer2);
xml_text_writer2.Formatting = Formatting.Indented;
xml2.WriteTo(xml_text_writer2); // xml is your XmlDocument
string formattedXml2 = string_writer2.ToString();
Console.Write(formattedXml2);
I have tried a similar example with different XML doc but it clearly work, I even tried debugging but it is not getting formatted.
Have you tried using the XDocument and related classes? I find they make manual construction of xml easier and more intuitive since the code looks very similar to the xml. The ToString method seems to output the xml formatted the way you want:
void Main()
{
var xDoc = new XDocument
(
new XElement("Parent",
new XElement("TemplateID", "xxxxx"),
new XElement("CaptionOptions",
new XElement("CaptionField",
new XElement("Field", "xxx"),
new XElement("Text", "xxx")
),
new XElement("CaptionField",
new XElement("Field", "xxxx"),
new XElement("Text", "")
)
)
)
);
Console.WriteLine(xDoc.ToString());
//To enclose the xml in a CDATA, you could use:
var cData = new XCData(xDoc.ToString());
Console.WriteLine(cData.ToString());
}
As described in the original question, using the Saxon xsl processor with the input xml and xsl file from the link XSLT Error produced "Ambiguous rule match", the Saxon processor generates the ambiguous rule match error/warning.
I am trying to produce the same or a similar message via the .Net xsl processor, but I have been unsuccessful. If anyone knows if this is possible, any advice would be greatly appreciated.
Below is the code snippet I am using:
XmlUrlResolver resolver = new XmlUrlResolver();
XslCompiledTransform xsltTrans = new XslCompiledTransform();
XsltSettings xslSettings = new XsltSettings();
xslSettings.EnableDocumentFunction = true;
xslSettings.EnableScript = true;
xsltTrans.Load(xslFile, xslSettings, resolver);
XmlWriterSettings writerSettings = xsltTrans.OutputSettings;
_xmlReaderSettings.XmlResolver = resolver;
try
{
using (XmlWriter xw = XmlWriter.Create(outFile, writerSettings))
{
using (XmlReader xr = XmlReader.Create(inputfile, _xmlReaderSettings))
{
if (_xslArgs != null)
{
xsltTrans.Transform(xr, _xslArgs, xw);
}
else
{
xsltTrans.Transform(xr, xw);
}
}
}
}
catch (XsltException xslex)
{
sResult = string.Format("Error: XSLT Exception - {0} - Line {1} - {2}", Path.GetFileName(xslFile), xslex.LineNumber, xslex.Message);
}
I am trying append a serialized object to an existing xml file beneath the root element, which I thought would be simple but is proving to be a little challenging.
The problem is in the AddShortcut method but I added some more code for completeness.
I believe what I need to do is:
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument.
The trouble is in steps 2 and 3. I have tried different variations but I think using XPathNavigator somehow to find the "root" node to append under is a step in the right direction.
I have also looked at almost every question on Stack Overflow on the subject.
Any suggestions welcome. Here is my code
class XmlEngine
{
public string FullPath { get; set; } // the full path to the xmlDocument
private readonly XmlDocument xDoc;
public XmlEngine(string fullPath, string startElement, string[] rElements)
{
FullPath = fullPath;
xDoc = new XmlDocument();
CreateXmlFile(FullPath, startElement, rElements);
}
public void CreateXmlFile(string path, string startElement, string[] rElements)
{
try
{
if (!File.Exists(path))
{
// create a txt writer
XmlTextWriter wtr = new XmlTextWriter(path, System.Text.Encoding.UTF8);
// make sure the file is well formatted
wtr.Formatting = Formatting.Indented;
wtr.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
wtr.WriteStartElement(startElement);
wtr.Close();
// write the top level root elements
writeRootElements(path, rElements);
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
Console.WriteLine("Could not create file: " + path);
}
}
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
rootNode = xDoc.AppendChild(xDoc.CreateElement("Shortcuts"));
var serializer = new XmlSerializer(s.GetType());
using (var writer = new StreamWriter(FullPath, true))
{
XmlWriterSettings ws = new XmlWriterSettings();
ws.OmitXmlDeclaration = true;
serializer.Serialize(writer, s);
}
xDoc.Save(FullPath);
}
}
This code sample worked for me:
xml:
<?xml version="1.0" encoding="UTF-8"?>
<Launchpad>
<Shortcuts>
<Shortcut Id="1">
<Type>Folder</Type>
<FullPath>C:\SomePath</FullPath>
<Name>SomeFolderName</Name>
</Shortcut>
</Shortcuts>
</Launchpad>
Method:
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
var rootNode = xDoc.GetElementsByTagName("Shortcuts")[0];
var nav = rootNode.CreateNavigator();
var emptyNamepsaces = new XmlSerializerNamespaces(new[] {
XmlQualifiedName.Empty
});
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(s.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, s, emptyNamepsaces);
writer.Close();
}
xDoc.Save(FullPath);
}
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument
So:
public void AddShortcut(Shortcut s)
{
// 1. load existing xml
xDoc.Load(FullPath);
// 2. create an XML node from object
XmlElement node = SerializeToXmlElement(s);
// 3. append that node to Shortcuts node under XML root
var shortcutsNode = xDoc.CreateElement("Shortcuts")
shortcutsNode.AppendChild(node);
xDoc.DocumentElement.AppendChild(shortcutsNode);
// 4. save changes
xDoc.Save(FullPath);
}
public static XmlElement SerializeToXmlElement(object o)
{
XmlDocument doc = new XmlDocument();
using(XmlWriter writer = doc.CreateNavigator().AppendChild())
{
new XmlSerializer(o.GetType()).Serialize(writer, o);
}
return doc.DocumentElement;
}
This post
I am reading a .docx file using OpenXML in C#. It reads everything correctly but strangely, the content of textbox is being read thrice. What could be wrong? Here is the code to read .docx:
public static string TextFromWord(String file)
{
const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
StringBuilder textBuilder = new StringBuilder();
using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(file, false))
{
// Manage namespaces to perform XPath queries.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("w", wordmlNamespace);
// Get the document part from the package.
// Load the XML in the document part into an XmlDocument instance.
XmlDocument xdoc = new XmlDocument(nt);
xdoc.Load(wdDoc.MainDocumentPart.GetStream());
XmlNodeList paragraphNodes = xdoc.SelectNodes("//w:p", nsManager);
foreach (XmlNode paragraphNode in paragraphNodes)
{
XmlNodeList textNodes = paragraphNode.SelectNodes(".//w:t", nsManager);
foreach (System.Xml.XmlNode textNode in textNodes)
{
textBuilder.Append(textNode.InnerText);
}
textBuilder.Append(Environment.NewLine);
}
}
return textBuilder.ToString();
}
The part of file I am talking about is:
The result is: I read it in a test application like this:
What's wrong here?
I am looking on Internet how keep the carriage return from XML data but I could not find the answer, so I'm here :)
The objective is to write in a file the content of a XML data. So, if the value of the node contains some "\r\n" data, the soft need to write them in file in order to create new line, but it doesn't write, even with space:preserve.
Here is my test class:
XElement xRootNode = new XElement("DOCS");
XElement xData = null;
//XNamespace ns = XNamespace.Xml;
//XAttribute spacePreserve = new XAttribute(ns+"space", "preserve");
//xRootNode.Add(spacePreserve);
xData = new XElement("DOC");
xData.Add(new XAttribute("FILENAME", "c:\\titi\\prout.txt"));
xData.Add(new XAttribute("MODE", "APPEND"));
xData.Add("Hi my name is Baptiste\r\nI'm a lazy boy");
xRootNode.Add(xData);
bool result = Tools.writeToFile(xRootNode.ToString());
And here is my process class:
try
{
XElement xRootNode = XElement.Parse(xmlInputFiles);
String filename = xRootNode.Element(xNodeDoc).Attribute(xAttributeFilename).Value.ToString();
Boolean mode = false;
try
{
mode = xRootNode.Element(xNodeDoc).Attribute(xWriteMode).Value.ToString().ToUpper().Equals(xWriteModeAppend);
}
catch (Exception e)
{
mode = false;
}
String value = xRootNode.Element(xNodeDoc).Value;
StreamWriter destFile = new StreamWriter(filename, mode, System.Text.Encoding.Unicode);
destFile.Write(value);
destFile.Close();
return true;
}
catch (Exception e)
{
return false;
}
Does anybody have an idea?
If you want to preserve cr lf in element or attribute content when saving a XDocument or XElement you can do that by using certain XmlWriterSettings, namely NewLineHandling to Entitize:
string fileName = "XmlOuputTest1.xml";
string attValue = "Line1.\r\nLine2.";
string elementValue = "Line1.\r\nLine2.\r\nLine3.";
XmlWriterSettings xws = new XmlWriterSettings();
xws.NewLineHandling = NewLineHandling.Entitize;
XDocument doc = new XDocument(new XElement("root",
new XAttribute("test", attValue),
elementValue));
using (XmlWriter xw = XmlWriter.Create(fileName, xws))
{
doc.Save(xw);
}
doc = XDocument.Load(fileName);
Console.WriteLine("att value: {0}; element value: {1}.",
attValue == doc.Root.Attribute("test").Value,
elementValue == doc.Root.Value);
In that example the value are preserved in the round trip of saving and loading as the output of the sample is "att value: True; element value: True."
Heres a useful link I found for parsing an Xml string with carraige returns, line feeds in it.
howto-correctly-parse-using-xelementparse-for-strings-that-contain-newline-character-in
It may help those who are parsing an Xml string.
For those who can't be bothered to click it says use an XmlTextReader instead
XmlTextReader xtr = new XmlTextReader(new StringReader(xml));
XElement items = XElement.Load(xtr);
foreach (string desc in items.Elements("Item").Select(i => (string)i.Attribute("Description")))
{
Console.WriteLine("|{0}|", desc);
}