В предыдущей статье был приведен пример XML-документа, полученного из рабочей книги Excel. Прямой импорт такого документа в курсоры или таблицы VFP невозможен из-за несоответствия структур. С другой стороны, в VFP можно создать объект парсера XML DOM и работать с ним. Приступим?
Создать объект парсера очень просто, например:
oXMLDom=CREATEOBJECT("MSXML2.DOMDocument.4.0")
Теперь нужно загрузить в него XML документ. Если вы желаете загрузить документ из файла, то используйте метод load, а если ваш документ помещен в переменную памяти, используйте метод loadXML
Для начала попробуем работать с Generic XML документом. В качестве примера можно использовать файл team_type1.xml, показанный во второй статье цикла, который вы можете взять здесь. Сохраните этот документ на диск в каталог "c:\vfptest" или в какой-нибудь другой каталог (в этом случае исправьте имя каталога в примерах на требуемое). Или просто скопируйте его содержание с этой страницы в Notepad и сохраните его под указанным именем. Его содержание приведено ниже.
<?xml version = "1.0" encoding="Windows-1252" standalone="yes"?>
<VFPData>
<team>
<id>TM_1A60NG</id>
<last_name>Ivanov</last_name>
<first_name>Ivan</first_name>
<birthdate>1980-10-01</birthdate>
<occupation>Manager</occupation>
<oc_history>02.10.2003- Manager at Marketing division
02.12.2001- 01.10.2003 Chif-engineer at Marketing division
02.02.2001-01.12.2001 Engineer at Marketing division
01.10.2000-01.02.2001 Probationer at Marketing division
</oc_history>
<salary>8000.0000</salary>
<perquisite>1.25</perquisite>
</team>
<team>
<id>TM_1A60NL</id>
<last_name>Petrov</last_name>
<first_name>Petr</first_name>
<birthdate>1976-01-12</birthdate>
<occupation>Chief-director</occupation>
<oc_history>01.10.2000- Chief_Director
</oc_history>
<salary>12000.0000</salary>
<perquisite>1.40</perquisite>
</team>
</VFPData>
Загрузим этот документ из файла:
oXMLDom.load("c:\vfptests\team_type1.xml") && или
*? oXMLDom.load("c:\vfptests\team_type1.xml")
Вторая строка вернет вам .T., если загрузка документа прошла успешно. Итак, документ загружен. Положим я хочу получить значения элементов ID из этого документа. Нет ничего проще. В XML DOM есть два замечательных метода - getElementsByTagName и selectNodes
Оба метода возвращают коллекции узлов, имя которых передано им в качестве параметра. В отличие от родных объектов класса коллекции VFP, индексы, получаемых коллекций построены на базе 0, то есть на первый элемент нужно ссылаться путем вызова дефолтного метода item(0).
Разница в вызове этих методов заключена в следующем:
getElementsByTagName принимает в качестве параметра имя элемента;
selectNodes принимает в качестве параметра выражение XPath (которому посвящена отдельная статья цикла).
Осталось исполнить задуманное...
oIDs=oXMLDOM.getElementsByTagName('id')
? oIDs.length
oIDs=oXMLDOM.selectNodes('VFPData//id')
? oIDs.length
* следующая строка также абсолютно правильна, с точки зрения XPath
* oIDs=oXMLDOM.selectNodes('.//id')
* ? oIDs.length
? oIDs.item(0).text
? oIDs.item(1).text
Класс! Если взглянуть на файл, полученный из Excel, то мы увидим, что требуемые нам данные располагаются между тэгами <Row> </Row> Где наши чепчики? ... Пусть там и лежат!
oXMLDom.load("c:\vfptests\oil_consumption_excel.xml")
oRows=oXMLDOM.getElementsByTagName('Row')
? oRows.length
Что имеем? Zero! Условимся пока не делать никаких изменений в исходном файле. Может мы делаем что-то не так? Хорошо, давайте получим объектную ссылку на корневой элемент:
oRoot=oXMLDom.documentElement
? oRoot.nodeName
Получаем Workbook. Теперь снова можете попробовать выбрать требуемые узлы с помощью методов выборки, как мы это делали раньше в примерах. Увы! Результат по-прежнему ноль.
Как быть? Попробуем пройтись по узлам с помощью цикла FOR EACH ... IN / ENDFOR. Поскольку работа цикла не поддерживается в командном окне, создадим программный файл excel_xml_parsing.prg.
LPARAMETERS pcXML
Local ;
oDOMXLS As MSXML2.DOMDocument.4.0, ;
oResDOM As As MSXML2.DOMDocument.4.0
Local oRoot, oWSheet, oTable, oRow
oDOMXLS=Createobject("MSXML2.DOMDocument.4.0")
oResDOM=Createobject("MSXML2.DOMDocument.4.0")
oDOMXLS.load(pcXML)
Do While oDOMXLS.readyState !=4
DoEvents
Enddo
Приведенный выше фрагмент кода создает два объекта XML DOM и загружает в первый из них наш исследуемый файл, переданный в качестве параметра. Далее, добавим строку кода, в которой получим объектную ссылку на корневой элемент и строку подтверждающую, что дочерние узлы имеются.
oRoot=oDOMXLS.documentElement
? oRoot.childNodes.length
Вновь взглянем на исследуемый файл. Таблица с данными находится в узле Worksheet..., причем узлов с такими именами три (по числу листов в книге), но нас интересует в данном примере только первый, поэтому, пока не обременяя себя универсальностью кода, ищем первое появление узла с таким именем в дочерних узлах корневого элемента:
? oRoot.childNodes.length
For Each oNode In oRoot.childNodes
If Lower(oNode.nodeName)="worksheet"
oWSheet=oNode
Exit
Endif
Endfor
? oWsheet.childNodes.length
? oWsheet.childNodes.item(0).nodeName
? oWsheet.childNodes.item(1).nodeName
Продолжим добавление кода:
For Each oNode In oWsheet.childNodes
If Lower(oNode.nodeName)="table"
oTable=oNode
Exit
Endif
Endfor
? oTable.childNodes.length
Отлично - объект oTable представляет собой объектую ссылку на коллекцию из 12 дочерних узлов узла <Table>
Осталось пройтись по этим узлам и найти все дочерние узла с именем "Row". Но куда положить результат? Давайте пока разместим объектные ссылки на требуемые узлы в коллекции colRows, которую создадим на базе родного класса коллекции Visual FoxPro.
LOCAL colRows as Collection
For Each oNode In oTable.childNodes
If Lower(oNode.nodeName)="row"
colRows.Add(oNode)
Endif
Endfor
В результате исполнения приведенного выше фрагмента кода, получаем коллекцию объектных ссылок на узлы с именем "Row". Осталось перебрать объекты-члены коллекции и посмотреть на их содержимое.
Local lnColCounter, oCurNode
For lnColCounter=1 To colRows.Count
oCurNode=colRows.Item(lnColCounter)
For Each oNode In oCurNode.childNodes
? oNode.firstChild.Text
Endfor
Endfor
Return
Где наши чепчики? ... Ну уже можно подбросить, ну скажем на полметра, поскольку мы все-таки вытащили данные, но они все имеют символьный тип! К счастью, нет ничего проще! Достаточно в приведенный выше цикл перебора добавить выборку значения аттрибута "Type" тэга содержащего данные.
For lnColCounter=1 To colRows.Count
oCurNode=colRows.Item(lnColCounter)
For Each oNode In oCurNode.childNodes
? oNode.firstChild.Text
? oNode.firstChild.Attributes.item(0).text
Endfor
Endfor
Собранный до кучи код примера содержит следующее:
LPARAMETERS pcXML
Local ;
oDOMXLS As MSXML2.DOMDocument.4.0, ;
oResDOM As As MSXML2.DOMDocument.4.0
Local oRoot, oWSheet, oTable, oRow
oDOMXLS=Createobject("MSXML2.DOMDocument.4.0")
oResDOM=Createobject("MSXML2.DOMDocument.4.0")
oDOMXLS.Load(pcXML)
Do While oDOMXLS.readyState !=4
DoEvents
Enddo
oRoot=oDOMXLS.documentElement
? oRoot.childNodes.Length
For Each oNode In oRoot.childNodes
If Lower(oNode.nodeName)="worksheet"
oWSheet=oNode
Exit
Endif
Endfor
? oWSheet.childNodes.Length
? oWSheet.childNodes.Item(0).nodeName
? oWSheet.childNodes.Item(1).nodeName
For Each oNode In oWSheet.childNodes
If Lower(oNode.nodeName)="table"
oTable=oNode
Exit
Endif
Endfor
? oTable.childNodes.Length
Local colRows As Collection, colRowsData As Collection
colRows=Createobject("Collection")
For Each oNode In oTable.childNodes
If Lower(oNode.nodeName)="row"
colRows.Add(oNode)
Endif
Endfor
Local lnColCounter, oCurNode
For lnColCounter=1 To colRows.Count
oCurNode=colRows.Item(lnColCounter)
For Each oNode In oCurNode.childNodes
? oNode.firstChild.Text
? oNode.firstChild.Attributes.item(0).text
Endfor
Endfor
Return
А продолжим мы в следующей статье, так, чтобы время загрузки файла было приемлемым. Как там насчет конверторов стоимостью кучу убитых енотов? Скажем, пока, словами классиков - "Шура, заплатите за кефир". Шутка, но думаю, что даже приведенного здесь достаточно, чтобы вы уже смогли продолжить работать. Но нас интересует импорт не столь простых файлов. Сейчас мне приходится работать с файлами Excel объемом более 50MB.
Как вам такие файлики? И кто это там, по-поводу будущего XML, как хранилища данных? Я пока в сторонке постою...
|