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





Если в библиотеке нет опции формата, единственный другой способ - использовать XSLT или внешний симпатичный принтер (я думаю, что htmltidy также может делать xml). Кажется, что в библиотеке codeproject нет опции, но вы можете указать таблицу стилей XSLT для MSXML.
Какой облом. В библиотеке codeproject действительно нет опции, это просто оболочка для MSXML. Есть ли простой способ применить таблицу стилей (без записи XML на диск и его форматирования там, а затем чтения обратно)? Как бы выглядела таблица стилей?
Попробуйте это, я нашел это много лет назад в Интернете.
#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)));
}
Отлично, отлично поработали. Я внес некоторые изменения, чтобы сделать это в памяти, я опубликую их в отдельном посте, потому что это не поместится в комментарии.
Я действительно хотел бы сам найти первоисточник. Это спасло мою задницу :)
Я нашел похожий на обсудить.com.cn/xml/86274.html, вот откуда я взял часть '.GetInterfacePtr ()'. Я не говорю по-китайски, поэтому большую часть страницы не понимаю :)
Что такое поток? Я надеюсь, что это не поток XML, потому что это было бы ужасно.
Вот измененная версия принятого ответа, которая преобразуется в памяти (изменения только в последних нескольких строках, но я публикую весь блок для удобства будущих читателей):
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 с отступом.) Я немного удивлен, что это так сильно изменило ситуацию.
Управление версиями MSXML тоже всегда сбивает меня с толку, я понятия не имею, как это должно работать.
Для меня это не сработало с битом ".GetInterfacePtr ()". Я нашел ОЧЕНЬ похожий фрагмент кода без этого здесь: social.msdn.microsoft.com/forums/vstudio/en-US/…
Это не работает для 60 (MXXMLWriter60, SAXXMLReader60, DOMDocument60). Есть идеи, почему? Когда я сохраняю, он просто создает пустой документ.
Это решение просто выводит пустой файл. Нашел решение здесь.
Когда я использую один и тот же объект IXMLDOMDocument для ввода и вывода, я получаю пустой IXMLDOMDocument. Я счел необходимым использовать промежуточный объект.
Некоторое время назад я написал сценарий 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
Добро пожаловать в ад Microsoft. Лабиринт потрясающей документации в 6 версиях и на нескольких языках, большинство из которых предназначены для другой версии, с которой вы работаете. Примеры использования Microsoft, включающие макросы, gotos и всевозможные преступления против человечности. Я иду по пути создания таблицы стилей и применяю функцию transformNodeToObject, используя таблицу стилей, к сожалению, возникает недокументированное исключение ....