Using C# to do xslt transform ignoring xsl:output - c#

I am doing an xslt transform inside my c# program. When I run the xslt on its own it outputs just fine, but when I run it from within my c# program it always leaves off the:
<?xml version="1.0" encoding="UTF-8"?>
At the top of the resulting xml document. My XSLT file looks like:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:hd="http://www.hotdocs.com/schemas/component_library/2009"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xml="http://www.w3.org/XML/1998/namespace">
<xsl:output method="xml" omit-xml-declaration="no" version="1.0" encoding="UTF-8"/>
<xsl:template match="/xsd:schema">
<hd:componentLibrary xmlns:hd="something" version="10">
</hd:componentLibrary>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
I am running the xslt in my c# program like this:
XPathDocument myXPathDoc = new XPathDocument(PathToXMLDocument);
XslCompiledTransform myXslTrans = new XslCompiledTransform();
myXslTrans.Load(PathToXSLTDocument);
XmlTextWriter myWriter = new XmlTextWriter(PathToOutputLocation, null);
myXslTrans.Transform(myXPathDoc,null,myWriter);
myWriter.Close();
I have tried the xslt document without the xsl:output line, but that does not seem to help.
How can i get the ?xml tag at the top of my outputted xml file?
Thanks

XmlTextWriter is a bit outdated. I recommend you switch to XmlWriter.Create.
Then you can specify OmitXmlDeclaration = false in the XmlWriterSettings.

If you use XmlWriter.Create() then you can pass an XmlWriterSettings instance as a parameter. The OmitXmlDeclaration member in the settings class controls whether or not the tag is included.

Related

XSL transformation: utf-8 and iso-8859-1 compatibility

I use .NET class XslCompiledTransform for xslt transformation and I have a problem with encodings. I have this word Förstelärare in my input xml. Here is cases:
input xml file has <?xml version="1.0" encoding="utf-8"?> - xslt file has <xsl:output encoding="utf-8" ... - OK
input xml file has <?xml version="1.0" encoding="utf-8"?> - xslt file has <xsl:output encoding="iso-8859-1" ... - OK
input xml file has <?xml version="1.0" encoding="iso-8859-1"?> - xslt file has <xsl:output encoding="iso-8859-1" ... - OK
input xml file has <?xml version="1.0" encoding="iso-8859-1"?> - xslt file has <xsl:output encoding="utf-8" ... - CORRUPTED - I see Förstelärare in output xml.
input.xml:
<?xml version="1.0" encoding="iso-8859-1"?>
<test>Förstelärare</test>
trans.xslt:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" standalone="yes" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="/test" />
</xsl:template>
<xsl:template match="test">
<test><xsl:value-of select="text()"/></test>
</xsl:template>
</xsl:stylesheet>
C# code:
var xslCompiledTransform = new XslCompiledTransform();
using (var xmlReader = XmlReader.Create(#"C:\trans.xslt", new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, XmlResolver = null }))
{
xslCompiledTransform.Load(xmlReader);
}
using (var xmlReader = XmlReader.Create(#"C:\input.xml", new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, XmlResolver = null }))
using (var xmlWriter = XmlWriter.Create(#"C:\output.xml", xslCompiledTransform.OutputSettings))
{
xslCompiledTransform.Transform(xmlReader, xmlWriter);
}
output.xml:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<test>Förstelärare</test>
Why does it happen? It looks like I need to use iso-8859-1 in my xslt file to prevent corruption cause it works in both cases iso-8859-1 and utf-8.
The output you're seeing is the result of interpreting a string encoded with UTF-8 as if it were iso-8859-1.
There are two possibilities:
Your source file is actually encoded as UTF-8: just because the XML declaration says iso-8859-1, that doesn't necessarily mean that's how the text has been saved. (EDIT: Based on comments, I believe this is what's happening in your case.)
Alternatively, when you're writing it out as UTF-8 it's working just fine, but whatever you're using to inspect the output is ignoring that and assuming it's iso-8859-1.
Here's the character in it's various encodings:
http://www.fileformat.info/info/unicode/char/00f6/index.htm
I would suggest looking at your source document in a hex editor, and immediately following the 'F' (70 or 0x46 in any encoding), you should see 0xF6 if it's in iso-8859-1 as per the XML declaration- in which case you're probably reading the output in the wrong encoding. If it's 0xC3 0xB6, that's UTF-8, and the encoding in the XML declaration of your source is wrong.

How to get an absolute path to the directory of the XSL file?

My Schema.xsd file is located in the same directory with the .xsl file. In the .xsl file I would like to generate a link to Schema.xsl in the generated output. The generated output is located in different directories. Currently I do it like this:
<xsl:template match="/">
<root version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../Schema.xsd">
<!-- . . . -->
However this forces the generated output to be located 3 levels under the directory of Schema.xsd. I would like to generate an absolute path to the schema in the output, so the output could be located anywhere.
Update. I use XSLT 1.0 (XslCompiledTransform implementation in .NET Framework 4.5).
XSLT 2.0 Solution
Use the XPath 2.0 function, resolve-uri():
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"
omit-xml-declaration="yes"
encoding="UTF-8"/>
<xsl:template match="/">
<root version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="{concat(resolve-uri('.'), 'Schema.xsd')}">
</root>
</xsl:template>
</xsl:stylesheet>
Yields, without parameter passing and regardless of the input XML:
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.0"
xsi:noNamespaceSchemaLocation="file:/c:/path/to/XSLT/file/Schema.xsd"/>
This is a sketch of how to do it (also see Passing parameters to XSLT Stylesheet via .NET).
In your C# code you need to define and use a parameter list:
XsltArgumentList argsList = new XsltArgumentList();
argsList.AddParam("SchemaLocation","","<SOME_PATH_TO_XSD_FILE>");
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load("<SOME_PATH_TO_XSLT_FILE>");
using (StreamWriter sw = new StreamWriter("<SOME_PATH_TO_OUTPUT_XML>"))
{
transform.Transform("<SOME_PATH_TO_INPUT_XML>", argsList, sw);
}
Your XSLT could be enhanced like this:
...
<xsl:param name="SchemaLocation"/> <!-- this more or less at the top of your XSLT! -->
...
<xsl:template match="/">
<root version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="{$SchemaLocation}">
...
...
</xsl:template>
....

Preserving whitespace within XML elements between attributes when using XslCompiledTransform

I am applying an XSL-T file xsltUri to an XML file TargetXmlFile using the XslCompiledTransform class:
XslCompiledTransform xslTransform = new XslCompiledTransform(false);
xslTransform.Load(xsltUri);
using (var outStream = new MemoryStream())
{
var writer = new StreamWriter(outStream, new UTF8Encoding());
using (var reader = new XmlTextReader(TargetXmlFileName)
{
WhitespaceHandling = WhitespaceHandling.All,
DtdProcessing = DtdProcessing.Ignore
})
{
xslTransform.Transform(reader, xsltArguments, writer);
}
outStream.Position = 0;
using (FileStream outFile = new FileStream(outputFileName, FileMode.Create))
{
outStream.CopyTo(outFile);
}
}
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<element
id="1"
attr1="value11"
attr2="value12"/>
<element id="2" attr1="value21" attr2="value22"/>
</root>
Input XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//element[#id='2']/#attr1">
<xsl:attribute name="attr1">
<xsl:value-of select="'newvalue21'"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Actual output XML:
<?xml version="1.0" encoding="utf-8"?><root>
<element id="1" attr1="value11" attr2="value12" />
<element id="2" attr1="newvalue21" attr2="value22" />
</root>
Desired output XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<element
id="1"
attr1="value11"
attr2="value12"/>
<element id="2" attr1="newvalue21" attr2="value22"/>
</root>
Question: How can I preserve the whitespace (particularly, line breaks) of the input XML file within the "element" tags in the output XML file? I have experimented with different options, but nothing worked for this case.
Thanks for any hints!
This has nothing to do with XSLT. The whitespace you're referring to does not exist in the XML document model, and it cannot be made significant to a conformant XML processor, even with xml:space="preserve". There is no place for it in the DOM, and it will be skipped by the reader; as such there is no way to copy it to the writer. You would have to emit the XML with custom code (in other words, not with an XmlWriter).
The internal formatting of a tag (whitespace between attributes) is completely ephemeral in XML.
As far as XML documents are concerned, it does not exist.
As far as XML parsers are concerned, it is ignored, because 1). The only exception is that whitespace is illegal immediately after a <.
As far as XML serializers are concerned, they can do what they want, because 1) and 2). Most (if not all) will use a single space character to separate attributes from each other.
So...
Don't try to build an application that depends on the source code layout of XML.
Since this kind of source code layout in XML is technically irrelevant… get over your OCD. ;)

Modify XSLT using C# Code

I am Working on Visual-studio 2012 in C#.
I want to update the value of a node of a XSLT.
This abc.xslt is like:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<DocumentElement>
<PositionMaster>
<Name>
<xsl:value-of select = "'Ryan'"/>
</Name>
</PositionMaster>
</DocumentElement>
Code i have written to modify this XSLT in the C# is:
XmlDocument xslDoc = new XmlDocument();
xslDoc.Load(abc.xslt);
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xslDoc.NameTable);
nsMgr.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
I am looking to change the value of Name field to David. What should i write further here?
XmlElement valueOf = xslDoc.SelectSingleNode("/xsl:stylesheet/xsl:template[#match = '/']/DocumentElement/PositionMaster/Name/xsl:value-of", nsMgr);
if (valueOf != null)
{
valueOf.SetAttribute("select", "'David'");
xslDoc.Save("new.xslt");
}
else
{
// handle case here that element was not found
}
You seem to be going about this a very odd way. Why not just use a stylesheet parameter (a global xsl:param element)?
And if you do need to modify a source stylesheet, as you sometimes do, surely it makes more sense to use XSLT for the purpose?

How can I render custom XSL controls in my XSLT file with C#?

I'm using C# to translate a XML file to HTML with the use of XSLT.
I use an Extension object to render my own code:
<?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:widget="urn:serverTime"
>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select="demo:printTime()"/>
</xsl:template>
and in my C#:
XsltArgumentList myList = new XsltArgumentList();
myList.AddExtensionObject("demo:serverTime", new ServerTime());
transform.Transform(document, myList, writer);
This works perfectly. However, I would like to create my own custom tags like:
<demo:printTime />
This doesn't work: the tag is printed to the output without being rendered. How can I make this work so I can use my own tags?
You can't do this. XSLT does not support "custom tags".
If you want to print out anything that is not a literal value, then it must be the result of a function call, wrapped in <xsl:value-of/>.

Categories