8

我正在使用 MSXML 解析器写出 XML 文件,并带有我从这里下载的包装器:http: //www.codeproject.com/KB/XML/JW_CXml.aspx。效果很好,除了当我从代码创建一个新文档(所以不从文件加载和修改)时,结果都在一个大行中。我希望元素能够很好地缩进,以便我可以在文本编辑器中轻松阅读。

谷歌搜索显示许多人有相同的问题 - 大约在 2001 年左右被问到。回复通常会说“应用 XSL 转换”或“添加您自己的空白节点”。尤其是最后一个让我去 %( 所以我希望在 2008 年有一种更简单的方法来漂亮的 MSXML 输出。所以我的问题;有吗,我该如何使用它?

4

5 回答 5

4

这是已接受答案的修改版本,它将在内存中进行转换(仅在最后几行更改,但为了方便未来的读者,我发布了整个块):

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;
}
于 2008-10-02T21:45:03.473 回答
3

即使我的 2 美分在 7 年后到货,我认为这个问题仍然值得一个简单的答案,只需几行代码,这可以通过使用 Visual C++ 的#import指令和本机 C++ COM 支持库(提供智能指针和封装错误处理)来实现.

请注意,就像接受的答案一样,它不会尝试适应CXmlOP 正在使用的类,而是显示核心思想。我也假设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有一个类,您可以使用它或从中推断如何编写自己的。

另一个对我来说效果很好的简单解决方案是利用 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
于 2016-05-02T12:28:59.833 回答
2

试试这个,这是我几年前在网上找到的。

#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)));
}
于 2008-10-02T21:18:59.200 回答
0

除非库有格式选项,否则唯一的另一种方法是使用 XSLT,或外部漂亮的打印机(我认为 htmltidy 也可以做 xml)codeproject 库中似乎没有选项,但您可以指定 XSLT样式表到 MSXML。

于 2008-10-02T21:06:50.103 回答
0

我已经为基本的 xml 缩进编写了一个 sed 脚本。如果一切都失败了,您可以将其用作外部缩进器(将其保存到 xmlindent.sed,并使用sed -f xmlindent.sed <filename>处理您的 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,标签似乎乱码......您可以从这里复制浪费:XML indenting with sed(1)

于 2008-10-02T21:46:14.283 回答