Сохранение XML в UTF-8 с MSXML - PullRequest
       38

Сохранение XML в UTF-8 с MSXML

2 голосов
/ 08 апреля 2010

Я пытаюсь загрузить простой XML-файл (закодированный в UTF-8):

<?xml version="1.0" encoding="UTF-8"?>
<Test/>

И сохраните его с MSXML в VBScript:

Set xmlDoc = CreateObject("MSXML2.DOMDocument.6.0")

xmlDoc.Load("C:\test.xml")

xmlDoc.Save "C:\test.xml" 

Проблема в том, что MSXML сохраняет файл в формате ANSI вместо UTF-8 (несмотря на то, что исходный файл был закодирован в UTF-8).

Документы MSDN для MSXML говорят, что save () запишет файл в любой кодировке, определенной в XML:

Кодировка символов основана на атрибуте кодировки в объявлении XML, например. Если атрибут кодирования не указан, по умолчанию используется UTF-8.

Но это явно не работает, по крайней мере, на моей машине.

Как сохранить MSXML в UTF-8?

Ответы [ 3 ]

3 голосов
/ 11 мая 2010

Вы используете два других класса в MSXML для записи XML, правильно закодированного в выходной поток.

Вот мой вспомогательный метод, который пишет в общий IStream:

class procedure TXMLHelper.WriteDocumentToStream(const Document60: IXMLDOMDocument2; const stream: IStream; Encoding: string = 'UTF-8');
var
    writer: IMXWriter;
    reader: IVBSAXXMLReader;
begin
{
    From http://support.microsoft.com/kb/275883
    INFO: XML Encoding and DOM Interface Methods

    MSXML has native support for the following encodings:
        UTF-8
        UTF-16
        UCS-2
        UCS-4
        ISO-10646-UCS-2
        UNICODE-1-1-UTF-8
        UNICODE-2-0-UTF-16
        UNICODE-2-0-UTF-8

    It also recognizes (internally using the WideCharToMultibyte API function for mappings) the following encodings:
        US-ASCII
        ISO-8859-1
        ISO-8859-2
        ISO-8859-3
        ISO-8859-4
        ISO-8859-5
        ISO-8859-6
        ISO-8859-7
        ISO-8859-8
        ISO-8859-9
        WINDOWS-1250
        WINDOWS-1251
        WINDOWS-1252
        WINDOWS-1253
        WINDOWS-1254
        WINDOWS-1255
        WINDOWS-1256
        WINDOWS-1257
        WINDOWS-1258
}

    if Document60 = nil then
        raise Exception.Create('TXMLHelper.WriteDocument: Document60 cannot be nil');
    if stream = nil then
        raise Exception.Create('TXMLHelper.WriteDocument: stream cannot be nil');

    // Set properties on the XML writer - including BOM, XML declaration and encoding
    writer := CoMXXMLWriter60.Create;
    writer.byteOrderMark := True; //Determines whether to write the Byte Order Mark (BOM). The byteOrderMark property has no effect for BSTR or DOM output. (Default True)
    writer.omitXMLDeclaration := False; //Forces the IMXWriter to skip the XML declaration. Useful for creating document fragments. (Default False)
    writer.encoding := Encoding; //Sets and gets encoding for the output. (Default "UTF-16")
    writer.indent := True; //Sets whether to indent output. (Default False)
    writer.standalone := True;

    // Set the XML writer to the SAX content handler.
    reader := CoSAXXMLReader60.Create;
    reader.contentHandler := writer as IVBSAXContentHandler;
    reader.dtdHandler := writer as IVBSAXDTDHandler;
    reader.errorHandler := writer as IVBSAXErrorHandler;
    reader.putProperty('http://xml.org/sax/properties/lexical-handler', writer);
    reader.putProperty('http://xml.org/sax/properties/declaration-handler', writer);


    writer.output := stream; //The resulting document will be written into the provided IStream

    // Now pass the DOM through the SAX handler, and it will call the writer
    reader.parse(Document60);

    writer.flush;
end;

Для сохранения в файл я вызываю Stream версию с FileStream :

class procedure TXMLHelper.WriteDocumentToFile(const Document60: IXMLDOMDocument2; const filename: string; Encoding: string='UTF-8');
var
    fs: TFileStream;
begin
    fs := TFileStream.Create(filename, fmCreate or fmShareDenyWrite);
    try
        TXMLHelper.WriteDocumentToStream(Document60, fs, Encoding);
    finally
        fs.Free;
    end;
end;

Вы можете конвертировать функции на любой язык, который вам нравится. Это Дельфы.

3 голосов
/ 10 апреля 2010

В вашем XML-файле нет текста, не являющегося ANSI, поэтому он будет идентичен в кодировке UTF-8 или ASCII. В моих тестах после добавления не ASCII-текста в test.xml MSXML всегда сохраняет в кодировке UTF-8, а также записывает спецификацию, если таковая была для начала.

http://en.wikipedia.org/wiki/UTF-8
http://en.wikipedia.org/wiki/Byte_order_mark

1 голос
/ 10 октября 2012

При выполнении load msxml не копирует кодировку из инструкции по обработке в созданный документ. Поэтому он не содержит никакой кодировки и кажется, что msxml выбирает то, что ему нравится. В моем окружении это UTF-16, который я не предпочитаю.

Решение - предоставить инструкции по обработке и указать там кодировку. Если вы знаете, что в документе нет инструкций по обработке, код тривиален:

Set pi = xmlDoc.createProcessingInstruction("xml", _
         "version=""1.0"" encoding=""windows-1250""")
If xmlDoc.childNodes.Length > 0 Then
  Call xmlDoc.insertBefore(pi, xmlDoc.childNodes.Item(0))
End If

Если возможно, что документ содержал другую инструкцию обработки, он должен быть сначала удален (поэтому приведенный ниже код должен предшествовать приведенному выше). Я не знаю, как использовать selectNode для этого, поэтому я просто перебрал все корневые узлы:

For ich=xmlDoc.childNodes.Length-1 to 0 step -1
  Set ch = xmlDoc.childNodes.Item(ich)
  If ch.NodeTypeString = "processinginstruction" and ch.NodeName = "xml" Then
    xmlDoc.removeChild(ch)
  End If
Next ich

Извините, если код не выполняется напрямую, потому что я изменил рабочую версию, написанную на что-то нестандартное, а не vbscript.

...