I have an XSL file that works fine in every place I have tried it, except in Delphi. Works for web parsers (e.g. https://xslttest.appspot.com/) and when using msxsl.exe
from Microsoft.
However, the code below gives the exception when calling transformNode()
in Delphi 10.2:
The stylesheet does not contain a document element. The stylesheet may be empty, or it may not be a well-formed XML document.
From what I understand, this should be using the same MSXML that msxsl
uses?
uses
Winapi.MSXML;
procedure TForm1.Button1Click(Sender: TObject);
var
transformstring: string;
xmldoc, xsldoc: IXMLDOMDocument3;
begin
xmldoc := CoDOMDocument60.Create;
xsldoc := CoDOMDocument60.Create;
xmldoc.load('C:\Temp\Data.xml');
xsldoc.load('C:\Temp\Stylesheet.xsl');
xsldoc.setProperty('AllowXsltScript', True);
transformstring := xmldoc.transformNode(xsldoc);
end;
XSL file is (this has been minimized):
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nbsp " ">
]>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="D2DSeverityFilter">^Caution^Minor^Moderate^Severe^</xsl:param>
<xsl:param name="D2HSeverityFilter">^Extreme Caution^Contraindication^Contraindicated^</xsl:param>
<xsl:param name="DocumentationFilter">^Not Established^Limited^Good^Well Established^</xsl:param>
<xsl:output method="html" omit-xml-declaration="yes"/>
</xsl:stylesheet>
The input data does not appear to be relevant, but for completeness this empty file will work:
<?xml version="1.0" encoding="windows-1252"?>
<Result ></Result>
The fixed code is below, including the extra properties that needed setting.
uses
Winapi.MSXML;
procedure TForm1.Button1Click(Sender: TObject);
var
transformstring: string;
xmldoc, xsldoc: IXMLDOMDocument3;
begin
xmldoc := CoDOMDocument60.Create;
xsldoc := CoDOMDocument60.Create;
xmldoc.load('C:\Temp\Data.xml');
xsldoc.setProperty('AllowXsltScript', True);
xsldoc.setProperty('ProhibitDTD', False);
xsldoc.setProperty('ValidateOnParse', False);
xsldoc.load('C:\Temp\Stylesheet.xsl');
transformstring := xmldoc.transformNode(xsldoc);
end;
There are different versions of MSXML, your Delphi code seems to try to use MSXML 6. I think it has, for security reasons, different default settings for various properties, one of them being related to DTDs. So, given your XSLT sample trying to use a DTD, I think you need to allow it explicitly, see https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms762632%28v%3dvs.85%29.
Therefore I think setting xsldoc.setProperty('ProhibitDTD', False)
before the load
call should help.
Additionally, as we have sorted out in the comments section, the use of the DTD together with MSXML 6 default setting to try to validate a document parsed by the load
call prevents your stylesheet from being processed, as that DTD snippet obviously doesn't specify the full grammar for an XSLT stylesheet. Thus, you need to set
xsldoc.setProperty('ValidateOnParse', False)
too.