Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Tranform XML - TransformNode - Encoding error and other problems

487 views
Skip to first unread message

Robert Zurer

unread,
Jan 12, 2002, 11:18:50 PM1/12/02
to
I have been trying to tranform an XML document using an XSL Document.
I tried both TransformNode methods of IXMLNode.

--------------------------------------------------------------------

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

Jeff Rafter

unread,
Jan 13, 2002, 12:05:48 AM1/13/02
to
> I have been trying to tranform an XML document using an XSL Document.
> I tried both TransformNode methods of IXMLNode.

<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

Robert Zurer

unread,
Jan 13, 2002, 11:45:57 AM1/13/02
to
In article <3c4115ee$1_2@dnews>, .jeffr...@defined.net says...

<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

Jeff Rafter

unread,
Jan 13, 2002, 12:08:23 PM1/13/02
to

> There are a few things I had trouble with.
> In your code I assume that Data and Stylesheet are global TStream
> objects created elsewhere ?

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!

Vincent Parrett(VSoft)

unread,
Jan 13, 2002, 3:46:45 PM1/13/02
to
"Robert Zurer" <b...@bluestarscrap.com> wrote in message
news:MPG.16ab83cb6...@newsgroups.borland.com...

> Also
> > Template.stylesheet:= XSL;
> doesn't compile as the Stylesheet Property is ReadOnly
> Template._Set_Stylesheet(XSL);
> does work however.

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


Jeff Rafter

unread,
Jan 13, 2002, 4:02:26 PM1/13/02
to
I realized I left off my footnote

> Also Vincent Parrett wrote an article
> about how to do it in D5 [1]

[1] http://www.vsoft-tech.com.au/xslcomp/xsltarticle.html

Vincent Parrett(VSoft)

unread,
Jan 13, 2002, 6:17:08 PM1/13/02
to
"Jeff Rafter" <.jeffr...@defined.net> wrote in message
news:3c41f625_1@dnews...

> I realized I left off my footnote
>
> > 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

0 new messages