Hi I've got several XSLT 2.0 files. I need to transform these with C#..
I use the following code I got from this site: http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=63
public bool Transform(string XMLPath, string XSLPath, string newXMLname){
try{
XPathDocument myXMLPath = new XPathDocument(XMLPath); //load the Xml doc
XslCompiledTransform myXSLTrans = new XslCompiledTransform();
myXSLTrans.Load(XSLPath); //load the Xsl
XmlTextWriter myWriter = new XmlTextWriter(newXMLname, null); //create the output stream
myXSLTrans.Transform(myXMLPath, null, myWriter); //do the actual transform of Xml ---> fout!!??
myWriter.Close() ;
return true;
}catch(Exception e){
return false;
}
}
But it doesn't work.. I think it's because I use XSLT version 2.0.
Is there a code/way to do this? Because there is no way to change my XSLT files to version 1.0...
Thanks in advance!
The two XSLT 2.0 processors that are designed to work in the .NET environment are Saxon.NET and XQSharp.
The XslCompiledTransform and XslTransform processors that come as part of .NET only implement XSLT 1.0.
Natively .Net Framework doesn't support XSLT 2.0. I would suggest to use XSLT 1.0, but if you can't, then use third party component, for example Saxon.
Related
I'm using Asp.net to make a transformation in XML using XSLT by C# code as follows.
protected void Page_Load(object sender, EventArgs e)
{
string strXSLTFile = Server.MapPath("EmployeeXSLT.xslt");
string strXMLFile = Server.MapPath("Employess.xml");
XmlReader reader = XmlReader.Create(strXMLFile);
XslCompiledTransform objXSLTransform = new XslCompiledTransform();
objXSLTransform.Load(strXSLTFile);
StringBuilder htmlOutput = new StringBuilder();
TextWriter htmlWriter = new StringWriter(htmlOutput);
objXSLTransform.Transform(reader, null, htmlWriter);
ltRss.Text = htmlOutput.ToString();
reader.Close();
}
and for full example with (Asp.net, XSLT and XML) follow this link
Doing XSLT Transformation in ASP.Net
Now I need to change the Xpath value of the XSLT file. I conduct a search to find out how to do this. finally, I found tow concepts to affect the XSLT file. However, no examples provided on how to apply these concepts to change XSLT "match" or "for-each select" where I need to add a variable for changeable Xpath.
the first one:
using so-called Dynamic XPath Evaluation
the second one:
using so-called XSLT Parameters
I need to know how can I change the XSLT "match" using C# code to meet specific selection.
You can use an XPathNavigator to compile an xpath expression then go through the matching nodes in your c# code.
// Create and compile the XPathExpression
string _xPathExpression = "/Data/Client[Id = 123]";
XPathExpression exprXPathCompiled
= xmlDocInputNavigator.Compile(_xPathExpression);
// Load the Stylesheet
XsltSettings settings = new XsltSettings();
XslCompiledTransform xsltTemplate = new XslCompiledTransform();
xsltTemplate.Load(_stylesheetFileName, settings, new XmlUrlResolver());
// Create an iterator to loop through the matching nodes
XPathNodeIterator iterator = xmlDocInputNavigator.Select(exprXPathCompiled);
StreamWriter fileOutput = null;
while (iterator.MoveNext())
{
//...
XPathNavigator docs
It would help if you explained the problem you are trying to solve, rather than the method you want to use to solve it.
An XSLT stylesheet is an XML document, so you can always transform it using XSLT (with a so-called "meta-stylesheet").
With XSLT 3.0 (not available from Microsoft, but available to C# users via the Saxon library) you can parameterize a stylesheet using "shadow attributes", for example
<xsl:template _match="{$pattern}"/>
where $pattern is a stylesheet parameter declared as
<xsl:param name="pattern" static="yes"/>
so that a value can be supplied by the calling application.
You can also of course use conventional (run-time) parameters in a match pattern of the form
<xsl:template match="*[#id=$requestedId]"/>
XSLT 1.0 doesn't allow parameter references in match patterns, unfortunately. Some XSLT 1.0 processors don't enforce this restriction but I don't know if that's the case for the Microsoft processor.
In ASP .NET Core, I am trying to add some XML-Element with attributes to an existing XML file.
In ASP NET 4.5, I would have used the code below to make this working:
string path = Server.MapPath("~/Data/foo.xml");
XDocument xdoc = XDocument.Load(path);
//Do stuff with XElement and XAttributes...
xdoc.Save(path);
But with ASP .NET Core, I cannot use Server.MapPath(), So I get the complete path with IHostingEnvironment instead: (Read more here )
Running the complete code below on ASP .NET Core will result in "Cannot convert from String to System.IO.Stream" when trying to run "xdoc.Save(pathToDataFile);" ??
var contentRoot = hostingEnvironment.ContentRootPath;
string pathToDataFile = contentRoot + "\\Data\\foo.xml";
XDocument xdoc = XDocument.Load(pathToDataFile);
//Do stuff with XElement and XAttributes...
xdoc.Save(pathToDataFile);
Why is "xdoc.Save()" not working in ASP .NET Core but working fine in .NET 4.5?
APIs available in .NET Core are a subset of the ones available in the full .NET framework. In some areas, you'll find that pretty much everything from .NET 4.5 is available in .NET Core, but that's not always the case.
In your case, if you have a look with Visual Studio at what overloads of the Save method are available, you'll find these ones:
public void Save(Stream stream);
public void Save(TextWriter textWriter);
public void Save(XmlWriter writer);
public void Save(Stream stream, SaveOptions options);
public void Save(TextWriter textWriter, SaveOptions options);
The reason why you have a compilation error is now pretty clear. In .NET Core, there's no overload accepting a string that defines the file path where the document should be saved.
You'll have to create a write-enabled Stream pointing to the desired path first and pass that Stream to the Save method. You can have a look at the full .NET framework implementation for reference.
I had the same issue, and FileStream works for me.
FileStream fileStream = new FileStream("file.xml", FileMode.Create);
XmlWriterSettings settings = new XmlWriterSettings() { Indent = true};
XmlWriter writer = XmlWriter.Create(fileStream, settings);
Remember to use the following lines of code to prevent the file from being truncated.
writer.Flush();
fileStream.Flush();
I want to port some C# code with the full .NET Framework as target into Silverlight-compatible code.
One of the problems I've encountered is that in the original code, an instance of XmlTextReader is used:
var xmlReader = new XmlTextReader(streamReader) {
WhitespaceHandling = WhitespaceHandling.None,
xmlResolver = null
};
However, in Silverlight, only XmlReader is available. Therefore, I'm wondering how to convert from the original XmlTextReader.
In the documentation of XmlTextReader, it's stated that
In the .NET Framework version 2.0 release, the recommended practice is to create XmlReader instances using the XmlReader.Create method. This allows you to take full advantage of the new features introduced in this release. For more information, see Creating XML Readers.
This supports the theory that a port should be possible.
How does the initialization of a XmlReader has to look like to process the XML files exactly the same as the XmlTextReader instance mentioned above?
var settings = new XmlReaderSettings {
...
}
var xmlReader = XmlReader.Create(streamReader, settings);
Its not possible to replicate this entirely. Silverlight XmlReader does not support ignoring significant whitespace. This therefore is close:-
var settings = new XmlReaderSettings { IgnoreWhitespace = true, XmlResolver = null };
I think you should just go with that and see what happens.
I'm trying to build an executable which applies XSLT transforms onto a large number XML files. Now my problem is that I'd like to include/refer to the XSLT file stored with my C# VS 2010 solution, so that when I repackage this for another machine, I don't have to copy across the XSLT files. Is this possible?
string xslFile = "C:\template.xslt";
string xmlFile = "C:\\file00324234.xml";
string htmlFile = "C:\\output.htm";
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(xslFile);
transform.Transform(xmlFile, htmlFile);
You can include the XSLT as an Embedded Resource into your assembly as described here:
How to embed an XSLT file in a .NET project to be included in the output .exe?
Once embedded, you can use the transform as follows:
using(Stream stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("YourAssemblyName.filename.xslt"))
{
using (XmlReader reader = XmlReader.Create(stream))
{
XslCompiledTransform transform = new XslCompiledTransform ();
transform.Load(reader);
// use the XslTransform object
}
}
In other words, is there a faster, more concise way of writing the following code:
//Create an object for performing XSTL transformations
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(HttpContext.Current.Server.MapPath("/xslt/" + xsltfile.Value), new XsltSettings(true, false), new XmlUrlResolver());
//Create a XmlReader object to read the XML we want to format
//XmlReader needs an input stream (StringReader)
StringReader sr = new StringReader(node.OuterXml);
XmlReader xr = XmlReader.Create(sr);
//Create a StringWriter object to capture the output from the XslCompiledTransform object
StringWriter sw = new StringWriter();
//Perform the transformation
xslt.Transform(xr, null, sw);
//Retrieve the transformed XML from the StringWriter object
string transformedXml = sw.ToString();
UPDATE (thanks for all the answers so far!):
Sorry for my vagueness: by "faster" and more "concise" I mean, am I including any unnecessary steps? Also, I would love a more "readable" solution if someone has one. I use this code in a small part of a web application I'm developing, and I'm about to move it to a large part of the application, so I want to make sure it's as neat as can be before I make the move.
Also, I get the XML from a static class (in a separate data access class library) which communicates with a database. I also manipulate the transformed XML string before shipping it off to a web page. I'm not sure if the input/response streams are still viable in this case.
One more thing: the XML and the XSLT supplied may change (users of the application can make changes to both), so I think I would be forced to compile each time.
Here's code I did for my ASP.NET, which is very similar to yours:
XDocument xDoc = XDocument.Load("output.xml");
XDocument transformedDoc = new XDocument();
using (XmlWriter writer = transformedDoc.CreateWriter())
{
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(XmlReader.Create(new StreamReader("books.xslt")));
transform.Transform(xDoc.CreateReader(), writer);
}
// now just output transformedDoc
If you have a large XSLT you can save the overhead of compiling it at runtime by compiling the XSLT into a .NET assembly when you build your project (e.g. as a post-build step). The compiler to do this is called xsltc.exe and is part of Visual Studio 2008.
In order to load such a pre-compiled XSLT you will need .NET Framework 2.0 SP1 or later installed on your server (the feature was introduced with SP1).
For an example check Anton Lapounov's blog article:
XSLTC — Compile XSLT to .NET Assembly
If pre-compiling the XSLT is not an option you should consider caching the XslCompiledTransform after it is loaded so that you don't have to compile it everytime you want to execute the transform.
Don't have time to do a full example, but some notes:
XML is not the same as System.String. Get it from the class library as XDocument or XmlDocument; finish with it as XDocument or XmlDocument.
You can use the ASP.NET Cache to store the compiled XSL, with a cache dependency on when the .XSLT file changes.
Don't convert the XML to a string then back to XML. Use node.CreateNavigator().ReadSubTree().
Similarly, use XPathNavigator.AppendChild to get an XmlWriter that will write into an XML Document.
Since you mention ASP.NET, the question is whether you can use the response stream directly for your transform output and whether you can use the input stream directly if it is a POST...
I'd rewrite the code like this:
string path = HttpContext.Current.Server.MapPath("/xslt/" + xsltfile.Value);
XmlReader reader = CreateXmlReader(node.OuterXml);
string transformedXml = Transform(path, reader);
private XmlReader CreateXmlReader(string text)
{
StringReader reader = new StringReader(text);
return XmlReader.Create(reader);
}
private string Transform(string xsltPath, XmlReader source)
{
XsltCompiledTransform transformer = new XsltCompiledTransform();
transformer.Load(
xsltPath,
new XsltSettings(true, false),
new XmlUrlResolver());
StringWriter writer = new StringWriter();
transformer.Transform(source, null, writer);
return writer.ToString();
}
The reason why I'd rewrite the code like this is because each block of code now has one and only one purpose. This makes it easier to read and understand. Additionally, the code requires less comments because a lot of information can be inferred from the names of the function and its parameters.