Visual2000 · Архив статей А.Колесова & О.Павловой

Использование XML DOM в VB и MS Office/VBA

Андрей Колесов

© Андрей Колесов, 2000
Авторский вариант. Статья была опубликована c незначительной литературной правкой в журнале "КомпьютерПресс" N 12/2000, CD-ROM.


Одна из "горячих" ИТ-тем нынешнего года...

Одна из "горячих" ИТ-тем нынешнего года — проблема интеграции разнородных информационных ресурсов, решение которой требует создания простого и надежного механизма обмена данными между различными приложениями. И сегодня, кажется, все уже знают: XML — тот золотой ключик, который должен открыть дверь в новый мир, где порядок наконец заменит собой существующий ныне информационный хаос великого множества форматов данных.

Напомним, что XML и HTML базируется на одинаковых синтаксических принципах — информация записывается в виде простого текста, в котором имеются управляющие команды (тэги) и собственно данные. XML отличается от HTML тем, что позволяет передавать не только данные, но также и информацию об их структуре, то есть HTML ориентирован на описание неструктурированных данных, а XML — структурированных.

С точки зрения использования информации неструктурированные данные предназначены в первую очередь для визуального восприятия человеком, струкутрированные — для автоматической обработки (в том числе вычислений). Отметим, что оба этих языка представляют собой упрощенный вариант давно известного среди компьютерных лингвистов языка SGML (Standard Generation Markup Language).

Подчеркнем, что в принципе сама идея языка XML — текстового описания структуры и содержания некоторых данных — совсем не нова. Новизна заключается лишь в том, что лидеры компьютерной индустрии вроде бы осознали необходимость перехода от внутренних, закрытых, форматов к общим, открытым. Но, честно говоря, о том, насколько серьезны эти намерения (а не маркетинговые акции, предназначенные для показа своей готовности к открытости), можно будет судить лишь спустя некоторое время. XML — это мировой отраслевой стандарт, создание и развитие которого ведется под эгидой WWW Consortium, общественной организации, представляющей интересы входящих в нее участников рынка.

Что касается прикладных программистов, то они уже очень давно оценили преимущества простых текстовых форматов данных по сравнению с двоичными при информационном взаимодействии компонентов вычислительных систем. Могу сослаться на пример из собственной программистской практики десятилетней давности. При разработке информационно-аналитических систем для обработки геологических данных мы полностью перешли на текстовые форматы данных и описаний их структуры, резко сократив время на отладку программного комплекса и его конфигурирование под задачи пользователей. Сначала мы рассматривали такое решение как временное и хотели для повышения производительности перейти на двоичные форматы. Но жизнь показала, что это пустая трата времени, так как производительность компьютеров росла быстрее, чем объем базы данных.

Здесь следует сделать одно важное замечание. Текстовое представление данных имеет свои недостатки. Один из них — сложности с использованием символов, задействованных в качестве специальных (например, "<" и ">"). Вторая проблема заключается в неоднозначности преобразования данных из внутреннего двоичного формата в текст, и наоборот. В последнем случае особое внимание нужно уделять национальной специфике форматов, особенно при работе с разными региональными установками и кодовыми таблицами.

Понятно, что появление общего стандарта описания данных избавляет разработчиков от необходимости придумывать собственные форматы. Однако за любую универсальность приходится расплачиваться определенной избыточностью, хотя в данном случае она будет не так велика и не будет сколь-нибудь заметна на современных компьютерах. Например, мне бы пришлось заменить свои старые описания параметров:

 LastName = Колесов

на:

 <LastName>Колесов</LastName>

Прежде чем перейти к конкретным техническим вопросам, стоит сделать еще одно важное замечание. Сам по себе XML не решает проблемы преобразования XML-документов, что необходимо для передачи данных между приложениями. Проще говоря, XML лишь связывает, например, некоторый набор чисел с понятием (тэгом) "цена", но что именно означает данный термин, язык уже не может расшифровать.

Таким образом, для правильной интерпретации содержимого XML-документов необходимо знать так называемую XML-схему, которая бы описывала смысл полей данных.

В начало статьи

Введение в XML DOM

Visual Basic 6.0 и приложения MS Office 2000 не включают в себя поддержку XML на уровне пользователя и разработчика, хотя при этом многие приложения Office используют передачу данных с помощью XML для выполнения внутренних операций. Вместе с тем уже сегодня, не дожидаясь новых версий этих систем (где что-то, связанное с XML, должно вскоре появиться), программисты могут применять XML-формат для обмена информацией с помощью написания собственных достаточно простых программных конструкций. Видятся два пути реализации этой задачи:

  1. Использование специального объекта XML, называемого XMLDOM, или DOMDocument (DOM, Document Object Model). Работа с этим объектом выполняется с помощью библиотеки Microsoft XML 2.0 (MSXML.DLL), на которую нужно сделать ссылку в окне Reference. (Обратите внимание: на моем компьютере в списке ссылок эта библиотека в начальный момент имеет индекс 1.0, а уже после ее подключения меняет его на 2.0.)

    С точки зрения программиста в применении DOMDocument можно выделить три момента:

  2. На практике наиболее распространенной задачей является экспорт-импорт с помощью XML-файла, что можно делать с помощью обычных средств VB/VBA. Вывод данных в формате XML является вообще достаточно тривиальной задаче. С вводом дело обстоит посложнее, так как в этом случае нужно "вручную" писать код для анализа синтаксиса и для разборки элементов документа. Как бы то ни было, в любом случае программист должен иметь в виду "ручной" метод экспорта-импорта, который иногда может быть очень полезен.

В начало статьи

Передача набора записей из VB-приложения в Excel

В наших "Советах для тех, кто программирует на VB" мы приводили два примера импорта данных из таблицы (набора записей, Recordset) в виде просто текстового файла (совет 297) и HTM-файла (совет 329). Попробуем выполнить аналогичную задачу с помощью XML-файла, который потом прочитаем в Excel.

В начало статьи

Импорт данных

Для тестирования мы сделаем базу данных с таблицей такого содержания:

FirstName  LastName  BirthDate  Height
======================================
Sergey     Sokolov   03.11.52   1,82
Андрей     Petrov    17.08.58   1,77
Света      Суслова   23.09.67   1,65
======================================

Далее напишем код, который обращается для импорта данных к процедуре ExportXML (листинг 1):

Dim strConnectString$, strSQL$, strHeading$
  Dim cnn As ADODB.Connection
  Dim rs As Recordset
  strConnectString = "Provider=Microsoft.Jet.OLEDB.3.51;" & _
    "Persist Security Info=False;" & _
     "Data Source=C:\vb-db\xmltest.mdb"
  strSQL = "Select FirstName +' ' + LastName as Name, " & _
      "BirthDate, Height as Рост" & _
      " from Employees Order by LastName, FirstName"
  strHeading = "Список сотрудников"Set cnn = New ADODB.Connection
  Set rs = New ADODB.Recordset
    cnn.Open strConnectString$   ' устанавливаем связь
    Set rs = cnn.Execute(strSQL) ' создаем Recordset
  Call ExportXML(rs, strHeading$, "d:\file1.xml")

В результате его выполнения получим XML-файла, содержимое которого лучше всего посмотреть в Internet Explorer (рис.1).

Рис. 1.

Для понимания выполненных нами действий нужно отметить следующие моменты:

Создание нового объекта DOMDocument начинается со строки кода:

Dim xmlDoc As DOMDocument
Set xmlDoc = New DOMDocument
StartString$ = "<?xml version='1.0'?>" ' начальная строка 
MainNode$ = "<Главный_узел_объекта/>"   главный узел объекта
xmlDoc.loadXML StartString$ + MainNode$

Для выбора кодировки передаваемых данных нужно сформировать начальную строку следующего вида:

StartString$ = "<?xml version='1.0' encoding='Windows-1251'?>"

Однако мне этого не удалось сделать — выдавалось сообщение о невозможности создания объекта.

В начало статьи

Ввод данных

Д Для ввода сформированного XML-файла в Excel используем функцию ImportXML (листинг 2).

Эта функция создает объект DOMDocument, который можно затем дополнительно обрабатывать, и записывает введенные значения в рабочую таблицу. Ввод данных можно сделать с помощью такой макрокоманды:

Sub MyMacro()
  Set mXML = ImportXML("D:\file1.xml")
End Sub

В результате ее выполнения мы получим заполненную таблицу рабочей книги (рис. 2).

Рис. 2.

Теперь внимательно рассмотрим, как производится ввод данных из созданного нами XML-файла. Обращение к функции ImportXML (Excel) в общем случае выглядит следующим образом:

Set mXML = ImportXML(xmlFile$, ObjectPath$, PropertyPath$)

Если мы используем установку ObjectPath = "*" (по умолчанию), то работа ведется во всеми объектами документа. В нашем случае это будут узлы с тэгами <OneRowI> (они могут иметь произвольные имена, в том числе одинаковые, например <OneRow>), а включенные в них тэги — свойствами. При формировании таблицы в Excel для определения имен колонок мы анализировали содержимое только первого узла, считая все узлы однородными.

Однако если мы укажем ObjectPath = "<//OneRow2>", то выборка будет сделана только для данного узла. Точно так же можно управлять выборкой отдельных полей, например указав PropertyPath = "BirthDate".

В начало статьи

Вопросы перекодировок данных

Анализируя выполненные нами операции по передаче данных, в первую очередь следует обратить внимание на то, что все данные (содержимое полей набора записей) передаются в виде текста. Преобразование информации в нашем случае выполнялось средствами VB, и поэтому был автоматически выбран формат в соответствии с текущими региональными установками. Принимающее приложение (Excel) также получило обычные текстовые данные, которые в принципе могут быть интерпретированы самым произвольным образом. Имейте в виду, что ячейки таблицы (рис. 2) заполнены обычным текстом в неопределенном формате (внешний признак этого — выравнивание по левому краю).

Подобная ситуация чревата ошибками при передаче XML-данных между компьютерами, имеющими разные региональные установки (для Америки значение "23.09.53" является недопустимой датой, а "1,23" — недопустимым числом). Для решения этой проблемы есть несколько путей. Во-первых, можно применять пользовательские атрибуты, которые будет понимать как передатчик, так и приемник информации. Во-вторых, можно присвоить каждому свойству узла "жесткий" тип данных, и тогда будет использоваться соответствующий фиксированный формат.

Для реализации первого способа немного модифицируем код функции RecordsetToXMLDOM:

For Each fldField In rs.Fields   ' запись полей записи
  ' создание нового элемента
  Set xmlField = xmlFields.appendChild( _
     xmlDoc.createElement(Replace(fldField.Name, " ", "_")))
  ' установка атрибута "MyType"
  Set attr = xmlDoc.createAttribute("MyType")
  attr.Value = fldField.Type
  xmlField.Attributes.setNamedItem attr
  xmlField.Text = fldField.Value   ' запись содержимого
Next

В результате этого в сформированном коде в тэги с наименованием полей добавятся атрибуты с кодами типов данных (рис. 3).

Рис. 3.

Соответственно при вводе данных их значения можно прочитать и сделать соответствующую установку форматов ячеек:

DateType = propertyNode.Attributes(0).nodeValue

Для второго варианта нужно использовать фиксированный, закрепленный за типом данных формат. Например, для переменных типа "дата" установку такого атрибута можно выполнить так:

Set attr = xmlDoc.createAttribute("dt:dt")
attr.Value = "date"
xmlField.Attributes.setNamedItem attr

или воспользоваться свойством dataType:

xmlField.dataType = "date"

В этом случае записываемые данные должны иметь обязательный формат даты "ГГГГ-ММ-ДД":

xmlField.Text = "2000-12-02"

Нужно также отметить, что тип поля необязательно задавать в каждом тэге. Можно, например, один раз сделать такое описание всех полей при формировании узла или всего документа (в том числе со ссылкой на XML-схему).

В начало статьи

Вывод данных о свойствах Word-документа

Теперь попробуем решить такую задачу: будем формировать протокол работы с документами Word 2000, записывая информацию о встроенных свойствах при завершении работы с документом. Для начала напишем функцию DocPropertiesToXML, формирующую DOMDocument-объект для одного документа Word (листинг 3).

Поместим эту функцию в модуль в составе глобального шаблона Normal.dot (нужно также для шаблона указать ссылку на MSXML.DLL). Далее создадим макрокоманду, которая будет формировать XML-файл для одного документа:

Public Sub OneDocPropertyToXML()   Dim xmlDoc As DOMDocument
   Set xmlDoc = DocPropertiesToXML(ThisDocument)
    'можно создавать файл с оригинальным именем
   xmlDoc.Save "d:\Myfile.xml"
End Sub

В результате мы получим файл со свойствами одного документа (фрагмент файла изображен на рис. 4):

Рис. 4.

Содержимое полученного файла можно прочитать созданной ранее процедурой ImportXML в Excel, но для этого обращение к ней должно выглядеть следующим образом (главный узел следует указать в качестве объекта):

Set mXML = ImportXML("D:\myfile.xml", "//DocProperties")

Напишем процедуру DocPropertyToLogXML, которая будет создавать файл протокола (листинг 4).

Обратите внимание на операции включения созданного для конкретного документа XML-объекта в объекта Log-файла. Теперь создадим макрокоманду, которая будет добавлять информацию об активном документе в протокол:

Call DocPropertyToLogXML(ThisDocument)

В результате вы будет автоматически формировать XML-файл, структура которого приведена на рис. 5:

Рис. 5.

Его также можно прочитать в Excel:

Set mXML = ImportXML("D:\logfile.xml")

Примечание. Обращение на запись информации в Log-файл можно поместить в процедуру ThisDocument_Close каждого документа. Механизм программного формирования такого кода в каждом открываемом файле описан в статье Владимира Биллига "Документы Office 2000 и их проекты".

В начало статьи

Создание архива входящей почты

Приведем еще один пример использования XML-файла — на этот раз для хранения архива поступающей электронной почты. Для этого нужно сначала сформировать две событийные процедуры:

Dim mailBagFileName As String
Dim AttachmentsDirectory As StringPrivate Sub Application_Startup()
  ' Инициализация при запуске прилежения
    ' файл с архивом входящей почты
    mailBagFileName = "d:/xmlPro/mailBag.xml"
    ' каталог для хранения присоединенных файлов
    AttachmentsDirectory = "d:/xmlPro/"
End SubPrivate

Sub Application_NewMail()
  ' При поступлении нового письма производится запись его в архив
    Dim mailItems As Items
    Dim mailmsg As MailItem
    ' Папка с входящими письмами
    Set mailItems = Application.Session.GetDefaultFolder( _
       olFolderInbox).Items
    Set mailmsg = mailItems.GetLast ' выбираем последнее
    ' Запись поступившего письма в XML-объект
    Set xmlMail = MessageToXML(mailmsg, AttachmentsDirectory)
  ' запись в архив
    Call AddMessageToArchive(xmlMail, mailBagFileName)
End Sub

Ключевым процедурами в этой задаче являются процедуры MessageToXML и AddMessageToArchive (листинг 5).

Общая логика формирования архива очень похожа на то, что мы делали, создавая протокол работы с Word-файлами: сначала преобразуем содержимое письма в DOMDocunemt, а потом подключим его к единому файлу. Новшеством здесь является то, что документ письма имеет более сложную структуру (появились вложенные узлы для описания подключенных файлов), и для хранения тела письма мы используем секцию CDATA (содержимое письма может иметь символы, нарушающие синтаксис XML). Результат преобразования в XML показан на рис. 6.

Рис. 6.

Чтобы закончить с этой задачей, остается только написать ASP-cтраницу (листинг 6) которая преобразует содержимое XML-архива писем в HTML-формат (рис. 7).

Рис. 7.

В начало статьи