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.
Related
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);
}
So I am following a tutorial from here. I have the following code but it simply won't display anything inside the web browser component. The specific instructions I followed are:
"Hi everyone,
I managed to do this using the following strategy:
Download an HTML 1.0 compliant version of defaultss.xsl (I'm using my own)
Create an XMLCompiledTransform object and load the "defaultss.xsl" stylesheet. I actually embedded it is a resource in my assembly and read it from there.
Transform your XML stream using the XMLCompiledTransform into an HTML stream.
Set the WebBrowser.DocumentStream = HTML stream
Remember to "AllowNavigation" otherwise the thing doesn't work.
Hope this helps,
B."
OpenFileDialog OpenCCD = new OpenFileDialog();
OpenFileDialog OpenXSL = new OpenFileDialog();
string xslpath, filepath;
private void button1_Click(object sender, EventArgs e)
{
OpenCCD.ShowDialog();
filepath = OpenCCD.FileName;
}
private void cmd_XSL_Click(object sender, EventArgs e)
{
OpenXSL.ShowDialog();
xslpath = OpenXSL.FileName;
}
private void cmd_View_Click(object sender, EventArgs e)
{
MessageBox.Show("XML Path:" + filepath + "\nXSL Path:" + xslpath);
XslCompiledTransform xsl = new XslCompiledTransform();
xsl.Load(xslpath);
MemoryStream ms = new MemoryStream();
xsl.Transform(filepath, null, ms);
webBrowser1.DocumentStream = ms;
}
The xml is pretty long so here is a piece of it:
<?xml version="1.0" encoding="UTF-8"?>
<CDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:hl7-org:v3 http://xreg2.nist.gov:8080/hitspValidation/schema/cdar2c32/infrastructure/cda/C32_CDA.xsd" xmlns="urn:hl7-org:v3" xmlns:mif="urn:hl7-org:v3/mif">
<realmCode code="US"/>
<typeId root="2.16.840" extension="PO"/>
<templateId root="2.16"/>
<templateId root="2.17"/>
<id root="2.16.84" extension="E5"/>
<code codeSystem="2.16.88" codeSystemName="" code="3" displayName="TITLE"/>
<title>TITLE</title>
<effectiveTime value="2925"/>
<confidentialityCode code="Y" codeSystem=""/>
<recordTarget>
<patientRole>
<id root="2.16.84" extension="215"/>
<addr use="P">
<streetAddressLine>123 GREEN TRAIL RD</streetAddressLine>
<city>BIRMINGHAM</city>
<state>AL</state>
<postalCode>35211</postalCode>
<country>USA</country>
</addr>
<telecom use="H" value="tel:000000000"/>
<patient>
<name>
<given qualifier="L">ADAM</given>
<family qualifier="R">EVERYMAN</family>
</name>
<ethnicGroupCode codeSystem="2.16.84" codeSystemName="Race" displayName="Not Hispanic or Latino"/>
<languageCommunication>
<languageCode code="eng"/>
</languageCommunication>
</patient>
</patientRole>
</recordTarget>
XSL is also long so here is a piece of it:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:n3="http://www.w3.org/1999/xhtml" xmlns:n1="urn:hl7-org:v3" xmlns:n2="urn:hl7-org:v3/meta/voc" xmlns:voc="urn:hl7-org:v3/voc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="html" indent="yes" version="4.01" encoding="ISO-8859-1" doctype-public="-//W3C//DTD HTML 4.01//EN"/>
<xsl:variable name="tableWidth">50%</xsl:variable>
<xsl:variable name="title">
<xsl:choose>
<xsl:when test="/n1:CDocument/n1:title">
<xsl:value-of select="/n1:CDocument/n1:title"/>
</xsl:when>
<xsl:otherwise>CDocument</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates select="n1:CDocument"/>
</xsl:template>
The solution that I found was to use the following code instead. This code, saves a result.html and renders that inside the web browser control.
XslCompiledTransform myXslTrans = new XslCompiledTransform();
myXslTrans.Load(xslpath);
string directoryPath = Path.GetDirectoryName(filepath);
myXslTrans.Transform(filepath, directoryPath+"result.html");
webBrowser1.Navigate(directoryPath+"result.html");
I am using XSLT transformation template which works with MSSQL database in which i have to do some complex computing. I show piece of code below. Important is method GetZaloha. It returns large xml element which I want past it to output xml, but obv. this approach doesn't work. I tried to return value as string through xElem.ToString() but result is represented (ofc.) as string. so "<" and ">" marks are represented as escaped > and <.
Do somebody have idea how provide some object to transformation which will be represented as xml structured text? Thank you very much.
<msxsl:script implements-prefix="utility" language="C#">
<msxsl:assembly name="System"/>
<msxsl:assembly name="System.Data"/>
<msxsl:using namespace="System"/>
<msxsl:using namespace="System.Xml"/>
<msxsl:assembly name="System.Core" />
<msxsl:assembly name="System.Xml.Linq" />
<msxsl:using namespace="System.Collections.Generic" />
<msxsl:using namespace="System.Linq" />
<msxsl:using namespace="System.Xml.Linq" />
<![CDATA[
public static XElement GetZaloha(string VariableSymbol)
{
XElement xElem = .....
return xElem;
}
]]>
</msxsl:script>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="CisloDokladu">
<xsl:element name="CisloDokladu">
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:element>
<xsl:variable name="VariabilniSymbol" select="./VariabilniSymbol"/>
<xsl:element name="OdpoctyZaloh">
<xsl:attribute name="ObjectType">List</xsl:attribute>
<xsl:attribute name="ObjectName">OdpocetZalohy</xsl:attribute>
<xsl:value-of select="utility:GetZaloha($VariabilniSymbol)" />
</xsl:element>
</xsl:template>
Some solution: write output XML with XSLT transform extension object and use XmlWriter method WriteCData:
public class CDataWriter
{
public static XDocument Transform(XDocument doc, string fileXslt)
{
XsltArgumentList xslArg = new XsltArgumentList();
XslCompiledTransform trans = new XslCompiledTransform();
trans.Load(fileXslt);
XDocument xmlOutput = new XDocument();
using (var writer = xmlOutput.CreateWriter())
{
CDataWriter info = new CDataWriter(writer);
xslArg.AddExtensionObject("urn:cdata", info);
trans.Transform(input: doc.CreateReader(), arguments: xslArg, results: writer);
}
return xmlOutput;
}
protected CDataWriter(XmlWriter writer) { this.writer = writer; }
XmlWriter writer;
public string CData(object obj)
{
if (obj != null && obj is System.Xml.XPath.XPathNodeIterator)
{
var iter = obj as System.Xml.XPath.XPathNodeIterator;
if (iter.Count > 0 && iter.MoveNext())
{
var current = iter.Current;
writer.WriteCData(current.OuterXml);
}
}
return string.Empty;
}
}
Sample xslt file:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:trans="urn:cdata">
<xsl:template name="cdata">
<Result>
<xsl:value-of select="trans:CData(*)" />
</Result>
</xsl:template>
</xsl:stylesheet>
You are currently using <xsl:value-of> to include your function result into your XML output. This tag always flattens the XML node tree by (more or less) extracting just the text parts from it. I would suggest that you use
<xsl:copy-of select="utility:GetZaloha($VariabilniSymbol)" />
instead which copies the complete node tree (without alteration) into the XML output.
Five years after, but since I have just figured out the solution I post it anyway.
The xsl engine only accepts a limited set of types. XPathNavigator is one and it allows to return a full xml sub-tree from the script into the document.
Here is some more info about the accepted return types
https://learn.microsoft.com/en-us/dotnet/standard/data/xml/script-blocks-using-msxsl-script
The xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:my-scripts="urn:my-scripts"
exclude-result-prefixes="msxsl my-scripts"
>
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<msxsl:script language="C#" implements-prefix="my-scripts">
<![CDATA[
public static XPathNavigator createXmlNodes(string text)
{
//prepare DOM with doc elent named "root"
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root/>");
var root = doc.DocumentElement;
//Add an element
var elem = doc.CreateElement("token");
elem.AppendChild(doc.CreateTextNode(text));
root.AppendChild(elem);
//Add an element
elem = doc.CreateElement("token2");
elem.AppendChild(doc.CreateTextNode(text));
root.AppendChild(elem);
//returnxpathNavigator, this will include "root"
return root.CreateNavigator();
// A second option using a string instead of a DOM
//var sr = new System.IO.StringReader("<token>" + text + "</token>");
//var xp = new XPathDocument(sr);
//return xp.CreateNavigator();
}
]]>
</msxsl:script>
<!-- Ignore Input just call function using fixed text -->
<xsl:template match="/">
<doc>
<!-- the /* is there to skip the root element -->
<xsl:copy-of select="my-scripts:createXmlNodes('Hello World')/*" />
</doc>
</xsl:template>
</xsl:stylesheet >
Empty dummy input
<?xml version="1.0" encoding="utf-8"?>
<empty/>
result
<?xml version="1.0" encoding="utf-8"?>
<doc>
<token>Hello World</token>
<token2>Hello World</token2>
</doc>
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))
I want to transform a XML document, but having a problem.
My XSLT looks like this:
<?xml version="1.0" encoding="iso-8859-1" ?>
<xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:apply-templates select="address" />
</xsl:template>
<xsl:template match="address">
<xsl:value-of select="#street" />
<xsl:value-of select="#housenr" />
<xsl:value-of select="#zipcode" />
<xsl:value-of select="#city" />
<xsl:value-of select="#country" />
</xsl:template>
</xsl:stylesheet>
And the XML document I want to transform looks like this:
<address id="4" type="1"
typename="Postadres"
street="pak street"
housenr="420"
zipcode="42000"
city="Nill"
country="Lahore"
kix="" />
Here is the code I've written:
public static string Transform(XmlDocument doc, XmlDocument stylesheet)
{
var transform = new System.Xml.Xsl.XslCompiledTransform();
XmlDocument domOutput = new XmlDocument();
stylesheet.PreserveWhitespace = false;
transform.Load(stylesheet); // compiled stylesheet
MemoryStream oStream = new MemoryStream();
var writer = new System.IO.StringWriter();
transform.Transform(doc, (XsltArgumentList)null, oStream);
domOutput.Load(oStream);
return writer.ToString();
}
The following line throws an exception
transform.Transform(doc, (XsltArgumentList)null, oStream);
Exception message:
White space cannot be stripped from input documents that have already been loaded. Provide the input document as an XmlReader instead.
Can you tell me what I am doing wrong?
Thanks in advance!
I solved it. Actually "XslCompiledTransform.Transform" takes XmlReader as first Parameter and i was passing XmlDocument in First Paramenter.
Here is the code.
public static string Transform(XmlDocument doc, XmlDocument stylesheet)
{
try
{
System.Xml.Xsl.XslCompiledTransform transform = new System.Xml.Xsl.XslCompiledTransform();
transform.Load(stylesheet); // compiled stylesheet
System.IO.StringWriter writer = new System.IO.StringWriter();
XmlReader xmlReadB = new XmlTextReader(new StringReader(doc.DocumentElement.OuterXml));
transform.Transform(xmlReadB, null, writer);
return writer.ToString();
}
catch (Exception ex)
{
throw ex;
}
}