2008-10-02 9 views
4

J'écris des fichiers XML en utilisant l'analyseur MSXML, avec un wrapper que j'ai téléchargé ici: http://www.codeproject.com/KB/XML/JW_CXml.aspx. Fonctionne bien sauf que lorsque je crée un nouveau document à partir du code (donc ne pas charger à partir du fichier et modifier), le résultat est tout en une grande ligne. J'aimerais que les éléments soient bien mis en retrait afin que je puisse les lire facilement dans un éditeur de texte. Googling montre de nombreuses personnes avec la même question - environ 2001 demandé. Les réponses indiquent généralement 'appliquer une transformation XSL' ou 'ajouter vos propres nœuds d'espaces blancs'. En particulier le dernier me fait passer à% (donc j'espère qu'en 2008, il y aura un moyen plus facile d'obtenir de jolies sorties MSXML.) Ma question est là et comment l'utiliser?MSXML from C++ - joli print/indent documents nouvellement créés

+0

Bienvenue à l'enfer Microsoft. Un labyrinthe de documentation attrayante à travers 6 versions et plusieurs langues, dont la plupart sont pour une version différente avec laquelle vous travaillez. Exemples d'utilisation de Microsoft impliquant des macros, des gotos et toutes sortes de crimes contre l'humanité. Je suis en train de créer une feuille de style et d'appliquer une fonction transformNodeToObject à l'aide de la feuille de style, malheureusement une exception non documentée est en train d'être lancée .... – Owl

Répondre

2

Essayez ceci, j'ai trouvé il y a des années sur le web.

#include <msxml2.h> 

bool FormatDOMDocument (IXMLDOMDocument *pDoc, IStream *pStream) 
{ 

    // Create the writer 

    CComPtr <IMXWriter> pMXWriter; 
    if (FAILED (pMXWriter.CoCreateInstance(__uuidof (MXXMLWriter), NULL, CLSCTX_ALL))) 
    { 
     return false; 
    } 
    CComPtr <ISAXContentHandler> pISAXContentHandler; 
    if (FAILED (pMXWriter.QueryInterface(&pISAXContentHandler))) 
    { 
     return false; 
    } 
    CComPtr <ISAXErrorHandler> pISAXErrorHandler; 
    if (FAILED (pMXWriter.QueryInterface (&pISAXErrorHandler))) 
    { 
     return false; 
    } 
    CComPtr <ISAXDTDHandler> pISAXDTDHandler; 
    if (FAILED (pMXWriter.QueryInterface (&pISAXDTDHandler))) 
    { 
     return false; 
    } 

    if (FAILED (pMXWriter ->put_omitXMLDeclaration (VARIANT_FALSE)) || 
     FAILED (pMXWriter ->put_standalone (VARIANT_TRUE)) || 
     FAILED (pMXWriter ->put_indent (VARIANT_TRUE)) || 
     FAILED (pMXWriter ->put_encoding (L"UTF-8"))) 
    { 
     return false; 
    } 

    // Create the SAX reader 

    CComPtr <ISAXXMLReader> pSAXReader; 
    if (FAILED (pSAXReader.CoCreateInstance (__uuidof (SAXXMLReader), NULL, CLSCTX_ALL))) 
    { 
     return false; 
    } 

    if (FAILED (pSAXReader ->putContentHandler (pISAXContentHandler)) || 
     FAILED (pSAXReader ->putDTDHandler (pISAXDTDHandler)) || 
     FAILED (pSAXReader ->putErrorHandler (pISAXErrorHandler)) || 
     FAILED (pSAXReader ->putProperty (
     L"http://xml.org/sax/properties/lexical-handler", CComVariant (pMXWriter))) || 
     FAILED (pSAXReader ->putProperty (
     L"http://xml.org/sax/properties/declaration-handler", CComVariant (pMXWriter)))) 
    { 
     return false; 
    } 

    // Perform the write 

    return 
     SUCCEEDED (pMXWriter ->put_output (CComVariant (pStream))) && 
     SUCCEEDED (pSAXReader ->parse (CComVariant (pDoc))); 
} 
+0

Génial, très bien travaillé. J'ai fait quelques modifications pour faire cela en mémoire, je les posterai dans un post séparé car il ne rentre pas dans un commentaire. – Roel

+0

J'aimerais vraiment pouvoir trouver moi-même la source originale. Cela a sauvé mes fesses :) – Torlack

+0

J'ai trouvé un semblable sur http://discuss.com.cn/xml/86274.html, c'est là que j'ai obtenu la partie '.GetInterfacePtr()'. Je ne parle pas chinois si je ne comprends pas la plupart de la page :) – Roel

0

À moins que la bibliothèque ait un format option alors la seule autre façon est d'utiliser XSLT, ou une jolie imprimante externe (je pense que htmltidy peut aussi faire xml) Il ne semble pas y avoir d'option dans la librairie codeproject mais vous pouvez spécifier une feuille de style XSLT à MSXML

+1

Quelle bêtise. Il n'y a en effet pas d'option dans le codeproject lib, c'est juste un wrapper barebones autour de MSXML. Y at-il un moyen facile d'appliquer la feuille de style (sans écrire le xml sur le disque et le formater là-bas, puis le relire)? À quoi ressemblerait la feuille de style? – Roel

4

est ici une version modifiée de la réponse acceptée qui tran SFORM en mémoire (modifications uniquement dans les dernières lignes, mais je poste tout le bloc pour la commodité des futurs lecteurs):

bool CXml::FormatDOMDocument(IXMLDOMDocument *pDoc) 
{ 
    // Create the writer 
    CComPtr <IMXWriter> pMXWriter; 
    if (FAILED (pMXWriter.CoCreateInstance(__uuidof (MXXMLWriter), NULL, CLSCTX_ALL))) { 
     return false; 
    } 
    CComPtr <ISAXContentHandler> pISAXContentHandler; 
    if (FAILED (pMXWriter.QueryInterface(&pISAXContentHandler))) { 
     return false; 
    } 
    CComPtr <ISAXErrorHandler> pISAXErrorHandler; 
    if (FAILED (pMXWriter.QueryInterface (&pISAXErrorHandler))) { 
     return false; 
    } 
    CComPtr <ISAXDTDHandler> pISAXDTDHandler; 
    if (FAILED (pMXWriter.QueryInterface (&pISAXDTDHandler))) { 
     return false; 
    } 

    if (FAILED (pMXWriter->put_omitXMLDeclaration (VARIANT_FALSE)) || 
     FAILED (pMXWriter->put_standalone (VARIANT_TRUE)) || 
     FAILED (pMXWriter->put_indent (VARIANT_TRUE)) || 
     FAILED (pMXWriter->put_encoding (L"UTF-8"))) 
    { 
     return false; 
    } 

    // Create the SAX reader 
    CComPtr <ISAXXMLReader> pSAXReader; 
    if (FAILED(pSAXReader.CoCreateInstance(__uuidof (SAXXMLReader), NULL, CLSCTX_ALL))) { 
     return false; 
    } 

    if (FAILED(pSAXReader->putContentHandler (pISAXContentHandler)) || 
     FAILED(pSAXReader->putDTDHandler (pISAXDTDHandler)) || 
     FAILED(pSAXReader->putErrorHandler (pISAXErrorHandler)) || 
     FAILED(pSAXReader->putProperty (L"http://xml.org/sax/properties/lexical-handler", CComVariant (pMXWriter))) || 
     FAILED(pSAXReader->putProperty (L"http://xml.org/sax/properties/declaration-handler", CComVariant (pMXWriter)))) 
    { 
     return false; 
    } 

    // Perform the write 
    bool success1 = SUCCEEDED(pMXWriter->put_output(CComVariant(pDoc.GetInterfacePtr()))); 
    bool success2 = SUCCEEDED(pSAXReader->parse(CComVariant(pDoc.GetInterfacePtr()))); 

    return success1 && success2; 
} 
+0

J'ai eu quelques difficultés à faire fonctionner ça au début, jusqu'à ce que je remarque que j'utilisais __uuidof (MXXMLWriter40) au lieu de __uuidof (MXXMLWriter). (Le code a été "réussi" mais n'a pas entraîné de XML en retrait.) Je suis un peu surpris que cela ait fait une grande différence. – Miral

+0

Le versionnement de MSXML me fait toujours aussi grimper, je ne sais pas comment ça doit fonctionner. – Roel

+1

Pour moi cela ne fonctionnait pas avec le bit ".GetInterfacePtr()". J'ai trouvé un extrait de code TRÈS similaire sans cela ici: http://social.msdn.microsoft.com/forums/vstudio/en-US/e2e1ec90-bc92-4f70-96ec-94499e862402/add-indent-spaces-to-xml -nodes-with-ms-ixmldom –

0

J'ai écrit un script sed un certain temps pour indenter XML de base. Vous pouvez l'utiliser comme un indenteur externe si tout le reste échoue (enregistrez-le dans xmlindent.sed, et traitez votre xml avec sed -f xmlindent.sed < nom de fichier >). Vous pourriez avoir besoin de cygwin ou d'un autre environnement posix pour l'utiliser.

est ici la source:

:a 
/>/!N;s/\n/ /;ta 
s///g;s/^ *//;s/ */ /g 
/^<!--/{ 
:e 
/-->/!N;s/\n//;te 
s/-->/\n/;D; 
} 
/^<[?!][^>]*>/{ 
H;x;s/\n//;s/>.*$/>/;p;bb 
} 
/^<\/[^>]*>/{ 
H;x;s/\n//;s/>.*$/>/;s/^ //;p;bb 
} 
/^<[^>]*\/>/{ 
H;x;s/\n//;s/>.*$/>/;p;bb 
} 
/^<[^>]*[^\/]>/{ 
H;x;s/\n//;s/>.*$/>/;p;s/^/ /;bb 
} 
/</!ba 
{ 
H;x;s/\n//;s/ *<.*$//;p;s/[^ ].*$//;x;s/^[^<]*//;ba 
} 
:b 
{ 
s/[^ ].*$//;x;s/^<[^>]*>//;ba 
} 

HRMP, les onglets semblent brouillées ... Vous pouvez copier les déchets d'ici à la place: XML indenting with sed(1)

0

Même mes 2 cents de 7 ans arrivent plus tard je pense La question mérite toujours une réponse simple encapsulée dans quelques lignes de code, ce qui est possible en utilisant la directive #import de Visual C++ et la bibliothèque de support C++ COM native (offrant des pointeurs intelligents et la gestion des erreurs d'encapsulation).

Notez que, comme la réponse acceptée, il n'essaie pas de rentrer dans la classe CXml que l'OP utilise, mais montre plutôt l'idée centrale. Je suppose également msxml6.

Jolie impression à un flux

void PrettyWriteXmlDocument(MSXML2::IXMLDOMDocument* xmlDoc, IStream* stream) 
{ 
    MSXML2::IMXWriterPtr writer(__uuidof(MSXML2::MXXMLWriter60)); 
    writer->encoding = L"utf-8"; 
    writer->indent = _variant_t(true); 
    writer->standalone = _variant_t(true); 
    writer->output = stream; 

    MSXML2::ISAXXMLReaderPtr saxReader(__uuidof(MSXML2::SAXXMLReader60)); 
    saxReader->putContentHandler(MSXML2::ISAXContentHandlerPtr(writer)); 
    saxReader->putProperty(PUSHORT(L"http://xml.org/sax/properties/lexical-handler"), writer.GetInterfacePtr()); 
    saxReader->parse(xmlDoc); 
} 

flux de fichier

Si vous avez besoin d'une écriture de classe de flux dans un fichier que vous pouvez écrire votre propre en mettant en œuvre l'interface IStream.

Une autre solution simple fonctionne bien pour moi est en utilisant la classe Ado Stream

void PrettySaveXmlDocument(MSXML2::IXMLDOMDocument* xmlDoc, const wchar_t* filePath) 
{ 
    ADODB::_StreamPtr stream(__uuidof(ADODB::Stream)); 
    stream->Type = ADODB::adTypeBinary; 
    stream->Open(vtMissing, ADODB::adModeUnknown, ADODB::adOpenStreamUnspecified, _bstr_t(), _bstr_t()); 
    PrettyWriteXmlDocument(xmlDoc, IStreamPtr(stream)); 
    stream->SaveToFile(filePath, ADODB::adSaveCreateOverWrite); 
} 

Encollage ensemble

Une fonction main simpliste montre en action:

#include <stdlib.h> 
#include <objbase.h> 
#include <comutil.h> 
#include <comdef.h> 
#include <comdefsp.h> 
#import <msxml6.dll> 
#import <msado60.tlb> rename("EOF", "EndOfFile") // requires: /I $(CommonProgramFiles)\System\ado 


void PrettyWriteXmlDocument(MSXML2::IXMLDOMDocument* xmlDoc, IStream* stream); 
void PrettySaveXmlDocument(MSXML2::IXMLDOMDocument* xmlDoc, const wchar_t* filePath); 


int wmain() 
{ 
    CoInitializeEx(nullptr, COINIT_MULTITHREADED); 

    try 
    { 
     MSXML2::IXMLDOMDocumentPtr xmlDoc(__uuidof(MSXML2::DOMDocument60)); 
     xmlDoc->appendChild(xmlDoc->createElement(L"root")); 

     PrettySaveXmlDocument(xmlDoc, L"xmldoc.xml"); 
    } 
    catch (const _com_error&) 
    { 
    } 

    CoUninitialize(); 

    return EXIT_SUCCESS; 
} 


// assume definitions of PrettyWriteXmlDocument and PrettySaveXmlDocument go here