I am trying to generate the following XSL block in my C# application. Can anyone tell me how to to it?
<XSL-Script xmlns:xsl="http://www.w3.org/......">
<xsl:value-of select="$VAR">
</XSL-Script>
I tried to use regular C# XML class, and it removes the xsl: from the tag name, because it thinks xsl: is the namespace. And it also doesn't allow to use "$" in front of VAR for attribute value of "select".
Thanks a lot.
Here is a simple C# program that "generates" a complete XSLT stylesheet and then performs this transformation on a "generated" XML document and outputs the result of the transformation to a file:
using System.IO;
using System.Xml;
using System.Xml.Xsl;
class testTransform
{
static void Main(string[] args)
{
string xslt =
#"<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:variable name='vX' select='1'/>
<xsl:template match='/'>
<xsl:value-of select='$vX'/>
</xsl:template>
</xsl:stylesheet>";
string xml = #"<t/>";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
XmlDocument xslDoc = new XmlDocument();
xslDoc.LoadXml(xslt);
XslCompiledTransform xslTrans = new XslCompiledTransform();
xslTrans.Load(xslDoc);
xslTrans.Transform(xmlDoc, null, new StreamWriter("output.txt"));
}
}
When this application is built and executed it creates a file named "output.txt" and its contents is the expected, correct result from the dynamically generated XSLT transformation:
<?xml version="1.0" encoding="utf-8"?>1
Related
I'd like to use embedded resources in my XSLT file, but while invoking 'document(...)' C# complains that "Error during loading document ..."
I'd like to use defined resources in XSLT file and get them by this: "document('')//my:resources/"...
How can i do that??
ex xsl:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="xslt-gruper-v1.2.xsl" exclude-result-prefixes="my">
<my:resources>
<one>tryb</one>
</my:resources>
<xsl:variable name="res" select="document('')/*/my:resources/("/>
</xsl:stylesheet>
How can i get access to such structure without exceptions in C#? I'll add that during static transform via ex. Opera everything works fine.
<xsl:variable name="res" select="document('')/*/my:resources/("/>
The value of the select attribute is not a syntactically correct XPath expression. Every compliant XSLT processor must raise an error.
Solution:
Correct the above to:
<xsl:variable name="vRes" select="document('')/*/my:resources"/>
If there is still an exception raised, do read about the XsltSettings class.
Then create an instance of XsltSettings with this constructor, like this:
XsltSettings(true, false)
Do not enable scripting -- keep the second argument of the constructor as false.
Below is a more complete code snippet:
// Create the XsltSettings object with document() enabled and script disabled.
XsltSettings settings = new XsltSettings(true,false);
// Create the XslCompiledTransform object and load the style sheet.
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load("sort.xsl", settings, new XmlUrlResolver());
Update: Another possible reason for an error is when the XSLT stylesheet is dynamically created in memory (doesn't come from file). In this case an XSLT processor typically cannot resolve the relative uri in document('').
In this last case the solution is to make the wanted element the content of an xsl:variable and to use the xxx:node-set() extension function to address this element.
After tearing my hair out for an entire day and a half, I finally figured out the solution to an identical issue I was seeing.
My code:
NUnit Test:
[Test]
public void Transform_WhenXslUsesTheDocumentFunction_DoesNotThrow()
{
//Arrange
string testOutputPath = GetTestOutputPath(
nameof(Transform_WhenXslUsesTheDocumentFunction_DoesNotThrow)
);
string inputXsl = TestFilePaths.GetResource("Import\\DocumentImporter.xsl");
XsltSettings xsltSettings = new XsltSettings(true, true);
XmlUrlResolver resolver = new XmlUrlResolver();
XslCompiledTransform xslCompiledTransform = new XslCompiledTransform();
xslCompiledTransform.Load(inputXsl, xsltSettings, resolver);
//Act
TestDelegate testDelegate = () => xslCompiledTransform.Transform(
TestFilePaths.MinimumValidXml
, testOutputPath
);
//Assert
Assert.DoesNotThrow(testDelegate);
}
DocumentImporter.xsl :
<?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:variable name="DocumentPath" select="'./Document.xml'"/>
<xsl:variable name="DocumentXML" select="document($DocumentPath)"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:value-of select="$DocumentXML//Message"/>
</xsl:template>
</xsl:stylesheet>
Document.xml :
<?xml version="1.0" encoding="utf-8" ?>
<Message>Document Message</Message>
Eventually I found this:
https://github.com/dotnet/runtime/issues/26969
Informing me that I needed to use a new .Net Core "Feature" which would allow me to use my external xml document, using the snippet:
AppContext.SetSwitch("Switch.System.Xml.AllowDefaultResolver", true);
Final code that works:
[Test]
public void Transform_WhenXslUsesTheDocumentFunction_DoesNotThrow()
{
AppContext.SetSwitch("Switch.System.Xml.AllowDefaultResolver", true);
//Arrange
string testOutputPath = GetTestOutputPath(
nameof(Transform_WhenXslUsesTheDocumentFunction_DoesNotThrow)
);
string inputXsl = TestFilePaths.GetResource("Import\\DocumentImporter.xsl");
XsltSettings xsltSettings = new XsltSettings(true, true);
XmlUrlResolver resolver = new XmlUrlResolver();
XslCompiledTransform xslCompiledTransform = new XslCompiledTransform();
xslCompiledTransform.Load(inputXsl, xsltSettings, resolver);
//Act
TestDelegate testDelegate = () => xslCompiledTransform.Transform(
TestFilePaths.MinimumValidXml
, testOutputPath
);
//Assert
Assert.DoesNotThrow(testDelegate);
}
I have XML and XSL I want to transform to valid HTML. Source and targets are XmlDocuments.
I have this code:
public static XmlDocument XslTransformation(XslCompiledTransform xslt, XmlDocument input)
{
XmlDocument target = new XmlDocument();
using (var writer = XmlWriter.Create(target.CreateNavigator().AppendChild(), xslt.OutputSettings))
{
xslt.Transform(input, writer);
}
return target;
}
In XSL we set output method:
<xsl:output method="html" version="1.0" encoding="iso-8859-1" indent="yes" omit-xml-declaration="no"/>
But result is not valid HTML. For example
<script src="blah.js"></script>
is converted to
<script src="blah.js" />
I checked OutputMethod of XSLT OutputSettings and it is set to "Html".
I found many related questions and accepted answers but I dont understand why I am still getting self-closing tags.
I am trying to set a password in an xml file generated via XSLT (using Saxon-HE v9.7.0.14), by setting a global parameter.
The password can contain any characters so it needs to be put in a CDATA section.
I am trying to achieve this by setting the cdata-section-elements attribute of my xslt's xsl:output element to include the name of the password element:
<xsl:output method="xml" indent="yes" cdata-section-elements="password"/>
This is not working. I have included example code, input, xslt, current output and desired output below.
What do I need to change to get the password inside a CDATA section?
Program:
using System;
using System.IO;
using Saxon.Api;
namespace XsltTest {
class Program {
static void Main(string[] args) {
var xslt = new FileInfo(#"transform.xslt");
var input = new FileInfo(#"input.xml");
var output = new FileInfo(#"output.xml");
var processor = new Processor();
var compiler = processor.NewXsltCompiler();
var executable = compiler.Compile(new Uri(xslt.FullName));
var transformer = executable.Load();
var destination = new DomDestination();
using (var inputStream = input.OpenRead()) {
transformer.SetInputStream(inputStream, new Uri(Path.GetTempPath()));
transformer.SetParameter(
new QName("password"),
new XdmAtomicValue("secret"));
transformer.Run(destination);
}
destination.XmlDocument.Save(output.FullName);
}
}
}
Transform.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:output method="xml" indent="yes" cdata-section-elements="password"/>
<xsl:param name="password" />
<xsl:template match="#* | node()">
<bar>
<username>
<xsl:value-of select="//username"/>
</username>
<password>
<xsl:value-of select="$password"/>
</password>
</bar>
</xsl:template>
</xsl:stylesheet>
Input.xml:
<?xml version="1.0" encoding="utf-8" ?>
<foo>
<username>john</username>
</foo>
Output.xml:
<bar>
<username>john</username>
<password>secret</password>
</bar>
The password is not put inside a CDATA section.
Desired result:
<bar>
<username>john</username>
<password><![CDATA[secret]]></password>
</bar>
The options on xsl:output affect the actions of the serializer, and if the output is not serialized, they have no effect. You are writing to a DomDestination rather than to a Serializer (and then serializing the DOM using DOM methods, which know nothing about the XSLT xsl:output declaration).
In any case your premise is wrong: "The password can contain any characters so it needs to be put in a CDATA section." Without cdata-section-elements, special characters will be serialized using entity references such as < and &, which should work just fine.
I tested your code and it appears to be working correctly.
The input line
xsltproc --stringparam password "1>2Ä-34" Transform.xslt Input.xml
returns the desired output
<?xml version="1.0"?>
<bar>
<username>john</username>
<password><![CDATA[1>2Ä-34]]></password>
</bar>
Did you miss the input of the password parameter to the XSLT processor to feed the <xsl:param name="password" />?
Michael Kay's answer explained the solution, which was that the cdata-section-elements attribute is only applicable when writing to a Serializer not a DomDestination.
Here's the C# code for doing that:
static void Main(string[] args) {
var xslt = new FileInfo(#"transform.xslt");
var input = new FileInfo(#"input.xml");
var output = new FileInfo(#"output.xml");
var processor = new Processor();
var compiler = processor.NewXsltCompiler();
var executable = compiler.Compile(new Uri(xslt.FullName));
var transformer = executable.Load();
var serializer = new Serializer();
using (var writer = new StreamWriter(output.FullName))
using (var inputStream = input.OpenRead()) {
serializer.SetOutputWriter(writer);
transformer.SetInputStream(inputStream, new Uri(Path.GetTempPath()));
transformer.SetParameter(
new QName("password"),
new XdmAtomicValue("secret"));
transformer.Run(serializer);
}
I've been having problems with the indentation of my XML files. Everytime I load them from a certain server, the XML nodes all jumble up on a few lines. I want to write a quick application to indent the nodes properly. That is:
<name>Bob<name>
<age>24</age>
<address>
<stnum>2</stnum>
<street>herp derp st</street>
</address>
currently it's coming out as :
<name>bob</name><age>24</age>
<address>
<stnum>2</stnum><street>herp derp st</street>
</address>
since I can't touch the internal program that gives me these xml files and re-indenting them without a program would take ages, I wanted to write up a quick program to do this for me. When I use the XMLdocument library stuff, it only reads the information of the nodes. So my question is, whats a good way to read the file, line by line and then reindenting it for me. All xml nodes are the same.
Thanks.
You can use the XmlTextWritter class. More specifically the .Formatting = Formatting.Indented.
Here is some sample code I found on this blog post.
http://www.yetanotherchris.me/home/2009/9/9/formatting-xml-in-c.html
public static string FormatXml(string inputXml)
{
XmlDocument document = new XmlDocument();
document.Load(new StringReader(inputXml));
StringBuilder builder = new StringBuilder();
using (XmlTextWriter writer = new XmlTextWriter(new StringWriter(builder)))
{
writer.Formatting = Formatting.Indented;
document.Save(writer);
}
return builder.ToString();
}
With LINQ to XML, it's basically a one-liner:
public static string Reformat(string xml)
{
return XDocument.Parse(xml).ToString();
}
Visual Studio or any decent XML editor will format (tabify) XML documents easily. There are also on-line tools available:
http://www.xmlformatter.net/
http://www.shell-tools.net/index.php?op=xml_format
If you are using Visual studio just open xml do Ctrl+a Ctrl+k Ctrl+F and that's it for formatting.
You can also use XSLT:
// This XSLT copies everything but idented
StringReader sr = new StringReader( xsl );
XmlReader reader = XmlReader.Create(sr);
XslTransform xslt = new XslTransform();
xslt.Load(reader);
xslt.Transform(xmlFileUnidentedPath, xmlFileIdentedPath);
Having xsl defined as:
string xsl = #"
<?xml version=""1.0""?>
<xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"">
<xsl:output method=""xml"" omit-xml-declaration=""no"" indent=""yes"" encoding=""US-SCII""/>
<xsl:strip-space elements=""*""/>
<xsl:template match=""/"">
<xsl:copy-of select="".""/>
</xsl:template>
</xsl:stylesheet>";
I can't possibly be the first person to do this, it seems like it would be such a common practice to merge two documents using XSLT. However, I can't seem to find a single example on the ol' interweb.
I have two XML documents that are being retrieved as strings of XML from SQL Server. I want to use XslCompiledTransform to merge the two documents. I know that XslCompiledTransform turns off the XSL document() function by default. I have turned that on using XsltSettings when I create my XslCompiledTransform object.
My understanding about how to "add" the second document to the transformation is to use an XsltArgumentList and use the AddParam() method and add an XPathNavigator object:
XsltArgumentList xsltArgs = new XsltArgumentList();
xsltArgs.AddParam(
(string)e.UserState + "s", "http://www.myuri.com/tabledata",
dataXmlDoc.CreateNavigator()
);
However any attempts at accessing the document that is added results in either an error or nothing returned. — C#:
XslCompiledTransform fieldToXhtmlTransform = new XslCompiledTransform(true);
try
{
UriBuilder xsltUri = new UriBuilder(
Request.Url.Scheme, Request.Url.Host,
Request.Url.Port, this.ResolveUrl("Transforms/address1.xslt")
);
XmlSecureResolver resolver = new XmlSecureResolver(
new XmlUrlResolver(), new PermissionSet(PermissionState.Unrestricted)
);
fieldToXhtmlTransform.Load(
xsltUri.ToString(), new XsltSettings(true, false), resolver
);
}
catch
{
//TODO: do something useful here.
}
XPathDocument fieldSchemaXmlDoc = null;
using (MemoryStream fieldMemoryStream = new MemoryStream(
Encoding.UTF8.GetBytes(e.Result.TableMetaDataXml)
))
{
fieldSchemaXmlDoc = new XPathDocument(fieldMemoryStream);
}
XPathDocument dataXmlDoc = null;
using (MemoryStream dataMemoryStream = new MemoryStream(
Encoding.UTF8.GetBytes(e.Result.DataXml)
))
{
dataXmlDoc = new XPathDocument(dataMemoryStream);
}
StringBuilder output = new StringBuilder();
XmlWriterSettings writerSettings = new XmlWriterSettings();
writerSettings.OmitXmlDeclaration = true;
writerSettings.Encoding = Encoding.UTF8;
XsltArgumentList xsltArgs = new XsltArgumentList();
xsltArgs.AddParam(
(string)e.UserState + "s", "http://www.myuri.com/tabledata",
dataXmlDoc.CreateNavigator()
);
XmlWriter transformedDataWriter = XmlWriter.Create(output, writerSettings);
fieldToXhtmlTransform.Transform(
fieldSchemaXmlDoc, xsltArgs, transformedDataWriter
);
XSLT - Only accesses the added document, not the document loaded with the transform.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:hlsschema="http://www.myuri.com/tableschema"
xmlns:hlsdata="http://www.myuri.com/tabledata"
exclude-result-prefixes="msxsl hlsschema hlsdata xsl"
>
<xsl:output method="html" indent="yes"/>
<p>
<xsl:template match="hlsdata:Address1s">
<xsl:for-each select="hlsdata:Address1">
<p>
<xsl:value-of select="hlsdata:dr_id"/>
</p>
</xsl:for-each>
</xsl:template>
</p>
</xsl:stylesheet>
XML
<hlsdata:Address1s
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:hlsdata="http://www.myuri.com/tabledata"
>
<hlsdata:Address1>
<hlsdata:dr_id>12345678</hlsdata:dr_id>
</hlsdata:Address1>
</hlsdata:Address1s>
I know I'm missing something obvious, but it is getting beyond frustrating. I know the document gets added as a parameter, but I can't find an example of how to access a document loaded as a parameter.
Any help would be greatly appreciated. Keep in mind that the code above is a work in progress and is between two of hundreds of attempts to make it work so if something looks a bit odd, its probably because its between attempts.
You need to define a parameter in your stylesheet and then use that parameter.
Here is a simple example, the stylesheet looks as follows:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:docs="http://example.com/2010/docs"
exclude-result-prefixes="docs"
>
<xsl:param name="docs:doc1" select="/.."/>
<xsl:template match="/">
<xsl:apply-templates select="$docs:doc1/node()"/>
</xsl:template>
<xsl:template match="root">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="foo">
<li>
<xsl:apply-templates/>
</li>
</xsl:template>
</xsl:stylesheet>
The C# code looks as follows:
string xml = "<root><foo>1</foo><foo>2</foo></root>";
XPathDocument doc = new XPathDocument(new StringReader(xml));
XslCompiledTransform proc = new XslCompiledTransform();
proc.Load(#"..\..\XSLTFile1.xslt");
XsltArgumentList xsltArgs = new XsltArgumentList();
xsltArgs.AddParam("doc1", "http://example.com/2010/docs", doc.CreateNavigator());
proc.Transform(XmlReader.Create(new StringReader("<dummy/>")), xsltArgs, Console.Out);
This is a console application which for simplicity writes to Console.Out but you can of course use other outputs the Transform method allows.
That example then writes <ul><li>1</li><li>2</li></ul> so the input parameter has been processed.
So that should show you how to pass in a parameter that XslCompiledTransform sees as a node-set you can process with XSLT.
As for writing a stylesheet that merges two documents, please post two input samples and the corresponding result sample you want to create if you have problems writing that XSLT.