2012-05-27 15 views
6

Quando provo a leggere un documento XML (file aquila) con un DTD ottengo l'errore:C++ Builder XE2, TXMLDocument 'DTD è proibito'

Project xx raised exception class EDOMParserError with message 'DTD is prohibited'

L'intestazione XML assomiglia a questo:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE eagle SYSTEM "eagle.dtd"> 

Se rimuovo la seconda linea ...

<!DOCTYPE eagle SYSTEM "eagle.dtd"> 

... tutto funziona bene.

Dopo un po 'googling sembra che il parser MSXML hanno un'opzione chiamata'prohibitDTD' impostata su true di default (nelle versioni precedenti era falso).

Tuttavia non sembra possibile impostare questa opzione su false dalla classe TXMLDocument. Una soluzione sembra essere una ricompilazione della libreria .pas o creare l'interfaccia da solo con CoCreateInstance().

Tutti gli esempi che ho visto sono in Delphi e ho difficoltà a trascriverli a C++ Builder.

Qualcuno sa come leggere un documento XML DTD con C++ Builder XE2?

Il mio codice di esempio ...

#include <xmldoc.hpp> 

_di_IXMLNode XMLObject; 

TXMLDocument *XMLDocument = new TXMLDocument(this); 
XMLDocument->LoadFromFile(fileName); // <----- Exception EDOMParserError 
XMLObject = XMLDocument->DocumentElement; 

Grazie ...

risposta

5

XE2 ha introdotto una soluzione nativa proprio a questo problema: esiste una variabile globale bool denominata MSXML6_ProhibitDTD dichiarata in Xml.Win.msxmldom.hpp. È possibile impostare a false prima di caricamento dei dati in TXMLDocument:

#include <xmldoc.hpp> 
#include <msxmldom.hpp> 

MSXML6_ProhibitDTD = false; 
TXMLDocument *XMLDocument = new TXMLDocument(this): 
XMLDocument->LoadFromFile(fileName); 
_di_IXMLNode XMLObject = XMLDocument->DocumentElement; 

Su un lato nota: non è generalmente una buona idea per creare TXMLDocument istanze in modo dinamico come questo.E 'meglio utilizzare l'interfaccia IXMLDocument invece:

#include <xmldoc.hpp> 
#include <msxmldom.hpp> 

MSXML6_ProhibitDTD = false; 
_di_IXMLDocument XMLDocument = LoadXMLDocument(fileName); 
_di_IXMLNode XMLObject = XMLDocument->DocumentElement; 
+0

Grazie, funziona come un fascino! Strano che questa stessa informazione debba essere così difficile da trovare ... Perché IXMLDocument è meglio di TXMLDocument? Come ho capito, IXMLDocument è una parte di TXMLDocument. –

+0

'TXMLDocument' implementa l'interfaccia' IXMLDocument', quindi ha la stessa funzionalità. Tuttavia, se istanziate dinamicamente 'TXMLDocument' con un proprietario' NULL' (che dovreste fare quando create oggetti XML di breve durata), agisce come un oggetto conteggiato con riferimento. Questo è un comportamento documentato. Non è sicuro assegnare un'istanza dinamica di 'TXMLDocument' a una variabile' TXMLDocument * 'a meno che non sia stato assegnato un Proprietario. Altrimenti, devi invece assegnarlo a una variabile '_di_IXMLDocument' per mantenere correttamente il conteggio dei riferimenti. –

+0

Grazie per la spiegazione. Nel mio caso l'istanza dinamica di TXMLDocument * ha sempre un proprietario e vive attraverso l'intera applicazione. Ho un codice di progetto precedente che funziona con TXMLDocument, quindi lo utilizzo per riutilizzare il vecchio codice. –

0

È necessario copiare MSXMLDOM.pas nella cartella di progetto, e modificarlo al fine di risolvere questo problema.

Modificare l'implementazione di function TMSDOMDocument.GetMSDocument in seguito e quindi ricostruire il progetto.

Nota: è necessario utilizzare IXMLDOMDocument2.setProperty invece di accedere direttamente a ProhibitDTD, poiché IXMLDOMDocument2 non pubblica ProhibitDTD.

function TMSDOMDocument.GetMSDocument: IXMLDOMDocument; 
var 
    Doc2: IXMLDOMDocument2; 
begin 
    Result := MSNode as IXMLDOMDocument; 
    if Supports(Result, IXMLDOMDocument2, Doc2) then 
     Doc2.setProperty('ProhibitDTD', False); 
end; 

Si noti che questo funziona solo se si è non edificio con i pacchetti di runtime!

Questa soluzione proviene da un post sui forum Embarcadero fatto da un membro del TeamB; Mi sono ricordato di averlo letto e l'ho trovato in una ricerca di questi forum via CodeNewsFast - le funzionalità di ricerca nei forum EMBT non hanno mai funzionato bene e una ricostruzione o reindex recente o qualcosa lo hanno reso ancora peggiore di prima. :-)

+0

Sono il membro TeamB che hanno discusso questa soluzione alternativa nei forum EMBT. Questa soluzione alternativa è necessaria solo in XE e versioni precedenti. XE2 ha introdotto una soluzione nativa per questo esatto problema. Vedi la mia risposta. –

+0

Sì, @Remy. Lo so. Ecco perché ho pubblicato il link alla ricerca da cui l'ho preso; quindi la gente avrebbe saputo che non mi stavo prendendo in considerazione per aver trovato la soluzione. :-) –

1

Dal momento che la soluzione alternativa con la variabile MSXML6_ProhibitDTD globale è deprecato e non ho potuto farlo funzionare con XE5 sia, qui è un'altra soluzione:

Come indicato nel documentation, c'è questo metodo per modificare la proprietà DOM

Xml.Win.Msxmldom.MSXMLDOMDocumentFactory.AddDOMProperty 

Purtroppo non è così banale utilizzare questo ...

includono l'intestazione per questo spazio dei nomi:

#include <Xml.Win.msxmldom.hpp> 

Foo::Foo() 
{ 
    //change the dom property in your constructor. 
    ((TMSXMLDOMDocumentFactory*)Xml::Win::Msxmldom::MSXMLDOMDocumentFactory)->AddDOMProperty("ProhibitDTD", False, true); 
} 

e accesso a questo metodo. (Il cast è necessario, perché la MSXMLDOMDocumentFactory stesso è ereditato da un'interfaccia metaclasse o giù di lì non mi ottenuto il concetto che sta dietro..)

ispirato da un blog Delphi: https://bobsotherblog.wordpress.com/2013/09/19/fixing-dtd-is-prohibited-error-in-delphi/