Schema-Validierung mit msxml in delphi
Frage
Ich versuche, eine XML-Datei gegen das Schema zu validieren es verweist. (Mit Delphi und MSXML2_TLB.) Der (relevante Teil des) Code sieht etwa so aus:
procedure TfrmMain.ValidateXMLFile;
var
xml: IXMLDOMDocument2;
err: IXMLDOMParseError;
schemas: IXMLDOMSchemaCollection;
begin
xml := ComsDOMDocument.Create;
if xml.load('Data/file.xml') then
begin
schemas := xml.namespaces;
if schemas.length > 0 then
begin
xml.schemas := schemas;
err := xml.validate;
end;
end;
end;
Dies hat zur Folge, dass Cache geladen wird (schemas.length > 0
), aber dann ist die nächste Aufgabe löst eine Ausnahme: „nur XMLSchemaCache-schemacollections verwendet werden kann“
Wie soll ich über diese gehen?
Lösung
Ich habe mit einem Ansatz kommen, die zu funktionieren scheint. Ich lade zuerst das Schema der explizit, dann zum schemacollection hinzufügen themn. Ich lade Weiter, um die XML-Datei und die schemacollection seines Schema Eigenschaft zuweisen. Die Lösung sieht nun wie folgt aus:
uses MSXML2_TLB
That is:
// Type Lib: C:\Windows\system32\msxml4.dll
// LIBID: {F5078F18-C551-11D3-89B9-0000F81FE221}
function TfrmMain.ValidXML(
const xmlFile: String;
out err: IXMLDOMParseError): Boolean;
var
xml, xsd: IXMLDOMDocument2;
cache: IXMLDOMSchemaCollection;
begin
xsd := CoDOMDocument40.Create;
xsd.Async := False;
xsd.load('http://the.uri.com/schemalocation/schema.xsd');
cache := CoXMLSchemaCache40.Create;
cache.add('http://the.uri.com/schemalocation', xsd);
xml := CoDOMDocument40.Create;
xml.async := False;
xml.schemas := cache;
Result := xml.load(xmlFile);
if not Result then
err := xml.parseError
else
err := nil;
end;
Es ist wichtig, XMLSchemaCache40 oder später zu verwenden. Frühere Versionen des W3C XML Schema-Standard nicht folgen, sondern nur validieren gegen XDR-Schema, eine Microsoft-Spezifikation.
Der Nachteil dieser Lösung ist, dass ich ausdrücklich das Schema des laden. Es scheint mir, dass es möglich sein sollte, sie automatisch abgerufen werden.
Andere Tipps
Während BennyBechDk auf dem richtigen Weg sein könnte, habe ich ein paar Probleme mit seinem Code, den ich unten korrigieren werde:
uses Classes, XMLIntf, xmlDoc, SysUtils;
function IsValidXMLDoc(aXmlDoc: IXMLDocument): boolean;
var
validateDoc: IXMLDocument;
begin
result := false; // eliminate any sense of doubt, it starts false period.
validateDoc := TXMLDocument.Create(nil);
try
validateDoc.ParseOptions := [poResolveExternals, poValidateOnParse];
validateDoc.XML := aXmlDoc.XML;
validateDoc.Active := true;
Result := True;
except
// for this example, I am going to eat the exception, normally this
// exception should be handled and the message saved to display to
// the user.
end;
end;
Wenn Sie das System wollten nur die Ausnahme auslösen, dann gibt es keinen Grund, warum es eine Funktion in erster Linie zu machen.
uses Classes, XMLIntf, XMLDoc, SysUtils;
procedure ValidateXMLDoc(aXmlDoc: IXMLDocument);
var
validateDoc: IXMLDocument;
begin
validateDoc := TXMLDocument.Create(nil);
validateDoc.ParseOptions := [poResolveExternals, poValidateOnParse];
validateDoc.XML := aXmlDoc.XML;
validateDoc.Active := true;
end;
Da validateDoc eine Schnittstelle ist, wird es von richtig, wie die Funktion / Prozedur beendet entsorgt wird, gibt es keine Notwendigkeit, die Entsorgung selbst auszuführen. Wenn Sie ValidateXmlDoc anrufen und nicht eine Ausnahme erhalten, dann ist es gültig. Persönlich mag ich den ersten Anruf, IsValidXMLDoc die, wenn sie gültig oder falsch true zurück, wenn nicht (und nicht erhöhen Ausnahmen außerhalb von selbst).
ich arbeitete an der Miel's Lösung die disadventage zu lösen. Ich öffne die xml zweimal, einmal die Namensräume zu bekommen, und die andere, nach dem Schema Sammlung erstellen, um die Datei zu validieren. Für mich geht das. Es scheint, wie die IXMLDOMDocument2, einmal geöffnet, DO NOT den Schemata Eigenschaft akzeptiert.
function TForm1.ValidXML2(const xmlFile: String;
out err: IXMLDOMParseError): Boolean;
var
xml, xml2, xsd: IXMLDOMDocument2;
schemas, cache: IXMLDOMSchemaCollection;
begin
xml := CoDOMDocument.Create;
if xml.load(xmlFile) then
begin
schemas := xml.namespaces;
if schemas.length > 0 then
begin
xsd := CoDOMDocument40.Create;
xsd.Async := False;
xsd.load(schemas.namespaceURI[0]);
cache := CoXMLSchemaCache40.Create;
cache.add(schemas.namespaceURI[1], xsd);
xml2 := CoDOMDocument40.Create;
xml2.async := False;
xml2.schemas := cache;
Result := xml2.load(xmlFile);
//err := xml.validate;
if not Result then
err := xml2.parseError
else
err := nil;
end;
end;
Ich habe vorher validierte XML-Dokumente mit dem folgenden Code:
Uses
Classes,
XMLIntf,
SysUtils;
Function ValidateXMLDoc(aXmlDoc: IXMLDocument): boolean;
var
validateDoc: IXMLDocument;
begin
validateDoc := TXMLDocument.Create(nil);
validateDoc.ParseOptions := [poResolveExternals, poValidateOnParse];
validateDoc.XML := aXmlDoc.XML;
validateDoc.Active := true;
Result := True;
end;