[XML] Validating with MSXML

Okay, for the current project I am working on I can only use the MSXML type library that I've imported from C:\WINNT\system32\msxml3.dll and absolutely NOTHING ELSE! I am not looking for an alternative solution. I just need some functionality that works. Absolutely NO third-party components or libraries or whatever. Just a solution to get this code to work... (And I'm using Delphi 5 so don't start talking about the XML wizard of Delphi 7. I don't have any wizards and I don't need one either.)

Did you read the above? Fine. Here's the problem.

I have an XML file which needs to be validated. Thus I also have an XSD file that is linked to it. Basically, the XML file contains a list of topics, with several questions per topic and a possible answer per question. It looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="Documents.xslt"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="D:\XML\Documents.xsd" LastUpdate="2004-11-12">
  <TopicGroup Name="EE Example">
    <Question Text="How to validate this file?">
      <Answer>I don't know...</Answer>
    </Question>
  </TopicGroup>
</Document>

And yes, there's also a stylesheet connected to this file. The stylesheet is what I will use to extract some specific data from the XML file and turn it into a HTML file. Not interesting either...

Okay, the Delphi code I have is very limited in what it can use. Basically just the ActiveX unit and the MSXML type library. It has to read the XML file, validate it with the XSD file then translate it with the XSLT file. Piece of cake, except for the validation...

uses
  Windows, SysUtils, ActiveX;

procedure TMyObject.Process;
var
  DOMDocument: IXMLDOMDocument2;
  HTMLDocument: IXMLDOMDocument2;
  StyleSheet: IXMLDOMDocument2;
  ParseError: IXMLDOMParseError;
begin
  // First create the documents I need.
  DOMDocument := CoDOMDocument.Create;
  HTMLDocument := CoDOMDocument.Create;
  StyleSheet := CoDOMDocument.Create;
  // We want to validate during parsing.
  DOMDocument.validateOnParse := True;
  // Load the XML file and the XSLT file.
  DOMDocument.load( 'D:\XML\Documents.xml' );
  StyleSheet.load( 'D:\XML\Documents.xslt' );
  // See if we got an error.
  ParseError := DOMDocument.parseError;
  if ( ParseError.errorCode <> 0 ) then ReportError( ParseError.reason );
  // For some reason, it went OK while I'm sure the XML file is NOT valid! (I made it invalid on purpose!)
  // Alternative method:
  ParseError := DOMDocument.validate;
  if ( ParseError.errorCode <> 0 ) then ReportError( ParseError.reason );
  // This just fails every time, saying: "Validate failed because the root element had no associated DTD/schema."
  // Oh, well. Transform the darned thing.
  HTMLDocument.loadXML( DOMDocument.transformNode( StyleSheet ) );
  HTMLDocument.save( 'D:\CVS\NetLeo\Documents\Documents.html' );
  // Well, that goes okay.

I know for sure the XML file is invalid yet MSXML claims it is not. (Or nags about the schema file.) Using XMLSpy, I made sure the contents of all files are as they should be. The schema and stylesheet are both valid and the XML file is just missing an attribute in one tag, making the rest valid.

If you're wondering what I'm trying to do here... I'm converting an XML file to XHTML here. Actually, there will be several transformations that I need to run on the XML file. Chances of invalid XML files is about 1 in 100, since sometimes, the process that generates the output gets killed before it has finished writing everything. (And in a rare occasion, it generates an invalid tag, but hey... I have no control over this other process.) My task is to make sure the file is valid (thus the schema) and then translate it. Preferably as fast as possible with a very small executable. Why? Because the executable will be located on a flash USB memory stick and the same stick will also have to contain the output file! (Yeah, it's a stupid method but hey... They only have to do this once per day so let them, okay?)

Maybe I should also load the Schema in an IXMLDOMDocument2 object and call some other function. Or whatever...

So, I can't spot my error. Who can?
LVL 17
Wim ten BrinkSelf-employed developerAsked:
Who is Participating?
 
Wim ten BrinkConnect With a Mentor Self-employed developerAuthor Commented:
About the solution, well... It was simple. First of all, I had to install MSXML4 and import C:\WINNT\system32\msxml4.dll instead of the one I used before. (Because MSXML doesn't have full schema support. MS Sucks!)

Then, I had to rewrite the code to something like this:

var
  DOMDocument: IXMLDOMDocument2;
  HTMLDocument: IXMLDOMDocument2;
  StyleSheet: IXMLDOMDocument2;
  ParseError: IXMLDOMParseError;
  XSLTemplate: IXSLTemplate;
  XSLProcessor: IXSLProcessor;
  Schema: XMLSchemaCache;
begin
  // Create and load document.
  DOMDocument := CoDOMDocument40.Create;
  DOMDocument.async := False;
  DOMDocument.resolveExternals := False;
  DOMDocument.validateOnParse := True;
  DOMDocument.load( 'D:\XML\Documents.xml' );
  // Create and load schema.
  Schema := CoXMLSchemaCache40.Create;
  Schema.add( '', 'D:\XML\Documents.xsd' );
  DOMDocument.schemas := Schema;
  ParseError := DOMDocument.validate;
  if ( ParseError.errorCode = 0 ) then begin
    // Create and load stylesheet.
    StyleSheet := CoFreeThreadedDOMDocument40.Create;
    StyleSheet.async := False;
    StyleSheet.resolveExternals := False;
    StyleSheet.load( 'D:\XML\Documents.xslt' );
    // Create and load XSLT template.
    XSLTemplate := CoXSLTemplate40.Create;
    XSLTemplate.stylesheet := StyleSheet;
    // Create processor.
    XSLProcessor := XSLTemplate.createProcessor;
    XSLProcessor.input := DOMDocument;
    // Transform the thing.
    if XSLProcessor.transform then begin
      HTMLDocument := CoDOMDocument40.Create;
      HTMLDocument.loadXML( XSLProcessor.output );
      HTMLDocument.save( 'D:\XML\Documents.html' );
      HTMLDocument := nil;
    end
    else begin
      MessageBox( GetDesktopWindow, 'Transformation failed.', 'Error', MB_OK );
    end;
  end
  else begin
    MessageBox( GetDesktopWindow, PChar( string( ParseError.reason ) ), 'Error', MB_OK );
  end;
  // Free the processor and the template and the stylesheet.
  DOMDocument := nil;
  HTMLDocument := nil;
  ParseError := nil;
  Schema := nil;
  StyleSheet := nil;
  XSLProcessor := nil;
  XSLTemplate := nil;
end;

Above code does exactly what I need. In case of a validation error it tells me what kind of error was generated. And if the translation fails, it tells me this too. But I have to make sure that I'm using the MSXML4 classes instead of the older versions. Probably means the user needs an update to his system too.
0
 
BigRatCommented:
Don't you need to set the Async property to false before loading the XML from a file?
0
 
Wim ten BrinkSelf-employed developerAuthor Commented:
Set it to false, no effect.
Btw. For the transformation I now use IXSLTemplate and IXSLProcessor which provide me a more powerful way to do the transformation. I'm looking for a similar solution for validations...
0
2018 Annual Membership Survey

Here at Experts Exchange, we strive to give members the best experience. Help us improve the site by taking this survey today! (Bonus: Be entered to win a great tech prize for participating!)

 
Wim ten BrinkSelf-employed developerAuthor Commented:
Don't bother anymore. Solved the problem...
0
 
Wim ten BrinkSelf-employed developerAuthor Commented:
@RomMod,
I have left a message explaining that I found a solution and even provided the solution for future reference.

I wished the comment from BigRat had been of any use for me finding the solution but it wasn't even close. I am grateful for his attempt to try and help me though. But just being nice doesn't justify awarding points at EE...
0
 
BigRatCommented:
>>I am grateful for his attempt to try and help me though.

   ... HER attempt....


No objections, RomMod.
0
 
Wim ten BrinkSelf-employed developerAuthor Commented:
Her? Oops, my mistake. Couldn't see that from here... :-)
0
All Courses

From novice to tech pro — start learning today.