cdata-section-elements not working - c#

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);
}

Related

How to transform xml string using xslt stylesheet linked by xml-stylesheet href="..." in C# [duplicate]

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);
}

How to add xsi:noNamespaceSchemaLocation to Serializer

I build an XML Document which needs to be validated against a xsd file. Thus I need a reference to the xsd file in the root element of the xml. So far I use this C# Code:
var ser = new XmlSerializer(typeof(myspecialtype));
XmlSerializerNamespaces MainNamespace = new XmlSerializerNamespaces();
MainNamespace.Add("xlink", "http://www.w3.org/1999/xlink");
MainNamespace.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
using (XmlWriter w = XmlWriter.Create(#"C:\myxmlfile.xml"))
{
w.WriteProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"utils/somexsl.xsl\"");
ser.Serialize(w, LeBigObject, HauptNs);
}
The resulting Xml begins like this:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="utils/somexsl.xsl"?>
<caddy-xml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlVersion="03.07.00">
but I need this:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="utils/somexsl.xsl"?>
<caddy-xml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlVersion="03.07.00" xsi:noNamespaceSchemaLocation="utils/theveryimportant.xsd">
I came across "CreateAttribute" here: Add Namespace to an xml root node c# but I can't put it together with the Serializer. Thank you!
I was pointed to the solution here:
https://social.msdn.microsoft.com/Forums/en-US/e43585c6-181b-4449-8806-b07f82681a2a/how-to-include-xsinonamespaceschemalocation-in-the-xml?forum=asmxandxml
I added this to my class:
[XmlAttribute("noNamespaceSchemaLocation", Namespace = XmlSchema.InstanceNamespace)]
public string attr = "utils/theveryimportant.xsd";
and it works.

XSLT not encoding double byte characters

I'm working on a viewer to display xml log files as html using xslt. Everything is going fine exception my localization. The resulting HTML file has a 'ó' where some double byte characters should be. I can't figure out what I am doing wrong.
Here is a a stripped down XSLT file:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
<xsl:output method="html" version="4.0" encoding="utf-8" indent="yes"/>
<xsl:variable name="language" select="nbklog/#language" />
<xsl:variable name="dictionaryName">
dictionary_<xsl:value-of select="$language"/>.xml
</xsl:variable>
<xsl:variable name="dictionary" select="document($dictionaryName)" />
<xsl:template match="/nbklog">
<html>
<body>
<h2>
<xsl:value-of select="$dictionary//String[#Key=$jobType]" />
</h2>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Here is a dictionary xml file used for localization:
<?xml version="1.0" encoding="utf-8"?>
<Dictionary xml:lang="es-ES">
<String Key="Application">
Applicación
</String>
</Dictionary>
Here is an example xml file to be transformed:
<?xml version="1.0" encoding="utf-8"?>
<nbklog id="51b654d4" jobType="backup" language="es-ES" version="1.0">
<deviceName>c:\</deviceName>
....
</nbklog>
I'm executing the transformation the following c# code:
string theOutputHtml;
using (MemoryStream ms = new MemoryStream()) {
using (XmlTextWriter writer = new XmlTextWriter(ms, Encoding.UTF8)) {
XPathDocument theDocument = new XPathDocument(inXmlFilename);
// Load the style sheet and run the transformation.
XslCompiledTransform theXslTrasform = new XslCompiledTransform();
theXslTrasform.Load(inXsltFilename, XsltSettings.TrustedXslt, null);
theXslTrasform.Transform(theDocument, writer);
ms.Position = 0;
using (StreamReader theReader = new StreamReader(ms)) {
theOutputHtml = theReader.ReadToEnd();
}
}
}
The content of theOutputHtml will have a 'ó' instead of the 'ó'.
EDIT:
Adding this between the and tags in the html string solved my problem:
<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'>
Change new XmlTextWriter(ms, Encoding.ASCII) to new XmlTextWriter(ms, Encoding.UTF8)
Update:
Another possible issue is that although your XML files have an encoding="utf-8" declaration, perhaps the files aren't actually saved with that encoding. Check that all of your XML files' encodings match their declared encodings. Personally, I prefer doing away with declaring the encoding so that it can be automatically be detected instead.
Pretty sure its because you are using the wrong encoding, try this:
using (XmlTextWriter writer = new XmlTextWriter(ms, Encoding.Unicode))

XSLT transformation does not return anything

I am trying to create a small code generator based upon XSLT transformation. I am rather new to XSLT, and it seems that I have made error (not sure where) in my transformations. I have two transformation (main and util), the metadata is pulled from XML file (it stores information about table names, which will be used for class generation- table name = class name; column name = field name ). Here are my transformations:
Main transformation
<?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"
xmlns:dbs="http://kadgen/DatabaseStructure">
<xsl:import href="..\MySolution\UtilTemplates.xslt"/>
<xsl:output method="text" encoding="UTF-8" indent="yes"/>
<xsl:param name="Name"/>
<xsl:param name ="filedName"/>
<xsl:template match="/">
<xsl:apply-templates select=
"//dbs:DataStructure//dbs:Table[#Name='Customer']"
mode="BuildClasses" />
</xsl:template>
<xsl:template match="dbs:Table" mode="BuildClasses">
<xsl:call-template name="Header"/>
Public Class <xsl:value-of select="#Name"/>
{
<xsl:call-template name="ClassConstructors"/>
<xsl:call-template name="ClassLevelDeclarations"/>
<xsl:call-template name="FieldAccessProperties"/>
}
</xsl:template>
<xsl:template name="ClassConstructors">
</xsl:template>
<xsl:template name="ClassLevelDeclarations">
</xsl:template>
<xsl:template name="FieldAccessProperties">
</xsl:template>
</xsl:stylesheet>
Here is the util transformation:
<?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"/>
<xsl:template name="Header">
using System;
using System.Collections.Generic;
using System.Xml;
using System.Linq;
using System.Text;
</xsl:template>
</xsl:stylesheet>
Here is part of my XML file:
<?xml version="1.0" encoding="utf-8" ?>
<dbs:MetaDataRoot FreeForm="true" xmlns:dbs="http://kadgen/DatabaseStructure">
<dbs:DataStructures>
<dbs:DataStructure Name="ASPDatabase">
<dbs:Tables>
<dbs:Table Name="Customer" OriginalName="Customer">
<dbs:TableColumns>
<dbs:TableColumn Name="CustomerID" NETType="int" IsPrimaryKey="true" />
<dbs:TableColumn Name="Name" NETType="string" IsPrimaryKey="false"/>
</dbs:TableColumns>
<dbs:TableConstraints>
<dbs:PrimaryKey>
<dbs:PKField Name="CustomerID"/>
</dbs:PrimaryKey>
</dbs:TableConstraints>
</dbs:Table>
</dbs:Tables>
</dbs:DataStructure>
</dbs:DataStructures>
</dbs:MetaDataRoot>
Here is how I start transformation:
XslCompiledTransform myXslTrans = new XslCompiledTransform();
myXslTrans.Load("xslt transformation location");
myXslTrans.Transform("XML source location", "Empty class file location");
After executing above code, only thing I get is empty cs file. It may seem robust but please, go through it and help me with this.
Thanks.
Have you tried debugging your XSLT files with VS XSLT debugger?
Looks like it generates correct output.
This is working for me but I made a couple of small changes and fixed your XML doc.
Here is my test app.
private static void Main(string[] args)
{
var myXslTrans = new XslCompiledTransform();
var doc = new XmlDocument();
doc.LoadXml(GetResourceTextFile("ProjectName.MainTransform.xslt"));
myXslTrans.Load(doc);
var sb = new StringBuilder();
var sw = new StringWriter(sb);
var xsltArgs = new XsltArgumentList();
xsltArgs.AddParam("Name", "", "test name");
xsltArgs.AddParam("filedName", "", "test filed name");
var docXml = new XmlDocument();
docXml.LoadXml(GetResourceTextFile("ProjectName.Test.xml"));
myXslTrans.Transform(docXml, xsltArgs, sw);
var test = sw.ToString();
}
public static string GetResourceTextFile(string filename)
{
string result = string.Empty;
var assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(filename))
{
if (stream != null)
{
using (var sr = new StreamReader(stream))
{
result = sr.ReadToEnd();
}
}
}
return result;
}
The major differences I made were adding XSLT arguments and loading the embedded files into XmlDocuments first. I can't reproduce the blank output so I can't be sure what the root cause of your issue is.

Generating XSL block in C#

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

Categories