6161
Как получить dbf-таблицы из xls-файла при наличии групп в данных?

 

Содержание:

Что/чем будем делать.

Вопрос действительно интересный! :-) Схема получения dbf-таблиц могла бы быть следующей:

  1. получение xml-данных из xls-файла
  2. преобразование полученных xml-данных в структуру, пригодную для преобразований в связанные dbf-файлы
  3. ну и собственно преобразование xml-данных в dbf-таблицы, используя VFP-класс XMLAdapter

Первый шаг может быть выполнен достаточно просто, если вы имеет Excel из MS Office 2003 (или выше). Чтобы выполнить второй шаг, на мой взгляд, проще всего воспользоваться технологией XSLT-преобразований, для чего следует хотя бы в общих чертах иметь представление об этой технологии. Другие пути для выполнения преобразований над xml-данными в использовании xml-парсеров и объектной модели DOM, также возможно написание кода обработки с использованием SAX2. Если у вас большие объёмы данных, то последнее выглядит наиболее предпочтительным. Наконец, чтобы воспользоваться VFP-классом XMLAdapter, появившемся в версии 8.0, вы должны быть обладателем именно версии VFP 9.0 (или выше), т.к. в перелагаемом в этой статье VFP-коде, используется свойство XMLField.XMLNameIsXPath. Ниже предполагается, что все перечисленные условия удовлетворены.

Итак, попробуем пройти все шаги, разбирая конкретный пример данных в MS Excel 2003. Допустим, что требуется получить dbf-таблицы для данных, которые в MS Excel выглядят так:

Рис.1

Представленная таблица имеет "заголовок таблицы" (строки: 3-6), со строк: 7, 15, ... начинаются группы, а со строк: 8,13;16,... начинаются соответствующие подгруппы... Каждая подгруппа помимо своего "заголовка" (строки: 8, 13, 16, ...), имеет некоторое множество строк - "содержания подгруппы" (строки: 9-12,14,17-20, ...).

По этим данным, требуется получить три VFP-таблицы связанные отношениями:

 

Экспорт данных Excel-таблицы в xml-файл

Как было сказано выше, для этого средствами MS Excel 2003 (или выше), выделив соответствующую таблицу (из файла: table.xls), выполним пункт меню: Файл/Сохранить как...[Другие форматы/Тип файла: Таблица XML 2003].

Рис.2

при этом, на возникший запрос о несоответствии формата следует ответить утвердительно. После чего мы получим файл с данными таблицы в xml-формате примерно такой структуры:

<?xml version="1.0" ?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
  <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
  ...
  </DocumentProperties>
  <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
  ...
  </OfficeDocumentSettings>
  <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
  ...
  </ExcelWorkbook>
  <Styles>
    <Style ss:ID="Default" ss:Name="Normal">
    ...
    </Style>
    <Style ss:ID="s23">
    ...
    </Style>
    ...
    <Style ss:ID="sNN">
    ...
    </Style>
    ...
  </Styles>
  <Worksheet ss:Name="...Лист1">
    <Table ...>
      <Column ss:AutoFitWidth="0" ss:Width="24" />
      <Column ss:AutoFitWidth="0" ss:Width="216" />
      <Column ss:AutoFitWidth="0" ss:Width="26.25" />
      <Column ss:AutoFitWidth="0" ss:Width="40.5" />
      <Column ss:AutoFitWidth="0" ss:Width="196.5" />
      <Column ss:AutoFitWidth="0" ss:Width="65.25" />
      <Column ss:AutoFitWidth="0" ss:Width="57.75" />
      <Column ss:AutoFitWidth="0" ss:Width="55.5" />
      <Column ss:AutoFitWidth="0" ss:Width="53.25" />
      <Row ss:AutoFitHeight="0" ss:Height="21">
        <Cell ss:Index="2" ss:StyleID="s23">
          <Data ss:Type="String">Форма №7</Data>
        </Cell>
        <Cell ss:StyleID="s23" />
      </Row>
      <Row ss:AutoFitHeight="0" ss:Height="42.75">
        <Cell ss:Index="2" ss:StyleID="s24">
          <Data ss:Type="String">Перечень работ кап. строительства на 2007 г. (без НДС, 
тыс.руб.)</Data>
        </Cell>
        <Cell ss:StyleID="s24" />
      </Row>
      ...
      <Row ss:AutoFitHeight="0" ss:Height="22.5">
        <Cell ss:StyleID="s32" />
        <Cell ss:StyleID="s43">
          <Data ss:Type="String">79.01 - СКРУ №1</Data>
        </Cell>
        <Cell ss:StyleID="s43" />
        <Cell ss:StyleID="s31" />
        <Cell ss:StyleID="s31" />
        <Cell ss:StyleID="s32" />
        <Cell ss:StyleID="s33" />
        <Cell ss:StyleID="s32" />
        <Cell ss:StyleID="s32" />
      </Row>
      <Row>
        <Cell ss:StyleID="s32" />
        <Cell ss:StyleID="s44">
          <Data ss:Type="String">01020000 - РУДНИК СКРУ-1</Data>
        </Cell>
        <Cell ss:StyleID="s44" />
        <Cell ss:StyleID="s31" />
        <Cell ss:StyleID="s31" />
        <Cell ss:StyleID="s32" />
        <Cell ss:StyleID="s33" />
        <Cell ss:StyleID="s32" />
        <Cell ss:StyleID="s32" />
      </Row>
      <Row ss:Height="29.25">
        <Cell ss:StyleID="s42">
          <Data ss:Type="Number">3</Data>
        </Cell>
        <Cell ss:StyleID="s35">
          <Data ss:Type="String">ОАО СИЛЬВИНИТ, ШСУ</Data>
        </Cell>
        <Cell ss:StyleID="s36">
          <Data ss:Type="String">1</Data>
        </Cell>
        <Cell ss:StyleID="s35">
          <Data ss:Type="String">0601011067</Data>
        </Cell>
        <Cell ss:StyleID="s37">
          <Data ss:Type="String">ВСКРЫТИЕ И ПОДГОТОВКА ЗАПАСОВ НОВО-СОЛИКАМСКОГО УЧАСТКА...</Data>
        </Cell>
        <Cell ss:StyleID="s38">
          <Data ss:Type="Number">104440</Data>
        </Cell>
        <Cell ss:StyleID="s39">
          <Data ss:Type="Number">29440</Data>
        </Cell>
        <Cell ss:StyleID="s40">
        <Data ss:Type="Number">9480</Data>
        </Cell>
        <Cell ss:StyleID="s40">
          <Data ss:Type="Number">75000</Data>
        </Cell>
      </Row>
      <Row ss:Height="19.5">
        <Cell ss:StyleID="s42">
          <Data ss:Type="Number">4</Data>
        </Cell>
        <Cell ss:StyleID="s35">
          <Data ss:Type="String">ОАО СИЛЬВИНИТ, ШСУ</Data>
        </Cell>
        <Cell ss:StyleID="s36">
          <Data ss:Type="String">1</Data>
        </Cell>
        <Cell ss:StyleID="s35">
          <Data ss:Type="String">0601011070</Data>
        </Cell>
        <Cell ss:StyleID="s37">
          <Data ss:Type="String">ОТРАБОТКА ПЛ.ВК В I И II СЕВЕРНЫХ ПАНЕЛЯХ РУДНИКА</Data>
        </Cell>
        <Cell ss:StyleID="s38">
          <Data ss:Type="Number">12905</Data>
        </Cell>
        <Cell ss:StyleID="s39">
          <Data ss:Type="Number">12905</Data>
        </Cell>
        <Cell ss:StyleID="s40">
          <Data ss:Type="Number">6536</Data>
        </Cell>
        <Cell ss:StyleID="s34" />
      </Row>
      ...
    </Table>
    <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
    ...
    </WorksheetOptions>
  </Worksheet>
</Workbook>
Файл: _table.xml

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

 

Удаление "области имён по умолчанию"

К сожалению, наличие "области имён по умолчанию" делает невозможным использование таких средств как XSLT-преобразования. Поэтому первым шагом на нашем пути, удалим xmlns="urn:schemas-microsoft-com:office:spreadsheet" из корневого элемента Workbook, а полученный после такого редактирования файл сохраним с новым именем (из _table.xml в table.xml в нашем случае).

 

Признаки отбора "прикладных данных"

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

Далее, мы постараемся воспользоваться этими обстоятельствами, чтобы организовать обработку данных с помощью XSLT-преобразования.

 

XSLT-преобразование из "плоского" XML в "структурированный", согласно группировки данных

Если вы уже имели опыт написания XSLT-преобразований, вам наверное известно, что всякий раз, когда требуется решить задачу, связанную "с организацией группировок в XSLT", мы испытываем вполне ощутимые трудности... :-( см. например, одно из решений здесь: http://xmlhack.ru/books/xslt/ch_11.html. В нашем случае, "на входе" мы имеем "линейную" последовательность элементов Row, тогда как "на выходе"  т.е. в результате преобразований, нам требуется получить "вложенные" xml-структуры согласно связям типа: "родитель -> дети".

Чтобы обеспечить это, воспользуемся перебором всего множества элементов из /.//Workbook/Worksheet/Table/Row с помощью XSLT-конструкции: <xsl:for-each>, организуя требуемую вложенность элементов их "динамическим формированием" в выходной поток. Что же конкретно нам нужно? Попробуем сформулировать:

Здесь предполагается, что во входном потоке отсутствуют "разрывы" в смысле групп/подгрупп. Т.е. данные во входном потоке упорядочены в строгом соответствии с требованиями группировок, а также и то, что во входном потоке нет данных, не принадлежащих ни группам, ни их подгруппам. Основная трудность в реализации этой схемы заключается в проверке условий на момент открытия новой группы/подгруппы, позволяющих ответить на вопрос: а есть ли ранее открытые аналогичные группы/подгруппы, которые следует закрыть при открытии новых?

При вышеупомянутых предположениях ответ на этот вопрос достаточно прост:

Ниже XSTL-код, реализующий описанную схему:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
  xmlns:o="urn:schemas-microsoft-com:office:office"
  xmlns:x="urn:schemas-microsoft-com:office:excel"
  xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
  xmlns:html="http://www.w3.org/TR/REC-html40"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  exclude-result-prefixes="o x ss html xsl msxsl msdata">
 
  <xsl:output method="xml"
    encoding="windows-1251"
    version="1.0"
    omit-xml-declaration="yes" />
 
  <xsl:variable name="varLft">
    <xsl:text disable-output-escaping="yes">&#60;</xsl:text>
  </xsl:variable>
  <xsl:variable name="varLft1">
    <xsl:text disable-output-escaping="yes">&#60;/</xsl:text>
  </xsl:variable>
  <xsl:variable name="varRgt">
    <xsl:text disable-output-escaping="yes">&#62;</xsl:text>
  </xsl:variable>
 
  <xsl:variable name="varBegGroup">
    <xsl:value-of disable-output-escaping="yes" select="concat($varLft, 'group', $varRgt)" />
  </xsl:variable>
  <xsl:variable name="varEofGroup">
    <xsl:value-of disable-output-escaping="yes" select="concat($varLft1, 'group', $varRgt)" />
  </xsl:variable>
 
  <xsl:variable name="varBegSubGroup">
    <xsl:value-of disable-output-escaping="yes" select="concat($varLft, 'subgroup', $varRgt)" />
  </xsl:variable>
  <xsl:variable name="varEofSubGroup">
    <xsl:value-of disable-output-escaping="yes" select="concat($varLft1, 'subgroup', $varRgt)" />
  </xsl:variable>
 
  <xsl:variable name="varBegName">
    <xsl:value-of disable-output-escaping="yes" select="concat($varLft, 'name', $varRgt)" />
  </xsl:variable>
  <xsl:variable name="varEofName">
    <xsl:value-of disable-output-escaping="yes" select="concat($varLft1, 'name', $varRgt)" />
  </xsl:variable>
 
  <xsl:variable name="varBegId">
    <xsl:value-of disable-output-escaping="yes" select="concat($varLft, 'id', $varRgt)" />
  </xsl:variable>
  <xsl:variable name="varEofId">
    <xsl:value-of disable-output-escaping="yes" select="concat($varLft1, 'id', $varRgt)" />
  </xsl:variable>
 
  <xsl:variable name="varFirstGrpID">
    <xsl:value-of select="generate-id(/.//Workbook/Worksheet/Table/Row/Cell[@ss:StyleID='s43']/..)"/>
  </xsl:variable>
 
  <xsl:template match="/">
    <xsl:text disable-output-escaping="yes"><![CDATA[<?xml version="1.0" encoding="windows-1251"?>]]></xsl:text>
    <xsl:text disable-output-escaping="yes">&#xA;</xsl:text>
    <root>
     <xsl:attribute name="form">
       <xsl:value-of select="normalize-space(/.//Workbook/Worksheet/Table/Row/Cell[@ss:StyleID='s23']/Data)"/>
     </xsl:attribute>
     <xsl:attribute name="title">
       <xsl:value-of select="normalize-space(/.//Workbook/Worksheet/Table/Row/Cell[@ss:StyleID='s24']/Data)"/>
     </xsl:attribute>
 
     <xsl:for-each select="/.//Workbook/Worksheet/Table/Row">
       <!-- // group -->
       <xsl:if test="Cell[@ss:StyleID='s43']/Data">
         <xsl:if test="generate-id()!=$varFirstGrpID">
           <!-- </subgroup> -->
           <xsl:value-of disable-output-escaping="yes" select="$varEofSubGroup" />
           <!-- </group> -->
           <xsl:value-of disable-output-escaping="yes" select="$varEofGroup" />
         </xsl:if>
         <!-- <group> -->
         <xsl:value-of disable-output-escaping="yes" select="$varBegGroup" />
         <xsl:variable name="varGroupName">
           <xsl:value-of disable-output-escaping="yes" select="normalize-space(Cell[@ss:StyleID='s43']/Data)" />
         </xsl:variable>
         <xsl:variable name="varGroupID">
           <xsl:value-of select="generate-id()" />
         </xsl:variable>
         <!-- <id> -->
         <xsl:value-of disable-output-escaping="yes" select="$varBegId" />
         <xsl:value-of disable-output-escaping="yes" select="$varGroupID" />
         <xsl:value-of disable-output-escaping="yes" select="$varEofId" />
         <!-- <name> -->
         <xsl:value-of disable-output-escaping="yes" select="$varBegName" />
         <xsl:value-of disable-output-escaping="yes" select="$varGroupName" />
         <xsl:value-of disable-output-escaping="yes" select="$varEofName" />
       </xsl:if>
       <!-- group // -->
       <!-- // subgroup -->
       <xsl:if test="Cell[@ss:StyleID='s44']/Data">
         <xsl:if test="string-length((preceding-sibling::*)[last()]/Cell[@ss:StyleID='s43']/Data)=0">
           <!-- </subgroup> -->
           <xsl:value-of disable-output-escaping="yes" select="$varEofSubGroup" />
         </xsl:if>
         <!-- <subgroup> -->
         <xsl:value-of disable-output-escaping="yes" select="$varBegSubGroup" />
         <xsl:variable name="varGroupName">
           <xsl:value-of disable-output-escaping="yes" select="normalize-space(Cell[@ss:StyleID='s44']/Data)" />
         </xsl:variable>
         <xsl:variable name="varGroupID">
           <xsl:value-of select="generate-id()" />
         </xsl:variable>
         <!-- <id> -->
         <xsl:value-of disable-output-escaping="yes" select="$varBegId" />
         <xsl:value-of disable-output-escaping="yes" select="$varGroupID" />
         <xsl:value-of disable-output-escaping="yes" select="$varEofId" />
         <!-- <name> -->
         <xsl:value-of disable-output-escaping="yes" select="$varBegName" />
         <xsl:value-of disable-output-escaping="yes" select="$varGroupName" />
         <xsl:value-of disable-output-escaping="yes" select="$varEofName" />
       </xsl:if>
       <!-- subgroup // -->
 
       <xsl:apply-templates select="." />
 
     </xsl:for-each>
 
     <!-- </subgroup> -->
     <xsl:value-of disable-output-escaping="yes" select="$varEofSubGroup" />
     <!-- </group> -->
     <xsl:value-of disable-output-escaping="yes" select="$varEofGroup" />
   </root>
  </xsl:template>
 
  <xsl:template match="Data" />
 
  <xsl:template match="Row">
    <xsl:if test="Cell[@ss:StyleID='s42']/Data">
      <cells>
        <xsl:for-each select="Cell">
          <xsl:if test="Data">
            <xsl:element name="{concat('d',string(position()))}">
              <xsl:value-of disable-output-escaping="yes" select="normalize-space(Data)" />
            </xsl:element>
          </xsl:if>
        </xsl:for-each>
      </cells>
    </xsl:if>
  </xsl:template>
 
</xsl:stylesheet>
Файл: ConvertTable.xslt

Здесь

Фрагмент результата выполнения преобразования ConvertTable.xslt над файлом table.xml выглядит так:

<?xml version="1.0" encoding="windows-1251"?>
<root form="Форма №7" title="Перечень работ кап. строительства на 2007 г. (без НДС, тыс.руб.)">
  <group>
    <id>IDAJL1P</id>
    <name>79.01 - СКРУ №1</name>
    <subgroup>
      <id>IDAOM1P</id>
      <name>01020000 - РУДНИК СКРУ-1</name>
      <cells>
        <d1>3</d1>
        <d2>ОАО СИЛЬВИНИТ, ШСУ</d2>
        <d3>1</d3>
        <d4>0601011067</d4>
        <d5>ВСКРЫТИЕ И ПОДГОТОВКА ЗАПАСОВ НОВО-СОЛИКАМСКОГО УЧАСТКА ВКМКС РУДНИКОМ СКРУ-1 ОАО "СИЛЬВИНИТ"</d5>
        <d6>104440</d6>
        <d7>29440</d7>
        <d8>9480</d8>
        <d9>75000</d9>
      </cells>
      <cells>
        <d1>4</d1>
        <d2>ОАО СИЛЬВИНИТ, ШСУ</d2>
        <d3>1</d3>
        <d4>0601011070</d4>
        <d5>ОТРАБОТКА ПЛ.ВК В I И II СЕВЕРНЫХ ПАНЕЛЯХ РУДНИКА</d5>
        <d6>12905</d6>
        <d7>12905</d7>
        <d8>6536</d8>
      </cells>
      <cells>
        <d1>5</d1>
        <d2>ОАО СИЛЬВИНИТ, ШСУ</d2>
        <d3>1</d3>
        <d4>0601011071</d4>
        <d5>ЗАКЛАДКА ГИДРАВЛИЧЕСКИМ СПОСОБОМ 10 И 12 ЗАПАДНЫХ ПАНЕЛЕЙ РУДНИКА</d5>
        <d6>36015</d6>
        <d7>32015</d7>
        <d8>19764</d8>
        <d9>4000</d9>
      </cells>
      <cells>
        <d1>12</d1>
        <d2>ОАО СИЛЬВИНИТ, ШСУ</d2>
        <d3>1</d3>
        <d4>0601022785</d4>
        <d5>ГИДРАВЛИЧЕСКАЯ ЗАКЛАДКА В БЛОКАХ 70,72,74,80,82,84,90,92,94</d5>
        <d6>13135</d6>
        <d7>13135</d7>
      </cells>
    </subgroup>
    <subgroup>
      <id>IDAVW1P</id>
      <name>01040000 - СИЛЬВИНИТОВАЯ ОБОГАТИТЕЛЬНАЯ ФАБРИКА</name>
      <cells>
        <d1>14</d1>
        <d2>ОАО СИЛЬВИНИТ, ШСУ</d2>
        <d3>1</d3>
        <d4>0601011106</d4>
        <d5>УСТАНОВКА ЦЕНТРИФУГ ПОЗ.238-4 НА СОФ</d5>
        <d6>43000</d6>
        <d7>2000</d7>
        <d8>1600</d8>
        <d9>41000</d9>
      </cells>
    </subgroup>
  </group>
  <group>
    <id>IDAI01P</id>
    <name>79.02 - СКРУ №2</name>
    <subgroup>
      <id>IDAM11P</id>
      <name>02020000 - РУДНИК СКРУ-2</name>
      <cells>
      ...
      </cells>
    </
subgroup>
    ...
  </group>
</root>
Фрагменты файла: out.xml

 

Преобразование xml-данных в dbf-таблицы, используя VFP-класс XMLAdapter

Вот только теперь можно воспользоваться VFP-классом XMLAdapter для получения соответствующих dbf-файлов. Однако, в полученном нами xml-файле имеется небольшая проблема, заключающаяся в том, что информация по первичным ключам в данных для групп/подгрупп имеется (значения элементов <id>...</id>, динамически нами сформированных во время выполнения вышеприведённого XSLT-преобразования), в то время как внешние ключи (foreign keys) в xml-файле непосредственно отсутствуют. К счастью, гибкость VFP-класса XMLAdapter в версии 9.0 позволяет устранить эту проблему путём формирования как самих внешних ключей, так и их значений во время выполнения преобразования.

Код, выполняющий подобное преобразование мог бы быть таким:

ACTIVATE SCREEN
CLEAR
CLOSE TABLES ALL
LOCAL loXA as XMLAdapter
loXA = NEWOBJECT("XMLAdapter")
 
*// Create tables
CREATE CURSOR group (id C(7), name C(120))
CREATE CURSOR subgroup (id C(7), name C(120), fk_id C(7))
CREATE CURSOR cells (d1 I, d2 C(120), d3 I, d4 I, d5 C(120), d6 F(18,2), d7 I, d8 I, d9 F(18,2), fk_id C(7))
 
*// Load XML-data
loXA.RespectCursorCP == .T.
loXA.UseCodePage = .T.
loXA.LoadXML("out.xml", .T., .F.)
 
*// AddTableSchemas
LOCAL loXtSg as XMLTable, loXtSl as XMLTable
loXA.AddTableSchema("group", .T.)
loXtSg = loXA.AddTableSchema("subgroup", .T.)
loXtSl = loXA.AddTableSchema("cells", .T.)
 
*/////////////////////////////
*// Create foreign keys
LOCAL loXf as XMLField
 
*// Add foreign key to XMLTable subgroup
loXf = NEWOBJECT("XMLField")
WITH loXf
  .Alias = "fk_id"
  .XMLNameIsXPath = .T.
  .DataType = "C"
  .MaxLength = 7
  .XMLName = STRCONV("parent::group/id", 5)
  loXtSg.Fields.Add(loXf, .XMLName)
ENDWITH
 
*// Add foreign key to XMLTable cells
loXf = NEWOBJECT("XMLField")
WITH loXf
  .Alias = "fk_id"
  .XMLNameIsXPath = .T.
  .DataType = "C"
  .MaxLength = 7
  .XMLName = STRCONV("parent::subgroup/id", 5)
  loXtSl.Fields.Add(loXf, .XMLName)
ENDWITH
 
*// Create VFP-tables
FOR EACH loTable as XMLTable IN loXA.Tables
  IF VARTYPE(loTable) = 'O' AND loTable.ToCursor(.T.) AND USED(loTable.Alias)
    SELECT (loTable.Alias)
    LIST
  ENDIF
ENDFOR
 
*// Save result as XML-data
SET SAFETY OFF
loXA.ReleaseXML(.F.)
loXA.ToXML("output.xml","",.T.)
SET SAFETY ON
Файл: getdbf.prg

На рисунке ниже представлен результат работы вышеприведённого кода:

Рис.3

Преобразование данных dbf-файла в xml 2003 таблицу, пригодную для просмотра из Excel

Используя XSLT-преобразования можно решить и обратную задачу: преобразование dbf-файла в xml-данные такой структуры, которые будут пригодны для просмотра из-под MS Excel 2003 (или выше). В простейшем случае, задача может быть решена за два шага:

Проделаем это для фрагмента данных из dbf-таблицы, представленной на рисунке ниже:

Рис.4

Первый шаг может быть решён использованием функции CursorToXML(), например таким VFP-кодом:

#DEFINE DBF_FILE "spisok"
SET DEFAULT TO (LEFT(SYS(16), RATC("\", SYS(16))))
IF !USED(DBF_FILE)
  USE (DBF_FILE) IN 0 SHARED
    IF !USED(DBF_FILE)
      RETURN .F.
    ENDIF
ENDIF
SET SAFETY OFF
CURSORTOXML(DBF_FILE, DBF_FILE, 3, 4+16+512, 0, "1")
SET SAFETY ON
USE IN (DBF_FILE)
Файл: dbftoxml.prg

в результате выполнения которого, получим вот такой xml-файл:

<?xml version = "1.0" encoding="Windows-1251" standalone="yes"?>
<VFPData xml:space="preserve">
  <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="row" minOccurs="0" maxOccurs="unbounded">
            <xsd:complexType>
              <xsd:attribute name="kod_atc" type="xsd:int" use="required"/>
              <xsd:attribute name="raion" type="xsd:int" use="optional"/>
              <xsd:attribute name="name_atc" use="optional">
                <xsd:simpleType>
                  <xsd:restriction base="xsd:string">
                    <xsd:maxLength value="15"/>
                  </xsd:restriction>
                </xsd:simpleType>
              </xsd:attribute>
              <xsd:attribute name="adr" use="optional">
                <xsd:simpleType>
                  <xsd:restriction base="xsd:string">
                    <xsd:maxLength value="50"/>
                  </xsd:restriction>
                </xsd:simpleType>
              </xsd:attribute>
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
        <xsd:anyAttribute namespace="http://www.w3.org/XML/1998/namespace" processContents="lax"/>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <row kod_atc="1" raion="1" name_atc="ОПТС-31 " adr="ул. Коноплянниковой, 4а "/>
  <row kod_atc="2" raion="1" name_atc="АТСК 33 " adr="ул. Новоторжская, 18 "/>
  <row kod_atc="3" raion="1" name_atc="ОПТС-36 " adr="ул. Склизкова, 36 "/>
  <row kod_atc="4" raion="1" name_atc="АТСК 100/2000 " adr="пос. &quot;Сахарово&quot; "/>
  <row kod_atc="5" raion="1" name_atc="АТС &quot;ВНИИСВ&quot; " adr="пос.Химинститутa "/>
  <row kod_atc="6" raion="1" name_atc="ПСК &quot;Южный&quot; " adr="ПСК &quot;Южный&quot;, ПСК &quot;Чайка&quot;,пр-т Победы,64 "/>
  <row kod_atc="7" raion="1" name_atc="ОПТС-32,34,50 " adr="ул. Новоторжская, д.18 "/>
  <row kod_atc="8" raion="1" name_atc="ОПТС-43,45 " adr="ул. Склизкова, 36 "/>
  <row kod_atc="9" raion="1" name_atc="ОПТС-(55-56) " adr="ул.Оборонная,д.4 "/>
  <row kod_atc="10" raion="1" name_atc="OПТC-42,44 " adr="ул. Баррикадная, д.8 "/>
</VFPData>
Файл: spisok.XML

Если теперь к полученному файлу spisok.XML применить вот такое XSLT-преобразование:

<?xml version="1.0" encoding="windows-1251"?>
<!-- file: convertToXslXml.xslt & see also: Q319180 "How to transform a DataSet to spreadsheet XML for Excel by using Visual Basic .NET and ASP.NET"-->
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
    xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
    exclude-result-prefixes="xsl msxsl xsd msdata ss">
 
  <xsl:output version="1.0"
      method="xml"
      encoding="windows-1251"/>
 
  <!-- Выборка типа xsd:attribute из схемы по названию атрибута (@name) -->
  <xsl:key name="rowType" match="/VFPData/xsd:schema/xsd:element/xsd:complexType/xsd:choice/xsd:element/xsd:complexType/xsd:attribute" use="@name" />
 
  <!-- Ряд параметров, значения которых можно переустановить перед обращением к преобразованию -->
  <xsl:param name="prmLastAuthor">
    <xsl:text>Michael</xsl:text>
  </xsl:param>
  <xsl:param name="prmCreated">
    <xsl:text>2007-04-21T09:11:11Z</xsl:text>
  </xsl:param>
  <xsl:param name="prmWorksheetName">
    <xsl:text>spisok</xsl:text>
  </xsl:param>
 
  <xsl:template match="/">
    <!-- Выводим "заголовок" специфичный для MS Excel -->
    <xsl:text disable-output-escaping="yes"><![CDATA[<?mso-application progid="Excel.Sheet"?>]]></xsl:text>
    <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:o="urn:schemas-microsoft-com:office:office"
        xmlns:x="urn:schemas-microsoft-com:office:excel"
        xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:html="http://www.w3.org/TR/REC-html40">
      <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
        <LastAuthor>
          <xsl:value-of select="$prmLastAuthor" disable-output-escaping="yes" />
        </LastAuthor>
        <Created>
          <xsl:value-of select="$prmCreated" disable-output-escaping="yes" />
        </Created>
        <Version>12.00</Version>
      </DocumentProperties>
      <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
        <WindowHeight>12345</WindowHeight>
        <WindowWidth>18960</WindowWidth>
        <WindowTopX>120</WindowTopX>
        <WindowTopY>75</WindowTopY>
        <ProtectStructure>False</ProtectStructure>
        <ProtectWindows>False</ProtectWindows>
      </ExcelWorkbook>
      <Styles>
        <Style ss:ID="Default" ss:Name="Normal">
          <Alignment ss:Vertical="Bottom"/>
          <Borders/>
          <Font ss:FontName="Calibri" x:CharSet="204" x:Family="Swiss" ss:Size="11"
ss:Color="#000000"/>
          <Interior/>
          <NumberFormat/>
          <Protection/>
        </Style>
      </Styles>
      <Worksheet ss:Name="{$prmWorksheetName}">
        <Table ss:ExpandedColumnCount="256" ss:ExpandedRowCount="11" x:FullColumns="1"
x:FullRows="1" ss:DefaultRowHeight="15">
          <!-- Выводим колонки (Column), взяв информацию из списка xsd:attribute в схеме, а также определяем приблизительное значение ss:Width для каждой из колонок -->
          <xsl:apply-templates select="/./*/xsd:schema" />
          <!-- Выводим заголовки колонок, взяв информацию также из списка xsd:attribute в схеме... -->
          <xsl:apply-templates select="/./*/xsd:schema/xsd:element/xsd:complexType/xsd:choice/xsd:element" mode="flds" />
          <!-- ... наконец, выводим собственно содержание dbf-файла (т.е. данные из каждого элеменета <row />) -->
          <xsl:apply-templates select="/./*/row" />
        </Table>
        <!-- Выводим "завершающий остаток" специфичный для MS Excel -->
        <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
          <Selected/>
          <ProtectObjects>False</ProtectObjects>
          <ProtectScenarios>False</ProtectScenarios>
        </WorksheetOptions>
      </Worksheet>
    </Workbook>
  </xsl:template>
 
  <!-- Выводим информацию для каждой колонки (Column) взяв информацию из списка xsd:attribute в схеме, а также определяем приблизительное значение ss:Width для каждой из колонок -->
  <xsl:template match="xsd:attribute">
    <!-- Пытаемся приблизительно определить длину обрабатываемой колонки-->
    <xsl:variable name="varWidth">
      <xsl:choose>
        <xsl:when test="count(child::*)>0 and xsd:simpleType/xsd:restriction/xsd:maxLength/@value">
          <xsl:value-of select="number(xsd:simpleType/xsd:restriction/xsd:maxLength/@value)*10" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>20</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <Column ss:AutoFitWidth="0" ss:Width="{$varWidth}" xmlns="urn:schemas-microsoft-com:office:spreadsheet" />
  </xsl:template>
 
  <!-- Выводим заголовок текущей колонки, взяв информацию также из списка xsd:attribute в схеме... -->
  <xsl:template match="xsd:element" mode="flds">
    <xsl:if test="@name='row'">
      <Row xmlns="urn:schemas-microsoft-com:office:spreadsheet">
      <xsl:for-each select="xsd:complexType/xsd:attribute">
        <Cell>
        <Data ss:Type="String">
          <xsl:value-of select="@name" />
        </Data>
        </Cell>
      </xsl:for-each>
      </Row>
    </xsl:if>
  </xsl:template>
 
  <!-- ... наконец, определяем вывод для каждого элемента <row />, т.е. собственно содержание dbf-файла -->
  <xsl:template match="row">
    <Row xmlns="urn:schemas-microsoft-com:office:spreadsheet">
      <!-- для каждого из атрибутов... -->
      <xsl:for-each select="@*">
        <!-- пытаемся выбрать его тип из схемы... -->
        <xsl:variable name="varRowType">
          <xsl:value-of select="key('rowType',local-name())/@type" />
        </xsl:variable>
        <!-- ... определяем текущий Excel-тип: либо как 'Number' (для xsd:int) или как 'String' в противном случае -->
        <xsl:variable name="varType">
          <xsl:choose>
            <xsl:when test="$varRowType='xsd:int'">
              <xsl:text>Number</xsl:text>
            </xsl:when>
            <xsl:otherwise>
              <xsl:text>String</xsl:text>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
        <!-- выводим значение очередной ячейки -->
        <Cell>
          <Data ss:Type="{$varType}">
            <xsl:value-of select="." />
          </Data>
        </Cell>
      </xsl:for-each>
    </Row>
  </xsl:template>
 
</xsl:stylesheet>
Файл: convertToXslXml.xslt

то полученный результат из-под MS Excel выглядит так:

Файл: result.xml

Ссылки для загрузки кода и др. ресурсы

 
 
Hosted by uCoz