Для русскоязычных пользователей Visio. Начинающих и профессионалов. Где взять, как сделать, что купить и т.д.

Выборка данных из файла Visio по XML технологии

Геннадий Туманов

Так как Visio, начиная с 2002 версии позволяет сохранять документ в формате XML (.vdx) интересно было попробовать использовать его в качестве XML источника данных. Попробовал, вот что получилось.

Исходный файл d1.vdx содержит всего три прямоугольника с Custom Properties p1, p2 и p3. Ставилась задача – выбрать эти Custom Properties. Вспомогательный инструмент – MSXML. Программа написана в VB6.

Проверены два способа: работа с DOM и XSLT-преобразование.

Работа с DOM

Работа с DOM никаких неожиданностей не принесла. Вот такой текст

Sub GetCustProp()

'Выборка данных из Visio файла с помощью MSXML

Dim xmlVis As MSXML2.DOMDocument 'Исходный документ

Dim NL As MSXML2.IXMLDOMNodeList

Dim NL1 As MSXML2.IXMLDOMNodeList

Dim NL2 As MSXML2.IXMLDOMNodeList

Set xmlVis = New MSXML2.DOMDocument

xmlVis.validateOnParse = False

xmlVis.async = False

s = fPath & "d1.vdx"

xmlVis.Load (s)

Set NL = xmlVis.getElementsByTagName("*/Shape")

For i = 0 To NL.length - 1

Set NL1 = NL.Item(i).selectNodes("@ID")

Debug.Print "ID шейпа = " & NL1.Item(0).Text

Set NL1 = NL.Item(i).selectNodes("Prop")

s = ""

For j = 0 To NL1.length - 1

Set NL2 = NL1.Item(j).selectNodes("Label")

s = s & NL2.Item(0).Text

Set NL2 = NL1.Item(j).selectNodes("Value")

s = s & " = " & NL2.Item(0).Text & "; "

Next

Debug.Print s

Next

Set xmlVis = Nothing

End Sub

дает вот такой вывод

ID шейпа = 1

p1 = Первый; p2 = wetwer; p3 = fgdfgsdfgsdf;

ID шейпа = 2

p1 = Второй; p2 = wert wer wer ; p3 = gh fg hfgh fg h;

ID шейпа = 3

p1 = Третий; p2 = kkkkkk; p3 = uyyyyyyyyi iiii;

Все, что требуется, – ориентироваться в XML модели Visio.

Работа с XSLT-преобразованием

Этот вариант дался немного потяжелее. Текст программы

Sub CopyCustPropToFile()

'Конвертирование Visio файла с помощью MSXML. XLT-преобразование

Dim xmlSource As MSXML2.DOMDocument

Dim xmlTr As MSXML2.DOMDocument

Set xmlSource = New MSXML2.DOMDocument

xmlSource.validateOnParse = False

xmlSource.async = False

s = fPath & "d1.vdx"

'Из входного файла приходится вырезать xmlns

s1 = fPath & "d1.vdx"

s2 = fPath & "temp.vdx"

Open s1 For Input As #1

Open s2 For Output As #2

Input #1, s3

Print #2, s3

Input #1, s3

s3 = Replace(s3, " xmlns='http://schemas.microsoft.com/visio/2003/core'", "")

Print #2, s3

Do While Not EOF(1)

Input #1, s3

Print #2, s3

Loop

Close #1

Close #2

'На вход загружается почищенная копия

s = fPath & "temp.vdx"

xmlSource.Load (s)

'Загрузка трансформатора

Set xmlTr = New MSXML2.DOMDocument

xmlTr.validateOnParse = False

s = fPath & "Автоконвертор.xslt"

xmlTr.Load (s)

'Вот собственно преобразование

s = xmlSource.transformNode(xmlTr)

'Трансформатор по умолчанию создает выходной файл в кодировке "UTF-16"

'Это можно исправить непосредственно в трансформаторе или (грубо) вручную функцией Replace

s = Replace(s, "UTF-16", "Windows-1251")

s1 = fPath & "d2.htm"

Open s1 For Output As #1

Print #1, s

Close #1

Set xmlSource = Nothing

Set xmlTr = Nothing

End Sub

Текст конвертора (файл Автоконвертор.xslt)

<?xml version="1.0" encoding="ISO-8859-5"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<xsl:template match="/">

<html>

<head />

<body>

<xsl:for-each select="VisioDocument">

<xsl:for-each select="DocumentSheet">

<xsl:for-each select="@NameU">

<xsl:value-of select="." />

</xsl:for-each>

</xsl:for-each>

<br />

<xsl:for-each select="Pages/Page">

<xsl:for-each select="@NameU">Страница = <xsl:value-of select="." />

</xsl:for-each>

<br />

<table border="1">

<xsl:for-each select="Shapes">

<xsl:for-each select="Shape">

<tr>

<xsl:for-each select="Prop">

<td>

<xsl:for-each select="Label">

<xsl:value-of select="." />

</xsl:for-each> =

<xsl:for-each select="Value">

<xsl:value-of select="." />

</xsl:for-each>

</td>

</xsl:for-each>

</tr>

</xsl:for-each>

</xsl:for-each>

</table>

</xsl:for-each>

</xsl:for-each>

</body>

</html>

</xsl:template>

</xsl:stylesheet>

На выходе получается html файл d2.htm примерно такого вида

TheDoc

Страница = Page-1

p1 = Первый p2 = wetwer p3 = fgdfgsdfgsdf

p1 = Второй p2 = wert wer wer p3 = gh fg hfgh fg h

p1 = Третий p2 = kkkkkk p3 = uyyyyyyyyi iiii

Все неприятности возникли почему-то из-за ссылки на пространство имен. Visio вставляет в файл атрибут xmlns='http://schemas.microsoft.com/visio/2003/core'. Но MSXML с такой ссылкой работать отказывается (еще надо бы выяснить, почему). Удаляться через RemoveNamedItem такой атрибут отказывается (другие атрибуты удаляются).

Причина скорее всего в том, что привязка к пространству имен в MSXML очень глубокая. Однажды захватив эту ссылку, он уже распространяет ее на весь документ. При попытке удаления атрибута со ссылкой атрибут на самом деле удаляется, но для сохранения привязки MSXML тут же вставляет аналогичный атрибут в другом месте.

Аналогичная история происходит при попытке создать новый документ и поэлементно перенести в него компоненты из первого. Ссылка xmlns размножается и присваивается каждому переносимому компоненту.

Радикальный способ – удалить ссылку непосредственно во входном файле. Это работает. Если весь входной файл переписать во временный, вычистив из второй строки ссылку xmlns, и подать полученную копию на вход конвертора, у MSXML претензий не возникает. Так и сделано в приведенном примере.


Контакты: Туманов Геннадий Евгеньевич   gCroc@yandex.ru