MSXML из C++ - красивые печати / отступы для вновь созданных документов

Я пишу XML-файлы с помощью парсера MSXML с оболочкой, которую я скачал отсюда: http://www.codeproject.com/KB/XML/JW_CXml.aspx. Отлично работает, за исключением того, что когда я создаю новый документ из кода (а не загружаю из файла и не изменяю), результат отображается в одной большой строке. Я бы хотел, чтобы элементы имели хороший отступ, чтобы я мог легко читать их в текстовом редакторе.

Поиск в Google показывает, что многие люди задают один и тот же вопрос, заданный примерно в 2001 году. В ответах обычно говорится «применить преобразование XSL» или «добавить собственные узлы-пробелы». Особенно последний заставляет меня идти% (так что я надеюсь, что в 2008 году будет более простой способ красиво оформить вывод MSXML. Итак, мой вопрос: есть ли там и как мне его использовать?

Добро пожаловать в ад Microsoft. Лабиринт потрясающей документации в 6 версиях и на нескольких языках, большинство из которых предназначены для другой версии, с которой вы работаете. Примеры использования Microsoft, включающие макросы, gotos и всевозможные преступления против человечности. Я иду по пути создания таблицы стилей и применяю функцию transformNodeToObject, используя таблицу стилей, к сожалению, возникает недокументированное исключение ....

Owl 30.05.2017 15:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
1
11 213
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Если в библиотеке нет опции формата, единственный другой способ - использовать XSLT или внешний симпатичный принтер (я думаю, что htmltidy также может делать xml). Кажется, что в библиотеке codeproject нет опции, но вы можете указать таблицу стилей XSLT для MSXML.

Какой облом. В библиотеке codeproject действительно нет опции, это просто оболочка для MSXML. Есть ли простой способ применить таблицу стилей (без записи XML на диск и его форматирования там, а затем чтения обратно)? Как бы выглядела таблица стилей?

Roel 03.10.2008 01:11
Ответ принят как подходящий

Попробуйте это, я нашел это много лет назад в Интернете.

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

Отлично, отлично поработали. Я внес некоторые изменения, чтобы сделать это в памяти, я опубликую их в отдельном посте, потому что это не поместится в комментарии.

Roel 03.10.2008 01:43

Я действительно хотел бы сам найти первоисточник. Это спасло мою задницу :)

Torlack 03.10.2008 01:44

Я нашел похожий на обсудить.com.cn/xml/86274.html, вот откуда я взял часть '.GetInterfacePtr ()'. Я не говорю по-китайски, поэтому большую часть страницы не понимаю :)

Roel 03.10.2008 01:46

Что такое поток? Я надеюсь, что это не поток XML, потому что это было бы ужасно.

Owl 30.05.2017 15:56

Вот измененная версия принятого ответа, которая преобразуется в памяти (изменения только в последних нескольких строках, но я публикую весь блок для удобства будущих читателей):

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

Сначала у меня были проблемы с тем, чтобы это работало, пока я не заметил, что использую __uuidof (MXXMLWriter40) вместо __uuidof (MXXMLWriter). (Код был «успешным», но не привел к XML с отступом.) Я немного удивлен, что это так сильно изменило ситуацию.

Miral 30.03.2011 13:10

Управление версиями MSXML тоже всегда сбивает меня с толку, я понятия не имею, как это должно работать.

Roel 30.03.2011 17:59

Для меня это не сработало с битом ".GetInterfacePtr ()". Я нашел ОЧЕНЬ похожий фрагмент кода без этого здесь: social.msdn.microsoft.com/forums/vstudio/en-US/…

Chris Dolan 16.05.2014 23:35

Это не работает для 60 (MXXMLWriter60, SAXXMLReader60, DOMDocument60). Есть идеи, почему? Когда я сохраняю, он просто создает пустой документ.

Adrian 27.02.2015 18:30

Это решение просто выводит пустой файл. Нашел решение здесь.

Adrian 18.03.2015 22:39

Когда я использую один и тот же объект IXMLDOMDocument для ввода и вывода, я получаю пустой IXMLDOMDocument. Я счел необходимым использовать промежуточный объект.

gigaplex 26.10.2015 10:49

Некоторое время назад я написал сценарий sed для базового отступа xml. Вы можете использовать его как внешний индентор, если ничего не помогает (сохраните его в xmlindent.sed и обработайте свой xml с помощью 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 с помощью sed (1)

Даже мои 2 цента прибыли 7 лет спустя, я думаю, что вопрос по-прежнему заслуживает простого ответа, заключенного всего в несколько строк кода, что возможно с помощью директивы Visual C++ #import и собственной библиотеки поддержки COM C++ (предлагающей интеллектуальные указатели и инкапсулирующую обработку ошибок. ).

Обратите внимание, что, как и принятый ответ, он не пытается вписаться в класс CXml, который использует OP, а скорее показывает основную идею. Также предполагаю 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

Другие вопросы по теме