--------------------------------------------------------------------
1) (XMLDocument_Data and XMLDocument_StyleSheet are both TXMLDocument
objects residing on the WebModule with their FileName properties set to
valid xml and xsl documents.)
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
S: WideString;
begin
XMLDocument_Data.DocumentElement.TransformNode
(XMLDocument_StyleSheet.DocumentElement, S);
Response.Content := S;
end;
This version causes an error in the browser.
Switch from current encoding to specified encoding not supported. Error
processing resource 'http://localhost/scripts/Project1.dll'. Line 1,
Position 40
<?xml version="1.0" encoding="UTF-16"?> <span style="color:blue" ...
etc.
Both the source xml document and the xsl document have their encoding
set to UTF-8. I don't know why the string sent to the browser is
encoding="UTF-16"
--------------------------------------------------------------------
2) (XMLDocument_Result is also on the WebModule with the FileName
Property blank. I assumed its XML property will be filled by
TransformNode)
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
XMLDocument_Data.DocumentElement.TransformNode
(XMLDocument_StyleSheet.DocumentElement, XMLDocument_Result);
Response.Content := XMLDocument_Result.XML.Text;
end;
This yields no error in then browser but also yields no data -- a blank
screen.
What am I doing wrong?
--------------------------------------------------------------------
In scouring the archives I found the code below. It works perfectly. I
get all my data and all my formatting so I know that there is no problem
within my documents.
function TransformXMLText(XMLText, XSLText: string): string;
var
xslFileObj, xmlFileObj: OleVariant;
begin
xslFileObj := CreateOLEObject('MSXML2.DOMDocument');
xslFileObj.Async := False;
xslFileObj.LoadXML(XSLText);
xmlFileObj := CreateOLEObject('MSXML2.DOMDocument');
xmlFileObj.Async := False;
xmlFileObj.LoadXML(XMLText);
Result := xmlFileObj.TransformNode(xslFileObj);
end;
Robert Zurer
<snip/>
This is probably caused by the fact the TransformNode always returns the
value as a BSTR (aka widestring) and therefore is always treated as UTF-16.
This is okay unless your document is labeled as UTF-8. The only way to
properly get around this is to use the function transformNodeToObject or to
use the IXSLTemplate and Stylesheet interfaces to get around this. The
latter is the recommended solution.
var XML : IXMLDomDocument2;
XSL : IXMLDomDocument2;
Template : IXSLTemplate;
Processor : IXSLProcessor;
Output : TStream;
begin
// Function tranforms Data using the Stylesheet, outputs to
// Response.ContentStream:
// Load the XML into an DOM
XSL:= CoFreeThreadedDomDocument30.Create;
XML:= CoFreeThreadedDomDocument30.Create;
// Create the Result stream
Output:= TMemoryStream.Create;
try
// Reset the stream
Data.Seek(0,0);
// Load the XML
XML.load(TStreamAdapter.Create(Data) as IStream);
XSL.load(Stylesheet);
// Check for errors
if (XML.parseError <> nil) and (XML.parseError.errorCode <> 0) then
raise Exception.Create('Loading XML File - ' + XML.parseError.reason +
' Line ' + IntToStr(XML.parseError.line) + ' Column ' +
IntToStr(XML.parseError.linepos));
if (XSL.parseError <> nil) and (XSL.parseError.errorCode <> 0) then
raise Exception.Create('Loading XSL Stylesheet - ' +
XSL.parseError.reason + ' Line ' + IntToStr(XSL.parseError.line) + ' Column
' + IntToStr(XSL.parseError.linepos) + ' (' + './' + Stylesheet + ')');
// Create a template
Template:= CoXSLTemplate30.Create;
Template.stylesheet:= XSL;
Processor:= Template.createProcessor;
Processor.input:= XML;
Processor.output:= TStreamAdapter.Create(Output) as IStream;
// Transform
if not Processor.transform then
raise Exception.Create('There was an error attempting to transform the
document');
// Reset the stream
Output.Seek(0,0);
// Set the output
Response.ContentStream:= Output;
finally
// Don't free the stream or the adapter
Processor:= nil;
Template:= nil;
XSL:= nil;
XML:= nil;
end;
HTH,
--
Jeff Rafter
Defined Systems
http://www.defined.net
XML Development and Developer Web Hosting
<snip>
Thanks very much for your help.
There are a few things I had trouble with.
In your code I assume that Data and Stylesheet are global TStream
objects created elsewhere ?
> Data.Seek(0,0);
> XML.load(TStreamAdapter.Create(Data) as IStream);
Also
> Template.stylesheet:= XSL;
doesn't compile as the Stylesheet Property is ReadOnly
Template._Set_Stylesheet(XSL);
does work however.
But the big problem is that
Response.ContentStream:= Output;
causes the dll to hang badly.
But if I input two TStringStream objects i.e.
Data := TStringStream.Create(XMLDocument_Data.XML.Text);
Stylesheet := TStringStream.Create(XMLDocument_StyleSheet.XML.Text);
rather than TMemoryStreams all is well.
I don't understand why however. According the help
TXMLDocument.SaveToStream(Stream) writes the *content* of the document
to a stream. Since the content is a string I don't know why this doesn't
work.
Also, I don't know why this solution is more efficient than
function TransformXMLText(XMLText, XSLText: string): string;
var
xslFileObj, xmlFileObj: OleVariant;
begin
xslFileObj := CreateOLEObject('MSXML2.DOMDocument');
xslFileObj.Async := False;
xslFileObj.LoadXML(XSLText);
xmlFileObj := CreateOLEObject('MSXML2.DOMDocument');
xmlFileObj.Async := False;
xmlFileObj.LoadXML(XMLText);
Result := xmlFileObj.TransformNode(xslFileObj);
end;
followed by
Response := TransformXMLText(XMLDocument_Data.XML.Text,
XMLDocument_StyleSheet.XML.Text);
Robert Zurer
Yeah... I think : )
> Also
> > Template.stylesheet:= XSL;
> doesn't compile as the Stylesheet Property is ReadOnly
> Template._Set_Stylesheet(XSL);
> does work however.
Hmm... I am not exactly sure why that is... I suppose I should mention (as
it looks like you are using D6 with ISAPI) that there is a TXSLPageProducer
that has this behavior "built in". Also Vincent Parrett wrote an article
about how to do it in D5 [1]
> But the big problem is that
> Response.ContentStream:= Output;
> causes the dll to hang badly.
Hmmm... Are you freeing Output? The Reponse object should handle this for
you.
> I don't understand why however.
I am as confused as you are--
> Also, I don't know why this solution is more efficient than
It is more efficient only because you can cache the stylesheet and reuse it
(prevents reloading/reparsing). Also the transformNode function is older--
the new model is just that-- newer it has been optimized more according to
Microsoft. Essentially though it shouldn't be too different. If you wanted
you could simply use transformNodeToObject. The Object would be the IStream.
Good Luck!
The problem here is Delphi 6's import type library. If you have Delphi 5,
import the msxml typlibrary with it and use the D5 import unit with D6. D6
wrongly imports many typlibraries, making properties read only when they
should be read/write.
HTH
Vincent Parrett
AtoZed Software
Email vincent [ at ] atozedsoftware dot com
-------------------------------------------------------------------
Automate your build process with FinalBuilder
IntraWeb - True RAD development for the Web
http://www.atozedsoftware.com
> Also Vincent Parrett wrote an article
> about how to do it in D5 [1]
[1] http://www.vsoft-tech.com.au/xslcomp/xsltarticle.html
Actually, a more up to date version of this article is here :
http://community.borland.com/article/0,1410,27106,00.html
Regards