проверка схемы с помощью msxml в delphi
Вопрос
Я пытаюсь проверить файл XML на соответствие схемам, на которые он ссылается. (Используя Delphi и MSXML2_TLB.) (Соответствующая часть) код выглядит примерно так:
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;
Это приводит к тому, что кэш загружается ( schemas.length > 0
), но затем следующее назначение вызывает исключение: " можно использовать только XMLSchemaCache-schemacollections. " р>
Как мне поступить об этом?
Решение
Я предложил подход, который, кажется, работает. Сначала я загружаю схему явно, затем добавляю их в коллекцию схем. Затем я загружаю xml-файл и присваиваю schemacollection его свойству schemas. Теперь решение выглядит так:
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;
Важно использовать XMLSchemaCache40 или более позднюю версию. Более ранние версии не следуют стандарту XML-схемы W3C, а проверяются только по схеме XDR, спецификации MicroSoft.
Недостатком этого решения является то, что мне нужно явно загрузить схему. Мне кажется, что их можно было бы получить автоматически.
Другие советы
Хотя BennyBechDk может быть на правильном пути, у меня есть несколько проблем с его кодом, которые я собираюсь исправить ниже:
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;
Если вы хотите, чтобы система просто выдавала исключение, то нет никаких причин делать его функцией в первую очередь.
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;
Поскольку validateDoc является интерфейсом, он будет утилизирован должным образом при выходе из функции / процедуры, поэтому нет необходимости выполнять удаление самостоятельно. Если вы вызываете ValidateXmlDoc и не получаете исключения, тогда оно действительно. Лично мне нравится первый вызов, IsValidXMLDoc, который возвращает true, если он действителен, или false, если нет (и не вызывает исключений вне себя).
Я работал над решением Миэль, чтобы решить проблему. Я открываю xml дважды, один раз, чтобы получить пространства имен, а другой, после создания коллекции схем, для проверки файла. Меня устраивает. Похоже, что IXMLDOMDocument2 после открытия не принимает установленное свойство schemas.
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;
Ранее я проверил документы XML, используя следующий код:
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;