C ++のMSXML-新しく作成されたドキュメントをきれいに印刷/インデントします

StackOverflow https://stackoverflow.com/questions/164575

  •  03-07-2019
  •  | 
  •  

質問

ここからダウンロードしたラッパーを使用して、MSXMLパーサーを使用してXMLファイルを書き出しています: http://www.codeproject.com/KB/XML/JW_CXml.aspx 。コードから新しいドキュメントを作成するとき(ファイルからロードして変更しないように)、結果がすべて1つの大きな行になることを除いて、すばらしい動作をします。テキストエディターで簡単に読み込めるように、要素を適切にインデントする必要があります。

グーグルは、同じ質問を持つ多くの人々を示しています-2001年頃に尋ねました。通常、返信には「XSL変換を適用する」または「独自の空白ノードを追加する」と表示されます。特に最後の1つは私を%(私が行くので、2008年にMSXML出力をきれいにする簡単な方法があることを望んでいます。私の質問です。

役に立ちましたか?

解決

これを試してみて、この数年前にウェブで見つけました。

#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)));
}

他のヒント

これは、インメモリを変換する受け入れられた回答の修正版です(最後の数行でのみ変更されますが、将来の読者の便宜のためにブロック全体を投稿しています):

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;
}

2セントでも7年後に届きますが、この質問はまだ数行のコードに包まれた簡単な答えに値すると思います。これは、Visual C ++の#importディレクティブとネイティブC ++ COMサポートライブラリ(スマートポインターおよびカプセル化エラー処理)。

受け入れられた答えのように、OPが使用しているCXmlクラスに適合しようとするのではなく、中核的なアイデアを示していることに注意してください。また、msxml6を想定しています。

任意のストリームへのプリティプリント

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);
}

ファイルストリーム

ファイルへのストリーム書き込みが必要な場合は、IStreamインターフェースの実装が必要です。
wtlext にはクラスがあり、これを使用したり、そこから独自の記述方法を推測したりできます。

もう1つの簡単な解決策は、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);
}

一緒に接着

単純なmain関数が実際にこれを示しています:

#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

ライブラリに形式オプションがない限り、唯一の他の方法はXSLTまたは外部のきれいなプリンターを使用することです(htmltidyはxmlもできると思います) codeproject libにはオプションはないようですが、MSXMLにXSLTスタイルシートを指定できます。

基本的なxmlインデント用にsedスクリプトを少し書きました。他のすべてが失敗した場合、外部インデンターとして使用できます(これをxmlindent.sedに保存し、 sed -f xmlindent.sed <!> lt; filename <!> gt; でxmlを処理します) 。ただし、cygwinまたは他のposix環境を使用する必要がある場合があります。

ソースは次のとおりです。

: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、タブが文字化けしているようです...代わりにここからコピーして廃棄することができます: sed(1)によるXMLインデント

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top