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

transforming XML to HTML using XSLT

34 views
Skip to first unread message

Frank von der Weth

unread,
Mar 4, 2002, 6:10:04 AM3/4/02
to
hi!
here's my problem: i try to tramsform a xml document (UTF-16 encoded) to a
html document (ISO-8859-1 encoded) with xslt using microsoft DOM version 4.0
in delphi5.

here's the xml document (containing a greece character):

<?xml version="1.0" encoding="UTF-16"?>
<?xml-stylesheet type="text/xsl" href="Untitled1.xslt"?>
<Text>this in a &#923; </Text>

here's the xslt document:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="ISO-8859-1" />
<xsl:template match="/">
<html><head></head><body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="text">
</xsl:template>
</xsl:stylesheet>

now i use the TransformNode or TransformNodeToObject functions by MSXML to
get the html result. it works, but the result html document is always UTF-16
encoded, not ISO-8859-1.

What have I done wrong?
frank


Marrow

unread,
Mar 4, 2002, 6:44:58 AM3/4/02
to
Hi Frank,

>now i use the TransformNode or TransformNodeToObject functions by MSXML to
>get the html result. it works, but the result html document is always UTF-16
>encoded, not ISO-8859-1.

Which? TransformNode() or TransformNodeToObject()?

The reasons:-
* TransformNode() returns a BSTR (WideString) - which is UTF-16... therefore it
cannot be encoded as anything but UTF-16. This is why MSXML will ignore your
<xsl:output encoding="ISO-8859-1"/> when you use TransformNode().
* TransformNoToObject() - if the second parameter is a DOM then it will also be
UTF-16 because the DOM expects to be written to in UTF-16 encoding (similar to
the fact that in loadXML() takes a BSTR parameter) [not absolutely sure on this
one - as I never use this method!]

The solution(s):-
* Don't use TransformNode at all! :-)
* If you use TransformNodeToObject() make sure the second parameter is an object
that supports the IStream interface;
* or best use the IXSLTemplate and IXSLProcessor interfaces - and before running
the .transform() method of IXSLProcessor assign the .output property as an
object that supports the IStream interface. Then after a successful
.transform() you can read the stream (which will contain whatever encoding as a
proper stream). Note that the IXSLProcessor interface also has other
advantages - like being able to pass parameters to the stylesheet and set the
start mode.

Hope this helps
Marrow
http://www.marrowsoft.com - home of Xselerator (XSLT IDE and debugger)
http://www.topxml.com/Xselerator


Frank von der Weth wrote in message <3c83574e_1@dnews>...

Frank von der Weth

unread,
Mar 4, 2002, 8:06:42 AM3/4/02
to
hi 'marrow'
thanks for your help. i've tried the following code:

procedure TForm1.Button1Click(Sender: TObject);
VAR source : DOMDocument40;
target : DOMDocument40;
stylesheet : DOMDocument40;
sss : String;
Template : IXSLTemplate;
Processor : IXSLProcessor;
vIn, vOut : Variant;
SMem : TIMemoryStream;
wb : WORDBOOL;
begin
source:= CreateComObject(CLASS_DOMDocument) as DOMDocument;
target:= CreateComObject(CLASS_DOMDocument) as DOMDocument;
stylesheet:= CreateComObject(CLASS_FreeThreadedDOMDocument) as
DOMDocument;

source.load('untitled1.xml');
sss := source.parseError.Get_reason;
stylesheet.load ('untitled1.xslt');
sss := stylesheet.parseError.Get_reason;

Template := CreateComObject(CLASS_XSLTemplate) AS IXSLTemplate;
Template.Set_stylesheet(stylesheet);
Processor := Template.createProcessor;
Processor.setStartMode('', '');
TVarData(vIn).VType := vtVariant;
TVarData(vIn).VUnknown := Pointer(source);
Processor.Set_input(vIN);
SMem := TIMemoryStream.Create(TMemoryStream.Create);
TVarData(vOut).VType := vtVariant;
TVarData(vOut).VUnknown := SMem;
Processor.Set_output(vOut);
wb := Processor.transform;
SMem.MemoryStream.SaveToFile('output.txt');
SMem.Free;
end;

on the Set_output(vOut) i've got an access viloation. what's the problem?
frank

"Marrow" <marrow-NO-@-SPAM-marrowsoft.com> schrieb im Newsbeitrag
news:<3c835f47$1_2@dnews>...

"Marrow" <marrow-NO-@-SPAM-marrowsoft.com> schrieb im Newsbeitrag
news:3c835f47$1_2@dnews...

Marrow

unread,
Mar 4, 2002, 11:20:39 AM3/4/02
to
Hi Frank,

The following code works, try this...

unit Main;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ActiveX,
// from MSXML4 import!!...
MSXML2_TLB;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
InputXML: IXMLDOMDocument2;
InputXSL: IXMLDOMDocument2;
XSLTemplate: IXSLTemplate;
XSLProcessor: IXSLProcessor;
OutputStream: IStream;
Stat: TStatStg;
OutSize: Cardinal;
BytesRead: Cardinal;
OutputS: string;
OutputWS: WideString;
ByteOrderMarker: WideChar;
NewPos: Int64;
const
// UTF-16 BOM check...
BOM_UTF16LE = WideChar($FEFF);
begin
try
// create DOMs...
InputXML := CoDOMDocument40.Create;
InputXML.async := False;
InputXML.validateOnParse := False;
InputXML.setProperty('NewParser',True); // use MSXML4 fully!
InputXSL := CoFreeThreadedDOMDocument40.Create;
InputXSL.async := False;
InputXSL.validateOnParse := False;
InputXSL.setProperty('NewParser',True); // use MSXML4 fully!
// load XML and XSL...
if not InputXML.load('untitled1.xml') then
raise Exception.Create(InputXML.parseError.reason);
if not InputXSL.load('untitled1.xsl') then
raise Exception.Create(InputXML.parseError.reason);
// create the xsl template...
XSLTemplate := CoXSLTemplate40.Create;
XSLTemplate.stylesheet := InputXSL;
// create the xsl processor...
XSLProcessor := XSLTemplate.createProcessor;
// set the input for the processor...
XSLProcessor.input := InputXML;
// set start mode (if required)...
// XSLProcessor.setStartMode('mytestmode','');
// add any parameters (if required)...
// XSLProcessor.addParameter('mytestparam','hello','');
// create output stream...
CreateStreamOnHGlobal(0,True,OutputStream);
XSLProcessor.output := OutputStream;
// run the transformation...
if not XSLProcessor.transform then
raise Exception.Create('Unknown transformation failure');
// do whatever with the output stream...
// e.g. place it into a string...
OutputStream.Stat(Stat,0);
if (Stat.cbSize <> 0) then
begin
// check what type of string is in the stream (UTF-16 or other)...
OutputStream.Seek(0,STREAM_SEEK_SET,NewPos);
OutputStream.Read(@ByteOrderMarker,2,@BytesRead);
if ByteOrderMarker = BOM_UTF16LE then
begin
// utf-16 BOM detected - it is a widestring...
{$IFDEF STRIP_BOM}
// stript BOM from string...
SetLength(OutputWS,(Stat.cbSize - 2) div 2);
OutputStream.Read(@OutputWS[1],Stat.cbSize - 2,@BytesRead);
{$ELSE}
// include BOM in string...
OutputStream.Seek(0,STREAM_SEEK_SET,NewPos);
SetLength(OutputWS,Stat.cbSize div 2);
OutputStream.Read(@OutputWS[1],Stat.cbSize,@BytesRead);
{$ENDIF}
ShowMessage(OutputWS);
end
else
begin
// encoding may be utf-8 - but this is just a demo...
// (Note: MSXML3/4 does **not** use a UTF-8 BOM!)
SetLength(OutputS,Stat.cbSize);
OutputStream.Seek(0,STREAM_SEEK_SET,NewPos);
OutputStream.Read(@OutputS[1],Stat.cbSize,@BytesRead);
ShowMessage(OutputS);
end;
end;
except
on E: Exception do
MessageDlg(E.Message,mtError,[mbOK],0);
end;
end;

end.


Hope this helps
Marrow
http://www.marrowsoft.com - home of Xselerator (XSLT IDE and debugger)
http://www.topxml.com/Xselerator


Frank von der Weth wrote in message <3c8372a3$1_2@dnews>...

Surjit Rajput

unread,
Mar 4, 2002, 5:02:24 PM3/4/02
to
You could use the TStreamAdapter class found in the classes unit instead of
using the Win32 API directlty to create your IStream object. Personally I
prefer using CreateStreamOnHGlobal, so that you don't get the overhead of
the endless Delphi wrappers.

To create the stream object replace

CreateStreamOnHGlobal(0,True,OutputStream);

with

ATStreamClass := TMemoryStream.Create; //or any other TStream descendent
Adapt := TStreamAdapter.Create(ATStreamClass);
Adapt.Seek(0, 0, tPos);
OutputStream := Adapt as IStream;

Then use the ATStream to acces the stream.

Surjit.

"Marrow" <marrow-NO-@-SPAM-marrowsoft.com> wrote in message
news:3c839fe5_1@dnews...

Frank von der Weth

unread,
Mar 5, 2002, 2:47:14 AM3/5/02
to
hi there,

thank's a lot for the great tips, all works fine!!

regards
frank

"Surjit Rajput" <Surjit...@hotmail.com> schrieb im Newsbeitrag
news:3c83eea1_2@dnews...

0 new messages