В первой части мы кратко посмотрели структуру документа XML. Теперь посмотрим на структуру более пристально. Сформируем документ с помощью VFP, используя родную функцию CursorToXML(). При формировании документа мы можем определить структуру документа на основе трех типов:
- Element centric
- Atrribute centric
- RAW
Воспользуемся таблицей Team из базы данных Common приложения примера VFPTests. Структура таблицы определена следующим образом включает в себя поля пяти типов - символьные, даты, двоичного мемо, денежного и численного:
Конвертируем таблицу в файлы XML, используя, по очереди, все три типа структуры.
=CURSORTOXML("Team","c:\vfptests\team_type1.xml",1,4608)
=CURSORTOXML("Team","c:\vfptests\team_type2.xml",2,4608)
=CURSORTOXML("Team","c:\vfptests\team_type3.xml",3,4608)
В результате получим файлы, содержание которых приведено ниже:
<?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>
<?xml version = "1.0" encoding="Windows-1252" standalone="yes"?>
<VFPData>
...<team id="TM_1A60NGA0C" last_name="Ivanov" first_name="Ivan"
birthdate="1980-01-10"
occupation="Manager" 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" salary="8000.0000"/>
<team id="TM_1A60NL66D" last_name="Petrov" first_name="Petr"
birthdate="1976-01-12"
occupation="Chief-director" oc_history="01.10.2000- Chief_Director"
salary="12000.0000"/>
</VFPData>
<?xml version = "1.0" encoding="Windows-1252" standalone="yes"?>
<VFPData>
<team id="TM_1A60NG" last_name="Ivanov" first_name="Ivan"
birthdate="1980-10-01" occupation="Manager" 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" salary="8000.0000" perquisite="1.25"/>
<team id="TM_1A60NL" last_name="Petrov" first_name="Petr"
birthdate="1976-01-12" occupation="Chief-director" oc_history="01.10.2000-
Chief_Director" salary="12000.0000" perquisite="1.40"/>
</VFPData>
Разберем эти три файла по порядку.
1. Первый файл был конвертирован как "Element centric". В этом случае каждая строка таблицы описана содержимым, обрамленным тэгами "team", а содержимое записи предcтавляет набор узлов, представляющих поля таблицы, обрамленное тэгами, имена которых сопадают с именами полей и заполненные содержимым этих полей.
Все было бы прекрасно, но из этого файла мы не можем извлечь информации о типах данных.
2. Второй файл был конвертирован как "Attribute centric". В этом случае каждая строка описана содержимым, обрамленным тэгами "team", а содержимое предcтавляет собой набор аттрибутов, по аттрибуту на каждое поле, при этом аттрибуты имеют имена полей и каждому аттрибуту присвоено содержимое соответствующего поля.
Как и в первом случае, из этого файла мы не можем извлечь информации о типах данных.
3. Третий файл был конвертирован как "RAW". Этот формат еще называют generic, поскольку информация в нем представлена строками, что и отражено в названии узлов. В этом случае каждая строка описана содержимым, обрамленным тэгами "row", а содержимое, как и во втором случае предcтавляет собой набор аттрибутов, по аттрибуту на каждое поле, при этом аттрибуты имеют имена полей и каждому аттрибуту присвоено содержимое соответствующего поля.
Как и ранее, из этого файла мы не можем извлечь информации о типах данных. Это означает, что при обратной конверсии мы теряем поля "memo" и "currency", в чем легко убедиться, выдав команду:.
XMLTOCURSOR("c:\vfptests\team_type1.xml","Test",4608)
COPY TO C:\vfptests\test_table.dbf
use C:\vfptests\test_table.dbf EXCLUSIVE
MODIFY STRUCTURE
...и взглянув на структуру:
Все три файла мы можем открыть в MS Excel 2003 (с использованием опции As Read-only Workbook). Обратите внимание, что в случае первого файла в лист рабочей книги будет добавлен еще один столбец с именем salary#agg. Второй и третий файл будут открыты одинаково.
Как получить информацию о типах данных, которые содержаться в конвертированных файлах? Для этого мы должны конвертировать наши курсоры с использованием опций schema.
Выполним команду:
=CURSORTOXML(;
"Team",;
"c:\vfptests\team_type1_xsd.xml",;
1,;
4608,;
0,;
"c:\vfptests\team_type1_xsd.xsd";
)
Что иы получим в результате исполнения этой команды? Мы конвертируем таблицу Team в файл "c:\vfptests\team_type1_xsd.xml" с типом "Element centric", то есть каждое поле будет выведено в файл как самостоятельный узел, при этом запретим конвертирование бинарного мемо-поля :(4608 составлено из флагов 512 - трактовать второе выражение команды, как имя файла, и 4096 - запрет конверсии бинарных мемо-полей с использованием кодирования base64), в файл XML будут выведены все записи и схема будет содержаться в файле "c:\vfptests\team_type1_xsd.xsd".
Содержимое полученных файлов "team_type1_xsd.xml" и "team_type1_xsd.xsd" приведено ниже.
<?xml version = "1.0" encoding="Windows-1252" standalone="yes"?>
<VFPData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="c:\vfptests\team_type1_xsd.xsd">
<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>
<?xml version = "1.0" encoding="Windows-1252" standalone="yes"?>
<xsd:schema id="VFPData" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="VFPData" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="team" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="id">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:maxLength value="12"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="last_name">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:maxLength value="20"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="first_name">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:maxLength value="20"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="birthdate" type="xsd:date"/>
<xsd:element name="occupation">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:maxLength value="20"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="oc_history">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:maxLength value="2147483647"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="salary">
<xsd:simpleType>
<xsd:restriction base="xsd:decimal">
<xsd:totalDigits value="19"/>
<xsd:fractionDigits value="4"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="perquisite">
<xsd:simpleType>
<xsd:restriction base="xsd:decimal">
<xsd:totalDigits value="3"/>
<xsd:fractionDigits value="2"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:anyAttribute namespace="http://www.w3.org/XML/1998/namespace" processContents="lax"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Полученный файл открывается без проблем в MS Excel 2003 (As XML List), но колонка salary игнорирует указанный в схеме тип данных "decimal" и выводит его как General. Кроме того, для содержимого поля oc_history в первой записи MS Excel автоматически устанавливает формат ячейки с параметром Wrap text, а для содержимого одноименного поля во второй записи - нет.
Подробнее использование схем рассмотрим в одной из следующих частей.
Если теперь провести обратное преобразование, то есть выполнить команду:
XMLTOCURSOR("c:\vfptests\team_type1_xsd.xml","Test",6656)
то мы получим курсор с "почти правильной" структурой и заполненный данными, содержащимися в XML-файле. Почему с "почти правильной"? Потому, что в исходной таблице для поля "memo" было указано NOCP, то есть мы имели дело с memo(binary), то в курсоре полученном из XML-документа мы получили простое memo-поле.
А что мы получим мы получим, если будем использовать новый класс XMLAdapter? Мы получим правильную структуру, правда при этом мы должны позаботиться о том, чтобы поля получили правильные типы. Чтобы поле salary получило тип "currency", необходимо указать объекту, полученному из класса XMLAdapter, что необходимо произвести разметку поля, имеющиего в схеме аттрибуты "decimal, 19, 4", для чего установить значение его свойства MapN19_4ToCurrency в .T.. Несколько сложнее указать, что поле "oc_history" должно быть бинарным. Дело в том, что если мы установим значение свойства NoCpTrans этого объекта в .T., то в бинарные будут превращены оба типа полей - как "character", так и "memo". В этом случае, мы должны установить значение такого свойства персонально для требуемого поля. После того, как XML загружен, мы можем обратиться к свойству соответствующего поля. Это может выглядеть так:
oXMLAdapter = Createobject('XMLAdapter')
oXMLAdapter.MapN19_4ToCurrency=.T.
oXMLAdapter.XMLSchemaLocation="c:\vfptests\team_type1_xsd.xsd"
oXMLAdapter.LoadXML("c:\vfptests\team_type1_xsd.xml",.T.,.T.)
For Each oField In oXMLAdapter.Tables.Item(1).Fields
If oField.Alias='oc_history'
oField.NoCPTrans=.T.
Exit
Endif
Endfor
oXMLAdapter.Tables.Item(1).ToCursor
Copy To C:\vfptests\test_table.Dbf
Use C:\vfptests\test_table.Dbf
Modify Structure
В таком случае мы имеем требуемую структуру.
Класс XMLAdapter будет подробно рассмотрен в одной из последующий статей.
И, наконец, аналогичный результат мы получим, если будем использовать новый класс CursorAdapter, который по сравнению с XMLAdapter'ом потребует большей писанины:
oCA=CREATEOBJECT("CURSORADAPTER")
oCA.DataSourceType="XML"
oCA.InsertCmdDataSourceType="XML"
oCA.UpdateCmdDataSourceType="XML"
oCA.DeleteCmdDataSourceType="XML"
oCA.CursorSchema="id C(12), last_name C(20), first_name C(20), birthdate D,;
occupation C(20), oc_history M NOCPTRANS, salary Y, perquisite N(4,2)"
oCA.SelectCmd="c:\vfptests\team_type1_xsd.xml"
oCA.CursorFill(.T.,.f.,512)
COPY TO C:\vfptests\test_table.dbf
USE
use C:\vfptests\test_table.dbf
MODIFY STRUCTURE
|