Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Perl > Perl Misc > Windows ActiveState Perl: MSXML transformNodeToObject finally succeeded

Reply
Thread Tools

Windows ActiveState Perl: MSXML transformNodeToObject finally succeeded

 
 
tuser
Guest
Posts: n/a
 
      02-17-2006
I have finally found a solution for my long-standing problem
with Xslt-transformation under Windows ActiveState Perl and
I thought that other people might have the same problem so I
would like to share my solution with the group. I hope you
don't mind this long post, here is the story:

I had read an article by Shawn Ribordy on
http://www.perl.com/pub/a/2001/04/17/msxml.html
('MSXML, It's Not Just for VB Programmers Anymore')
in which he described how to do Xslt-transform on XML-files
using the "transformNodeToObject" method of a Win32::OLE
object.

The following lines are copied straight from his article:

>> $doc_to_transform->transformNodeToObject($style_sheet_doc,
>> $transformed_doc);
>> $transformed_doc->save("$new_xml_doc_file");


"Great...", I thought, "...let's try this at home".

So I sat down at my Windows XP computer (with Activestate
v5.8.7 and the latest Msxml2.DOMDocument.4.0/SP2 installed),
fired up notepad.exe and pasted Shawn's example straight
into my perl program, and his example worked -- but that
was as far as it got!

When I started to use my own xslt-stylesheet, things went
seriously wrong. Well, I knew that my own xslt-stylesheets
had some problems, but I hoped (and expected) that the
transformNodeToObject() method would throw something useful
at me (which unfortunately it did not!) The problem was
that Shawn's example did not have any error handling
whatsoever.

I googled every possible combination of (perl, xslt, msxml,
win32, errorhandling) under the sun and I searched CPAN to
destruction, but to no avail.

Finally, after months of "pulling out my hair" I finally
stumbled upon the following variables/functions which
allowed me to correctly and reliably test for (almost)
every possible error condition.
- Win32::OLE::LastError()
- $doc->{parseError}->{reason}
- $doc->{parseError}->{line};
- $doc->{parseError}->{linePos};
- $doc->{parseError}->{srcText};

With the improved error-handling, I was now able to
experiment with different situations in my xslt-stylesheets.
Here is what I experienced:

XML-input-files: Use <?xml version='1.0' encoding='...'?>
================================================== =======
In your XML-Input-Files, always specify the encoding in the
first line <?xml version='1.0' encoding='...'?>. This is
'ISO-8859-1' for plain old ASCII, but could also be 'UTF-8'
or 'UTF-16' if your XML-Input-File is set-up this way.
If you don't respect the correct encoding, you will end up
with an error ("An invalid character was found in text
content")

XSLT-files: Use <?xml version='1.0' encoding='...'?>
================================================== ==
In your XSLT-Files, always specify the encoding in the first
line <?xml version='1.0' encoding='...'?>.
Strictly speaking it is not necessary to specify the encoding
in the first line of the XSLT-file, a simple
<?xml version='1.0'?> is enough. but by doing so, you let
Microsoft guess the encoding, which it does correctly in 95%
of the cases. However, in the remaining 5% of the cases,
Microsoft gets it wrong and you end up with an error
("Switch from current encoding to specified encoding not
supported"). Consequently, I suggest to always specify the
actual encoding directly in the first line of the XSLT-file.

XSLT-files: Use <xslutput encoding='ISO-8859-1'/>
================================================== =
It is more convenient to use
<xslutput encoding='ISO-8859-1'/> in your XSLT-file. This
works very well, even with accented characters and Umlaute.
You can use other encodings (such as
<xslutput encoding='UTF-8'/>), and the XML-Output-File
will be displayed correctly in Internet Explorer, but then
you will find it inconvenient that Notepad does not display
the XML-Output-file correctly any more.

XSLT-files: Use <xslutput method='xml'/>
==========================================
If you want to generate Html, you can do so easily by
generating an XML file with its tags in Html-syntax
(such as <p>, <table>, <hr/>, etc...). However, do not
attempt to use <xslutput method='html'/> in your XSLT-file,
use <xslutput method='xml'/> instead (even if you want to
generate 'Html', think of 'XHtml' and use
<xslutput method='xml'/>). You may end in up tears when you
discover that by using <xslutput method='html'/>, your
encoding does not work the way you want to. And you might
even discover that '&#160' and/or '&nbsp' will cause an error
after having erased your output-file! - Why is that so? - I
don't know.
The ultimate rule is: Never use 'html' as your method in
<xslutput method='...'/>, you must use
<xslutput method='xml'/> at all times.

XSLT-files: Use <xslutput indent='yes'/>
==========================================
This advice is more for convenience than anything else. If
you specify <xslutput indent='yes'/> and you look at your
XML-Output-file with Notepad, you will find that its
linebreaks are more conveniently located than they would
have been without <xslutput indent='yes'/>. It is still
not perfect, but it is better. So finally, the
<xslutput... /> line in your XSLT-file should look like
this:
<xslutput method='xml' indent='yes' encoding='ISO-8859-1'/>

In XSLT-files: Use ' ' instead of '&nbsp;'
===============================================
The instruction '&nbsp;' does not work with MSXML. If you
want your XSLT-file to generate a non-breaking space, use
' ' instead.


....that's the end of my list.

For those of you who want to try, here is a test program:

use strict;
use warnings;
use Win32::OLE;

my $MxErr;

testcase(1, 'transformation succeeds');
testcase(2, 'unbalanced tags in *.xml');
testcase(3, 'unbalanced tags in *.xsl');
testcase(4, 'syntax error in *.xsl');
testcase(5, 'output method=html fails');

sub testcase {
my ($Case, $Description) = @_;

makefiles($Case);

system('cls');
print "Testcase no $Case: $Description\n";

print "\n\nThis is the xml file 'test$Case.xml':\n";
print "============================================= \n";
system("type test$Case.xml");
print "============================================= \n";
system('pause');

print "\n\nThis is the xsl file 'trf$Case.xsl':\n";
print "============================================= \n";
system("type trf$Case.xsl");
print "============================================= \n";
system('pause');

my $success = TransformXslt(xml => "test$Case.xml",
xslt => "trf$Case.xsl",
out => "output$Case.html");

if ($success) {
print "\n\nTransformXslt succeeded, result:\n";
print "=========================================\n";
system("type output$Case.html");
print "=========================================\n";
}
else {
print "\n\nProblem with TransformXslt:\n";
print "=========================================\n";
print "$MxErr\n";
print "=========================================\n";
}
system('pause');
print "\n";
}

sub makefiles {
my ($Case) = @_;

my $XData = ($Case == 2 ? 'data1' : 'data');
my $XTitle = ($Case == 3 ? 'title1' : 'title');
my $XFunc = ($Case == 4 ? 'r([?' : '.');
my $XMethod = ($Case == 5 ? 'html' : 'xml');

open OFL, '>', "test$Case.xml"
or die "err write test$Case.xml: $!";
print OFL qq{<?xml version="1.0"}.
qq{ encoding="ISO-8859-1"?>\n};
print OFL qq{<index>\n};
print OFL qq{ <data>aaaa</$XData>\n};
print OFL qq{ <data>bbbb</data>\n};
print OFL qq{</index>\n};
close OFL;

open OFL, '>', "trf$Case.xsl"
or die "err write trf$Case.xsl: $!";
print OFL qq{<?xml version="1.0"}.
qq{ encoding="ISO-8859-1"?>\n};
print OFL qq{<xsl:stylesheet version="1.0"\n};
print OFL qq{xmlnssl="http://www.w3.org/1999}.
qq{/XSL/Transform">\n};
print OFL qq{ <xslutput method="$XMethod" indent=}.
qq{"yes" encoding="ISO-8859-1"/>\n};
print OFL qq{ <xsl:template match="/">\n};
print OFL qq{ <html>\n};
print OFL qq{ <body>\n};
print OFL qq{ <title>Test</$XTitle>\n};
print OFL qq{ <p>nonbreaking space</p>\n};
print OFL qq{ <hr/>\n};
print OFL qq{ <xsl:for-each select="index/data">\n};
print OFL qq{ <p>Test: *** <xsl:value-of}.
qq{ select="$XFunc"/> ***</p>\n};
print OFL qq{ </xsl:for-each>\n};
print OFL qq{ </body>\n};
print OFL qq{ </html>\n};
print OFL qq{ </xsl:template>\n};
print OFL qq{</xsl:stylesheet>\n};
close OFL;
}

sub TransformXslt {
my ($xml_input_file, $xslt_file, $xml_output_file)
= ($_[1], $_[3], $_[5]);
$MxErr = '';
my $DomDocument = 'Msxml2.DOMDocument.4.0';

# Load the document (Xml-Input-File)
my $xml_input_doc = Win32::OLE->new($DomDocument);
unless ($xml_input_doc) {
$MxErr = qq{Mx-0040: Couldn't create Win32::OLE}.
qq{ $DomDocument for XML-Input-File}.
qq{ "$xml_input_file"};
return undef;
}

$xml_input_doc->{async} = 'False';
$xml_input_doc->{validateOnParse} = 'True';
if (!$xml_input_doc->Load($xml_input_file)) {
my $Rs = $xml_input_doc->{parseError}->{reason};
$Rs =~ s/\r//; chomp $Rs;
my $Ln = $xml_input_doc->{parseError}->{line};
my $Ps = $xml_input_doc->{parseError}->{linePos};
my $Tx = $xml_input_doc->{parseError}->{srcText};
$MxErr = qq{Mx-0060: XML-Input-File}.
qq{ "$xml_input_file"}.
qq{ did not load for $DomDocument at line}.
qq{ $Ln, pos $Ps, reason: $Rs, text: '$Tx'};
return undef;
}

# create Output-object
my $xml_output_doc = Win32::OLE->new($DomDocument);
unless ($xml_output_doc) {
$MxErr = qq{Mx-0055: Couldn't create Win32::OLE}.
qq{ $DomDocument for XML-Output-File}.
qq{ "$xml_output_file"};
return undef;
}

# Load the Stylesheet (Xsl-File)
my $xslt_doc = Win32::OLE->new($DomDocument);
unless ($xslt_doc) {
$MxErr = qq{Mx-0050: Couldn't create Win32::OLE}.
qq{ $DomDocument for XSLT-File "$xslt_file"};
return undef;
}

$xslt_doc->{async} = 'False';
$xslt_doc->{validateOnParse} = 'True';
if (!$xslt_doc->Load($xslt_file)) {
my $Rs = $xslt_doc->{parseError}->{reason};
$Rs =~ s/\r//; chomp $Rs;
my $Ln = $xslt_doc->{parseError}->{line};
my $Ps = $xslt_doc->{parseError}->{linePos};
my $Tx = $xslt_doc->{parseError}->{srcText};
$MxErr = qq{Mx-0070: XSLT-file "$xslt_file" did not}.
qq{ load for $DomDocument at line}.
qq{ $Ln, pos $Ps, reason: $Rs, text: '$Tx'};
return undef;
}

# Do the work: transform xml using an xslt stylesheet
$xml_input_doc->transformNodeToObject($xslt_doc,
$xml_output_doc);
if (Win32::OLE::LastError()) {
my $Rs = Win32::OLE::LastError(); $Rs =~s/\s+/ /g;
$MxErr = qq{Mx-0080: XSLT-file "$xslt_file" has}.
qq{ syntax-errors for $DomDocument, }.
qq{reason: $Rs};
return undef;
}

# Save the done work to the output-file
$xml_output_doc->save($xml_output_file);
if (Win32::OLE::LastError()) {
my $Rs = Win32::OLE::LastError(); $Rs =~s/\s+/ /g;
$MxErr = qq{Mx-0090: Can't save to output-file}.
qq{ "$xml_output_file" for $DomDocument, }.
qq{reason: $Rs};
return undef;
}

# "-z" tests for empty file, which is considered to be
# a fatal error
if (-z $xml_output_file) {
$MxErr = qq{Mx-0100: A fatal error occured in either}.
qq{ your XSLT-file "$xslt_file", or in}.
qq{ your XML-input-file "$xml_input_file",}.
qq{ the output-file "$xml_output_file" will}.
qq{ be empty.};
return undef;
}

return 1;
}

 
Reply With Quote
 
 
 
 
Samwyse
Guest
Posts: n/a
 
      02-18-2006
tuser wrote:
> I have finally found a solution for my long-standing problem
> with Xslt-transformation under Windows ActiveState Perl and
> I thought that other people might have the same problem so I
> would like to share my solution with the group.


Thank you very much for posting this. While I doubt that I will ever
have any need for your specific solution, I hope that you will serve as
an example to others. Many times when researching a problem, I will
find USENET posts from people with the same problem as me, but never any
hint of how it was eventually solved. I sincerely wish that others will
remember this post and share whatever solutions they find for their
problems.
 
Reply With Quote
 
 
 
 
robic0
Guest
Posts: n/a
 
      02-18-2006
On 17 Feb 2006 13:14:27 -0800, "tuser" <(E-Mail Removed)> wrote:

>I have finally found a solution for my long-standing problem
>with Xslt-transformation under Windows ActiveState Perl and
>I thought that other people might have the same problem so I
>would like to share my solution with the group. I hope you
>don't mind this long post, here is the story:
>
>I had read an article by Shawn Ribordy on
>http://www.perl.com/pub/a/2001/04/17/msxml.html
>('MSXML, It's Not Just for VB Programmers Anymore')
>in which he described how to do Xslt-transform on XML-files
>using the "transformNodeToObject" method of a Win32::OLE
>object.
>

Good job! You have used a bunch of modules.
Style sheet transforms? I'm willing to bet you don't know
a rats ass about markup at all !! You've quoted code and
folks that do though...
I wouldn't hire you to clean the toilets!

 
Reply With Quote
 
Tad McClellan
Guest
Posts: n/a
 
      02-18-2006
tuser <(E-Mail Removed)> wrote:


> sub TransformXslt {
> my ($xml_input_file, $xslt_file, $xml_output_file)
> = ($_[1], $_[3], $_[5]);



An "array slice" would make that much prettier:

my ($xml_input_file, $xslt_file, $xml_output_file) = @_[1,3,5];


--
Tad McClellan SGML consulting
http://www.velocityreviews.com/forums/(E-Mail Removed) Perl programming
Fort Worth, Texas
 
Reply With Quote
 
tuser
Guest
Posts: n/a
 
      02-19-2006

Tad McClellan wrote:
> tuser <(E-Mail Removed)> wrote:
>
>
> > sub TransformXslt {
> > my ($xml_input_file, $xslt_file, $xml_output_file)
> > = ($_[1], $_[3], $_[5]);

>
>
> An "array slice" would make that much prettier:
>
> my ($xml_input_file, $xslt_file, $xml_output_file) = @_[1,3,5];


Thanks for your input, I haven't thought of using array slices in perl
before.
I will use that in my program.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Heads up, Update 955069 breaks transformNodeToObject in ASP Anthony Jones ASP General 4 12-19-2008 04:23 AM
IIS7 ASP Response object incompatible with MSXML transformNodeToObject Anthony Jones ASP General 6 08-13-2008 04:31 PM
XSL and transformNodeToObject mtugnoli XML 2 03-27-2007 03:02 PM
ANN: Ruby support is (finally) in ActiveState Komodo on OS X Eric Promislow Ruby 1 08-06-2005 10:42 PM
OT: Has anybody succeeded in subscribing to Individual.Net? PeteCresswell HTML 2 05-20-2005 04:17 PM



Advertisments