代码之家  ›  专栏  ›  技术社区  ›  Roel

C++中的MSXML——漂亮的打印/缩进新创建的文档

  •  5
  • Roel  · 技术社区  · 16 年前

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

    谷歌显示许多人都有同样的问题——大约在2001年左右被问到。回复通常说“应用XSL转换”或“添加您自己的空白节点”。尤其是最后一个让我成功了(所以我希望在2008年有一个更简单的方法来处理MSXML输出)。所以我的问题是:有吗?我该如何使用它?

    5 回复  |  直到 9 年前
        1
  •  2
  •   Torlack    16 年前

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

    #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)));
    }
    
        2
  •  4
  •   Roel    16 年前

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

    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;
    }
    
        3
  •  0
  •   Martin Beckett    16 年前

    除非库有格式选项,否则唯一的其他方法是使用XSLT或外部漂亮的打印机(我认为htmltidy也可以使用XML)。 代码项目库中似乎没有选项,但您可以将XSLT样式表指定为MSXML。

        4
  •  0
  •   mitchnull    16 年前

    不久前,我编写了一个SED脚本,用于基本的XML缩进。如果所有其他操作都失败,则可以将其用作外部缩进器(将其保存到xmlindent.se,并使用 sed-f xmlindent.sed<文件名> )不过,您可能需要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
    }
    

    嗯,标签好像乱七八糟的…您可以从这里复制废物: XML indenting with sed(1)

        5
  •  0
  •   klaus triendl    9 年前

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

    请注意,与接受的答案一样,它不会尝试适应 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 接口。

    另一个适合我的简单解决方案是使用ADO流类:

    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