Xml шаблон документа в word

Introduction

Hi, thanks for your interest in this tip. I am writing this tip because I faced a problem in achieving a simple goal.
The goal was to use a Word document as a template and create a new one, some contents from the template should get replaced by user provided values.

There are plenty of ways provided for this task but it needed more understanding of XML templates. May be some of us expect rapid task completion without spending more
time on understandings. Hence this tip is useful.

A detailed problem and its solution are provided below.

Background

OpenXML is widely used for creating/updating Office documents. It has a predefined structure of XML for document. One can refer to this
link for details.

My Task: Use Open XML and create a new Word document. Template is predefined with extension as «*.docx«.
This template contains some words which need to be replaced by user provided values.

Let’s Do It

Before you start using Visual Studio, check whether you have installed Open XML 2.0
on machine. If yes, we are good to go forward.

Follows the steps:

  1. Open Visual Studio and select the blank ASP.NET project, add references
    of two DLLs to the project: «WindowsBase» and «DocumentFormat.OpenXml«.
  2. Then create two folders «Templates» and «New Documents«.
  3. Add a Word document «mytemplate.docx» in the template
    folder to use it as template for new documents.
  4. Add your text in document. E.g., Hi folks, My name is “Replaceable Text”.
  5. Now on default.aspx, add the following code:
  6. Please enter your name <asp:TextBox ID="txtName" runat="server" />:

    <asp:Button ID="btnSubmit" runat="server" Text="Create" />
  7. Now click onto button to generate click event and add the below code in it.
  8. protected void btnSubmit_Click(object sender, EventArgs e){
        #region
        try
        {
            string sourceFile = Server.MapPath(Path.Combine("/", "Templates/mytemplate.docx"));
            string destinationFile = Server.MapPath
            (Path.Combine("/", "New Documents/FirstDocument.docx"));
    
            
            File.Copy(sourceFile, destinationFile, true);
    
           	
    		
            Dictionary<string, string> keyValues = new Dictionary<string, string>();
            keyValues.Add(""Replaceable Text"", txtName.Text);                
      		SearchAndReplace(destinationFile, keyValues);
    
            Process.Start(destinationFile);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    
    #endregion
    } 

    Note: «Replaceable Text» can be any text like ‘My text’ or ::#My Text::#

  9. Add one more function below above event.
  10. public static void SearchAndReplace(string document, Dictionary<string, string> dict)
    {
        using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, true))
        {
            string docText = null;
            using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
            {
                docText = sr.ReadToEnd();
            }
    
            foreach (KeyValuePair<string, string> item in dict) 
            {
                Regex regexText = new Regex(item.Key);
                docText = regexText.Replace(docText, item.Value);
            }
    
            using (StreamWriter sw = new StreamWriter(
                      wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
            {
                sw.Write(docText);
            }
    
        }
    }
  11. Compile code and execute it and we are done with code.
  12. Enter name in text box and file will open automatically.

Expected Problems and Limitations

This article has been developed by considering that the template you are using is correctly formatted.

  1. Let me explain in detail as:
  2. Consider you are trying to replace word from document is «Employee» it will not work with above code replacement.
    Because docx file is formatted in XML format internally. XML format for this word can be as follows:

    <w:r>
        <w:rPr>
          <w:b />
          <w:color w:val="333333" />
          <w:sz w:val="32" />
          <w:szCs w:val="32" />
        </w:rPr>
        <w:t>E</w:t>
    </w:r>
    <w:r w:rsidRPr="00DA41AA">
        <w:rPr>
          <w:color w:val="333333" />
          <w:sz w:val="32" />
          <w:szCs w:val="32" />
        </w:rPr>
        <w:t>mployee</w:t>
    </w:r> 

    As you can see, it has <w:t>E</w:t> formatting for E and rest of the characters are in <w:t>mployee</w:t> XML element, hence our code won’t find Employee word.
    So it cannot be replaced.

  3. We have used Regx class so take care of those charters used in regular expression formatting.

This member has not yet provided a Biography. Assume it’s interesting and varied, and probably something to do with programming.

Выяснили, как использовать элементы управления содержимым для создания документов и как вставлять данные из XML в элементы управления содержимым. Я разделил это на 2 части:

  • Часть 1. Создайте свой шаблон документа для создания документа.
  • Часть 2: Используйте код на C# для создания документов на основе шаблона

Часть 1. Создайте свой шаблон документа для создания документа.

  1. Создайте образец XML, на основе которого вы можете создать шаблон Word для создания документа. Желательно начать с менее сложной версии, чтобы понять ее.

Я использовал следующий XML для тестирования. Для тестирования у меня не было повторяющихся разделов, картинок и т. д.

<?xml version = "1.0" encoding = "utf-8"?>
<mydata xmlns = "http://CustomDemoXML.htm">
    <field1>This is the value in field1 from the XML file</field1>
    <field2>This is the value in field2 from the XML file</field2>
    <field3>This is the value in field3 from the XML file</field3>
</mydata>

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

Заметка 2: Атрибут xmlns может содержать буквально все, что вы хотите, и это не обязательно должен быть URL, начинающийся с http.

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

  1. Убедитесь, что в вашей копии Word включена вкладка Developer [File -> Options -> Customize Ribbon -> В разделе Customize the Ribbon убедитесь, что выбрано Developer -> OK]. Подробности: Как: показать вкладку разработчика на ленте

  2. Создайте новый документ Word (или используйте существующий документ Word), который будет вашим шаблоном для создания документа.

  3. На вкладке Developer щелкните XML Mapping Pane. Это откроет XML Mapping Pane в правой части документа.

  4. На панели сопоставления XML выберите раскрывающийся список Custom XML Part -> Выбрать (Add new part).

  5. Выберите файл XML, который вы сохранили на шаге 1 -> Open.

  6. На панели сопоставления XML выберите раскрывающийся список Custom XML Part -> выберите элемент с текстом, который был в атрибуте xmlns настраиваемого файла XML. Если вы используете приведенный выше образец файла, это будет http://CustomDemoXML.htm.

  7. Добавьте статический текст в документ Word и добавьте рядом с ним Plain Text Content Control (на вкладке Developer -> раздел Controls. Повторите для всех полей, которые вам нужно добавить.

Для приведенного выше примера XML у меня был следующий документ Word:

  1. Щелкните первый Plain Text Content Control -> На панели сопоставления XML щелкните правой кнопкой мыши поле, которое вы хотите сопоставить с этим элементом управления содержимым -> Щелкните Map to Selected Content Control. Повторите эти действия для всех полей, которые хотите сопоставить.

Примечание: В качестве альтернативы, вместо добавления элементов Plain Text Content Control из вкладки разработчика на шаге 8, вы можете щелкнуть правой кнопкой мыши поле, которое вы хотите сопоставить, на панели сопоставления XML -> Щелкните Insert Content Control -> Щелкните Plain Text.

Точно так же вы также можете добавить другие типы элементов управления, такие как флажки, средства выбора даты и даже повторяющиеся разделы (он также поддерживает вложенные повторяющиеся разделы! — начиная с Word 2013) и сопоставить данные из XML с теми, которые используют только встроенные функции Word и без какой-либо третьей стороны. инструменты!

  1. Сохраните свой шаблон документа.

Часть 2: Используйте код на C# для создания документов на основе шаблона

При этом используется рекомендованный Microsoft OpenXML SDK для создания документов с использованием XML-файла, содержащего реальные данные.

  1. Создайте свой XML-файл / откройте существующий XML-файл, чтобы сгенерировать документ из шаблона, созданного выше. Он должен быть в том же формате, что и образец XML-файла, использованного для создания шаблона.

  2. Используйте OpenXML SDK для удаления любых элементов CustomXMLPart из документа. Это предполагает, что в документе не используются никакие другие настраиваемые части XML, как в этом примере. В сложных сценариях при необходимости можно удалить определенные части XML.

  3. Используйте OpenXML SDK, чтобы добавить новый CustomXMLPart на основе файла XML на шаге № 1 выше.

Вот пример кода, который я должен «обновить» / «перезагрузить» для образцов данных в шаблоне данными из XML-файла, содержащего реальные данные (при условии, что XML-файл, используемый для создания документа, уже создан и сохранен):

using System.IO;
using DocumentFormat.OpenXml.Packaging;

namespace SampleNamespace
{
    public static class SampleClass
    {
        public static void GenerateDocument()
        {
            string rootPath = @"C:Temp";
            string xmlDataFile = rootPath + @"MyNewData.xml";
            string templateDocument = rootPath + @"MyTemplate.docx";
            string outputDocument = rootPath + @"MyGeneratedDocument.docx";

            using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(templateDocument, true))
            {
                //get the main part of the document which contains CustomXMLParts
                MainDocumentPart mainPart = wordDoc.MainDocumentPart;

                //delete all CustomXMLParts in the document. If needed only specific CustomXMLParts can be deleted using the CustomXmlParts IEnumerable
                mainPart.DeleteParts<CustomXmlPart>(mainPart.CustomXmlParts);

                //add new CustomXMLPart with data from new XML file
                CustomXmlPart myXmlPart = mainPart.AddCustomXmlPart(CustomXmlPartType.CustomXml);
                using (FileStream stream = new FileStream(xmlDataFile, FileMode.Open))
                {
                    myXmlPart.FeedData(stream);
                }
            }

        }
    }
}

Это оно!

Время на прочтение
5 мин

Количество просмотров 1.4K

Привет, Хабр! Меня зовут Дарья Чувашова, я — руководитель группы отделения SAP-разработки. В процессе моей проектной деятельности мне приходилось сталкиваться с задачами выгрузки документов в .doc формат и делать это нужно было быстро. При этом эти документы могли быть с совершенно разным форматированием, кучей таблиц, реквизитов и т. д. В SAP для выгрузки в форматы pdf и excel есть удобные инструменты, возможность работать с формулярами и графическими редакторами форм. Для работы с форматом.doc инструментов меньше. В этой статье я расскажу о быстром и самом простом способе выгрузить документ любой сложности.

Почему я решила написать этот «how‑to»? Как я упомянула, задачи по выгрузке файлов в.doc мне приходилось выполнять часто. В какой‑то момент я собрала все лайфхаки и советы по ускорению работы в один материал, а сейчас хочу поделиться им с хабровской аудиторией. Надеюсь, для коллег записи будут полезными. Описанный вариант решения имеет свои особенности, поэтому я постараюсь на примерах продемонстрировать некоторые «узкие» моменты.

Пошаговая инструкция решения вопроса

Шаг 1

В первую очередь нам нужно подготовить шаблон в MS Word в нужном формате. Важно заполнить все реквизиты тестовыми данными для примера, это значительно упростит нам жизнь в последующих действиях.

В качестве примера рассмотрим вот такой документ «Счёт‑фактура» в MS Word:

Этот и другие используемые здесь примеры были взяты из открытого источника: https://glavkniga.ru/situations/k505106

Этот и другие используемые здесь примеры были взяты из открытого источника: https://glavkniga.ru/situations/k505106

Шаблон необходимо заполнить тестовыми примерами, чтобы проверить, что при заполнении ничего не съезжает, и все реквизиты остаются на месте:

Шаг 2

Сохраним наш документ в формате XML: Файл — Сохранить как. Выбираем расширение .xml

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

Для того чтобы открыть данный файл, мне удобно использовать программу Altova XML Spy. Скорее всего нам потребуется проанализировать содержимое, а в данной программе выполнять анализ файла очень удобно за счёт подсветки синтаксиса. Вы, конечно, можете использовать любой другой редактор.

Открываем свой XML, видим примерно такую картину:

После применения команды PrettyPrinter:

Шаг 3

Переходим в SAP. В своём пакете разработки создадим Преобразование:

Выберем трансформацию XSLT:

Видим следующую картину:

Для того, чтобы наша трансформация верно работала, необходимо указать следующий код между тегами <xsl:template match=»/»> </xsl:template>:

<xsl:processing-instruction name="mso-application" progid="Word.Document">
      <xsl:text progid="Word.Document"/>
  </xsl:processing-instruction>

Теперь можно смело вставить весь XML‑код ниже из нашего документа:

Визуально просматриваю данный XML‑код, обнаруживаю, что часть тегов подсвечивается, как текст:

Вижу, что это произошло из‑за кавычек в наименовании компании (Company), смело их удаляю:

Теперь пытаемся активировать трансформацию. В 90% случаях активация пройдёт успешно.

Но если у вас появятся подобные ошибки,

Предлагаю стереть данные коды, так как они не имеют никакого смысла для генерации документа из SAP.

Удаляем:

После удаления всех кодов трансформация успешно активируется.

Шаг 4

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

Данный код максимально облегчён для простоты восприятия и предельной наглядности.

После запуска программы в папке C:TEMP сохранится файл точно в таком же виде, как наш подготовленный шаблон:

При открытии файла может возникнуть следующая ошибка:

Для того, чтобы от неё избавиться, переходим в трансформацию и ищем /word/settings.xml

Избавиться от ошибки мне помогло удаление всего блока <pkg:part … </pkg:part>. Это не повлияло на работоспособность, и файл стал открываться нормально. Без подсветки синтаксиса тяжело искать закрывающий тег, поэтому имеет смысл снова воспользоваться программой Altova XML Spy (в данной программе вы можете удалить лишний код, а затем вставить новую версию в нашу трансформацию).

Удаляем и активируем, проверяем, что ошибка ушла и с файлом всё в порядке.

Шаг 5

Переходим к выгрузке данных из контекста. Начнём с самого простого: выгрузим данные в поле «Продавец»:

Контекст представляет собой структуру c данными, например, вот такую:

Её мы заполняем и подаём в трансформацию как контекст. Далее копируем из файла, заполненного в качестве примера, текст из реквизита «Продавец» и ищем это место в нашей XML:

Вместо данного текста вставляем:

Не забываем указать нужную структуру контекста и сделать выборку данных. Для примера прописываю хардкодом наименование продавца:

Результат трансформации:

Остальные реквизиты заполняем аналогично.

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

Отдельную сложность может представлять собой заполнение табличных данных. В структуре контекста имеем вложенную таблицу с данными T_INVOICE. Для вывода данных используем цикл for each. Начнём с 1 строки 1 столбца. Ищем поиском пример «Яблоки» и вставляем код, приведённый чуть ниже.

Теги описания таблицы довольно понятны: <w:tc> </w:tc> — стоблец, <w:tr </w:tr> — соответственно строка, ну и сам текст <w:t> </w:t>.

Если мы хотим каждую строку таблицы из контекста выводить в новую строку таблицы, то цикл ставим перед тегом строки и закрываем после окончания описания строки:

Конец цикла будет обозначен тут:

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

Результат:

Видим, что строка автоматически добавилась. Так как нам не нужны старые данные из примера, удалим эти строки из таблицы. Ищем так же по тегам.

В идеале можно было бы в самом шаблоне оставить лишь одну строку для заполнения, тогда лишних действий по удалению колонок не пришлось бы делать. Но я хочу показать неидеальный случай.

Если необходимо вывести данные из таблицы контекста не в каждой строке таблицы, а текстом с переносом, то можем воспользоваться тегом переноса строки <w:br/>, например,

Получим вот такой результат:

Ещё немного полезных рекомендаций

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

Что ещё записано в моих заметках?

Как поменять шрифт быстро, если он перестал подходить? Допустим, мы желаем заменить Arial на Calibri. Для этого в трансформации выполняем поиск Arial — «Заменить все» и вставляем название нового шрифта Calibri.

Как сделать защиту листа и позволить редактировать лишь некоторые реквизиты в выгруженном файле?

Для этого нужно в исходном файле на 1 шаге настроить защиту листа, тогда кодирующие эту операцию теги будут отражены в нашей трансформации.

Примеры исходного кода из статьи можно увидеть в репозитории github.

Данной информации должно быть достаточно, чтобы сделать выгрузку практически любого документа быстро и эффективно.

6262


 

Содержание:

  • Для чего написана эта статья.
  • Получение xml+xsd-файлов по
    данным из VFP-таблицы.
  • Создание и настройка шаблона в MS Word 2007,
    использующего xsd-схемы данных.
  • Построение XSLT-преобразования
    для заполнения шаблона MS Word 2007 документа данными из xml-файла.
  • C#-код приложения для
    заполнения шаблона MS Word 2007 документа данными
    из xml-файла.
  • VFP-код приложения для
    заполнения шаблона MS Word 2007 документа данными
    из xml-файла.
  • Построение XSLT-преобразования
    для извлечения xml-данных из таблицы в MS Word 2007 документе.
  • C#-код приложения для
    извлечения xml-данных из таблицы в MS Word 2007 документе.
  • VFP-код приложения для
    извлечения xml-данных из таблицы в MS Word 2007 документе.
  • XSLT-преобразование
    таблицы данных из xml-формата
    в html-представление.
  • Краткое описание кода примеров.
  • Ссылки по теме.

Для чего написана эта статья.

Часто возникает необходимость экспорта VFP-таблицы
данных в виде таблицы в MS Word. Это можно сделать
многими способами, однако в данной заметки нами будет рассмотрен только один: с
использованием возможностей, появившихся в MS Word
2007. Если вы имеете установленную у Вас серию продуктов из MS Office
2007, Вы наверное обратили внимание на тип файла «Документ
Word (*.docx)
«,
имеющийся в диалогах «Сохранить [как/Другие форматы]«,
расширение которого, как и структура содержащихся в нём данных, отличается от
прежнего doc-формата (документ
Word 97-2003). Этот типа принадлежит к т.н. типам файлов
Office 2007, известных как Office XML Open Format. См. также
статьи Алексея Федорова в разделе: Ссылки по теме.
Ниже приведена таблица новых форматов, поддерживаемых Word
из MS Office 2007 с краткими пояснениями к ним:

Формат Расширение Описание
Документ DOCX Стандартный формат файлов
Office Word 2007 на основе XML. Не сохраняет код VBA-макросов.
Документ с макросами DOCM Документ с включением макросов.
Шаблон документа DOTX Стандартный формат файлов
шаблонов Office Word 2007. Не сохраняет код VBA-макросов.
Шаблон (код) DOTM Формат файлов шаблонов
Office Word 2007, поддерживающий сохранение
макросов.

На самом деле, любой файл docx-формата представляет собой
zip-
файл, содержащий в себе несколько подкаталогов, в которых имеются ряд
файлов с данными в xml(eXtensible Markup Language)-формате. В этом нетрудно
убедиться, если заменить расширение docx-файла на
zip и попробовать разархивировать полученный после
переименования zip-файл с помощью какой-нибудь
утилиты, способной это сделать, например используя WinRAR.exe, а в старших версиях OS Windows это можно
также осуществить и с помощью обычного «Проводника» OS
Windows
.

Цель данной заметки заключается в том, чтобы на конкретном примере показать способ
преобразования xml-таблицы данных, в частности полученной
например из
dbf-файла, в docx-файл, используя
XSLT(eXtensible Stylesheet Language Transformations)-преобразования над xml-данными. Нами будет проделано
следующее:

  • во-первых, на основе данных из dbf-файла
    средствами VFP мы получим соответствующие
    xml и xsd файлы. 
  • во-вторых, подсоединив xsd-схему данных к
    MS Word-документу, мы подготовим таблицу-шаблон
    для заполнения его xml-данными.
  • в-третьих, опираясь на шаблон из пункта выше, мы модифицируем его так,
    чтобы получилось XSLT-преобразование для
    заполнения содержимого документа нашими исходными xml-данными
    из первого пункта, а для выполнения необходимых преобразований при этом, мы будем использовать
    XSLT-утилиты msxsl.exe или средств, входящих
    в MSXML 4.0 Service Pack 2 (Microsoft XML Core Services),
    которые можно свободно и бесплатно загрузить по ссылкам, приведённом мной в разделе:
    Ссылки по теме.
    В результате мы получим файл с xml-данными,
    который может быть использован как данные для MS Word 2007
    документа.
  • наконец, мы попробуем решить и обратную задачу: извлечь таблицу данных
    из MS Word 2007 документа в xml-формате,
    а полученные xml-данные преобразовать в
    VFP-курсор.

Получение xml+xsd-файлов по
данным из VFP-таблицы.

В MS Visual FoxPro начиная с версии 7.0 задача
преобразования данных из dbf-формата в
xml+xsd никакой трудности не представляет и достаточно
лишь воспользоваться функцией CursorToXml() правильно
указав ей параметры. Так, в результате выполнения вот такого кода:


CLEAR
CLOSE TABLES ALL
SET DEFAULT TO
(LEFT(SYS(16),
RATC(«»,
SYS(16))))

SELECT TOP
5 lastname, firstname,
birthdate, notes ;
  
FROM
(HOME(2) + ‘northwindemployees.dbf’)
ORDER BY lastname, firstname INTO CURSOR
employee

IF USED
(’employee’)
  
SET SAFETY OFF
  
?CURSORTOXML(’employee’,
’employee’, 1, 512+8+48, 0, ’employee’)
  
SET SAFETY ON
ELSE
  ERROR
101, ‘Not found file: ‘ +
LOWER(HOME(2) + ‘northwindemployees.dbf’)


ENDIF
CLOSE TABLES ALL
CLOSE DATABASES ALL

Получаем вот такой файл employee.XML:


<?
xml
version
=
«1.0«
encoding=«UTF-8«
standalone=«yes«?>
<
VFPData
xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance«
xsi:noNamespaceSchemaLocation=«employee.XSD«>
  <
employee>
    <
lastname>Buchanan</lastname>
    <
firstname>Steven</firstname>
    <
birthdate>1955-03-04</birthdate>
    <
notes><![CDATA[Steven
Buchanan graduated from St. Andrews University, Scotland, with a BSC degree.
Upon joining the company as a sales representative, he spent 6 months in an
orientation program at the Seattle office and then returned to his permanent
post in London, where he was promoted to sales manager. Mr. Buchanan has
completed the courses «Successful Telemarketing» and «International Sales
Management.» He is fluent in French.
]]></notes>
  </
employee>
  <
employee>
    <
lastname>Callahan</lastname>
    <
firstname>Laura</firstname>
    <
birthdate>1958-01-09</birthdate>
    <
notes><![CDATA[Laura
received a BA in psychology from the University of Washington. She has also
completed a course in business French. She reads and writes French.
]]></notes>
  </
employee>
  <
employee>
    <
lastname>Davolio</lastname>
    <
firstname>Nancy</firstname>
    <
birthdate>1968-12-08</birthdate>
    <
notes><![CDATA[Education
includes a BA in psychology from Colorado State University. She also completed
«The Art of the Cold Call.» Nancy is a member of Toastmasters International.
]]></notes>
  </
employee>
  <
employee>
    <
lastname>Dodsworth</lastname>
    <
firstname>Anne</firstname>
    <
birthdate>1969-07-02</birthdate>
    <
notes><![CDATA[Anne
has a BA degree in English from St. Lawrence College. She is fluent in French
and German.
]]></notes>
  </
employee>
  <
employee>
    <
lastname>Fuller</lastname>
    <
firstname>Andrew</firstname>
    <
birthdate>1952-02-19</birthdate>
    <
notes><![CDATA[Andrew
received his BTS commercial and a Ph.D. in international marketing from the
University of Dallas. He is fluent in French and Italian and reads German. He
joined the company as a sales representative, was promoted to sales manager and
was then named vice president of sales. Andrew is a member of the Sales
Management Roundtable, the Seattle Chamber of Commerce, and the Pacific Rim
Importers Association.
]]></notes>
  </
employee>
</
VFPData>

а также файл employee.XSD:


<?
xml
version
=
«1.0»
encoding=»UTF-8″
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=»employee»
minOccurs=»0″
maxOccurs=»unbounded»>
          <
xsd:complexType>
            <
xsd:sequence>
              <
xsd:element
name=»lastname»>
                <
xsd:simpleType>
                  <
xsd:restriction
base=»xsd:string»>
                    <
xsd:maxLength
value=»20″/>
                  </
xsd:restriction>
                </
xsd:simpleType>
              </
xsd:element>
              <
xsd:element
name=»firstname»>
                <
xsd:simpleType>
                  <
xsd:restriction
base=»xsd:string»>
                    <
xsd:maxLength
value=»10″/>
                  </
xsd:restriction>
                </
xsd:simpleType>
              </
xsd:element>
              <
xsd:element
name=»birthdate»
type=»xsd:date»
minOccurs=»0″/>
              <
xsd:element
name=»notes»
minOccurs=»0″>
                <
xsd:simpleType>
                  <
xsd:restriction
base=»xsd:string»>
                    <
xsd:maxLength
value=»2147483647″/>
                  </
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>

содержащий схему для него. В полученную схему следует внести небольшие
изменения с тем, чтобы мы имели возможность делать вставку произвольного текста
в Word документ и этот текст не противоречил бы нашей
схеме. Для этого добавьте атрибут mixed со значением
true для первых двух элементов <xsl:complexType> так как показано/выделено в фрагменте файла
employee.XSD ниже:


<?
xml
version
=
«1.0»
encoding=»UTF-8″
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
mixed=»true»>
      <
xsd:choice
mxOccurs=»unbounded»>
        <
xsd:element
nme=»employee»
minOccurs=»0″
maxOccurs=»unbounded»>
          <
xsd:complexType
mixed=»true»>
            <
xsd:sequence>
              <
xsd:element
name=»lastname»>
              …

Создание и настройка шаблона в MS Word 2007,
использующего xsd-схемы данных.

Далее, создадим на основе данных этих двух файлов таблицу в
MS Word из MS Office 2007.
Ниже шаг за шагом и т.с. в картинках показано, как это можно сделать. Но прежде
всего, если у Вас на «Панели
быстрого доступа» отсутствует вкладка «Разработчик», то проделайте действия,
изображённые на Рис.1:

xlsx1.jpg
Рис.1

т.е. отметьте пункт «Показывать вкладку «Разработчик» на ленте» в диалоге
«Параметры Excel» и подтвердите изменения, нажав
кнопку «Ok» в правой нижней части окна этого диалога.

Следующим действием загрузим файл-схему employee.XSD
в MS Word 2007, для чего открыв новый
Word-документ, выполним следующее:

docx2.jpg
Рис.2

В возникшем при этом диалоге «Параметры схемы», определите поля:
URI и Псевдоним так, как показано на картинке ниже и подтвердите установку
схемы.

docx3.jpg
Рис.3

После удачной установки схемы в правой части окна у Вас должна появиться
панель «Структура XML» подобная также изображенная на картинке
ниже. Введите текст в качестве заголовка всего документа, выделите его весь мышкой и
примените к нему всю схему, ткнув мышкой в нижней части панели «Структура
XML» в текст
«VFPData {Schema Employee from VFP}» подобно тому, как это сделано на
картинке ниже:

docx4.jpg
Рис.4

В возникшем при этом диалоге «Применить ко всему документу?» подтвердите
выбором кнопки «Применить ко всему документу». После чего у Вас должно
получиться нечто похожее представленному на рисунке ниже:

docx5.jpg
Рис.5

Перемесив позицию курсора к началу конечного элемента
VFPData,
нажмите мышкой на элемент employee в
нижней части панели «Структура XML«. Поле чего Вы
должны получить то, что показано на рисунке ниже:

docx6.jpg
Рис.6

Последовательно выбирая мышкой подчинённые элементы:
lastname, firstname, birthdate, notes
у элемента
employee,
в нижней части панели «Структура XML»
и после ввода очередного элемента, перемещая текущую позицию в документе к
началу конечного элемента employee, Вы должны получить
подобное тому, что изображено на картинке ниже:

docx7.jpg
Рис.7

Следующим шагом, мы должны создать таблицу таким образом, чтобы элемент
employee с вложенными в него элементами разместился бы
в первой строке создаваемой нами таблицы, а каждый из вложенных элементом попал
бы в отведённый ему столбец таблицы. Строку заголовков столбцов таблицы при этом,
следует поместить сразу после заголовка документа, но перед элементом
employee. Проделав это, Вы должны получить подобное
тому, что показано на картинке ниже:

docx8.jpg
Рис.8

К этому же результату можно придти, если сначала создать таблицу с
заголовками столбцов и одной пустой строкой в качестве её данных, а затем,
выделив всю строку данных в таблице, связать с ней групповой элемент
employee. Далее, последовательно смещаясь по столбцам,
связать с каждым из них элементы, принадлежащие групповому элементу
employee: lastname, firstname, birthdate
и birthdate.

Чтобы придать полученной таблице презентабельный вид, можно воспользоваться
одним из стилей таблиц, например таким:

docx9.jpg
Рис.9

Наконец, полученный результат сохраняем как MS Word 2007
документ:

docx10.jpg
Рис.10

Следующим шагом нам нужно извлечь xml-данные в
формате Open XML у полученного документа. Это можно
сделать

  • либо сделав копию полученного документа с расширением .zip и далее воспользоваться возможностями одной из утилит работы с
    zip-архивами данных,
  • либо, если у Вас имеется
    установленный WinRAR.exe, то создать командный файл
    getContentsUseWinRAR.cmd
    ,
    подобный следующему:
@echo off
mode con: cp select=1251 > _tmp.txt
"C:Program FilesWinRARWinRAR.exe" X -ad -ibck -o+ "Пример VFP-таблицы в виде MS Word 2007.docx"
del _tmp.txt

Файл: getContentsUseWinRAR.cmd

и выполнить его из того каталога, куда Вы только что сохранили файл «Пример
VFP-таблицы в виде MS Word 2007.docx». В данном случае, нас будет интересовать только файл
document.xml из
подкаталога word:

docx11.jpg
Рис.11

Вот каким он получился у меня:


<?
xml
version=«1.0«
encoding=«UTF-8«
standalone=«yes«?>
<
w:document
xmlns:ve=«http://schemas.openxmlformats.org/markup-compatibility/2006«

    xmlns:o=«urn:schemas-microsoft-com:office:office«

    
xmlns:r=«http://schemas.openxmlformats.org/officeDocument/2006/relationships«
    
xmlns:m=«http://schemas.openxmlformats.org/officeDocument/2006/math«

    
xmlns:v=«urn:schemas-microsoft-com:vml«

    
xmlns:wp=«http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing«
    
xmlns:w10=«urn:schemas-microsoft-com:office:word«

    
xmlns:w=«http://schemas.openxmlformats.org/wordprocessingml/2006/main«

    
xmlns:wne=«http://schemas.microsoft.com/office/word/2006/wordml«>
  <
w:body>
    <
w:customXml

w:uri=«Schema_Employee«

w:element=«VFPData«>
      <
w:p
w:rsidR=«00883633«
w:rsidRPr=«00AF60CD«
w:rsidRDefault=«00883633«
w:rsidP=«00AF60CD«>
        <
w:pPr>
          <
w:jc
w:val=«center«/>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
          </
w:rPr>
        </
w:pPr>
        <
w:r
w:rsidRPr=«00883633«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
          </
w:rPr>
          <
w:t>Пример</w:t>
        </
w:r>
        <
w:r
w:rsidR=«00AF60CD«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
          </
w:rPr>
          <
w:t
xml:space=«preserve«>
</
w:t>
        </
w:r>
        <
w:r
w:rsidR=«00AF60CD«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
            <
w:lang
w:val=«en-US«/>
          </
w:rPr>
          <
w:t>VFP</w:t>
        </
w:r>
        <
w:r
w:rsidR=«00AF60CD«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
          </
w:rPr>
          <
w:t></w:t>
        </
w:r>
        <
w:r
w:rsidRPr=«00883633«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
          </
w:rPr>
          <
w:t
xml:space=«preserve«>таблицы в виде таблицы в </w:t>
        </
w:r>
        <
w:r
w:rsidR=«00AF60CD«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
            <
w:lang
w:val=«en-US«/>
          </
w:rPr>
          <
w:t>MS</w:t>
        </
w:r>
        <
w:r
w:rsidR=«00AF60CD«
w:rsidRPr=«00AF60CD«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
          </
w:rPr>
          <
w:t
xml:space=«preserve«>
</
w:t>
        </
w:r>
        <
w:r
w:rsidR=«00AF60CD«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
            <
w:lang
w:val=«en-US«/>
          </
w:rPr>
          <
w:t>Word</w:t>
        </
w:r>
        <
w:r
w:rsidR=«00AF60CD«
w:rsidRPr=«00AF60CD«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
          </
w:rPr>
          <
w:t
xml:space=«preserve«>
2007
</w:t>
        </
w:r>
        <
w:r
w:rsidR=«00AF60CD«>
          <
w:rPr>
            <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«/>
          </
w:rPr>
          <
w:t>.</w:t>
        </
w:r>
      </
w:p>
      <
w:tbl>
        <
w:tblPr>
          <
w:tblStyle
w:val=«1-1«/>
          <
w:tblW
w:w=«0«
w:type=«auto«/>
          <
w:tblLook
w:val=«04A0«/>
        </
w:tblPr>
        <
w:tblGrid>
          <
w:gridCol
w:w=«2392«/>
          <
w:gridCol
w:w=«2393«/>
          <
w:gridCol
w:w=«2393«/>
          <
w:gridCol
w:w=«2393«/>
        </
w:tblGrid>
        <
w:tr
w:rsidR=«00174F3F«
w:rsidTr=«00174F3F«>
          <
w:trPr>
            <
w:cnfStyle
w:val=«100000000000«/>
          </
w:trPr>
          <
w:tc>
            <
w:tcPr>
              <
w:cnfStyle
w:val=«001000000000«/>
              <
w:tcW
w:w=«2392«
w:type=«dxa«/>
            </
w:tcPr>
            <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«007F26DB«
w:rsidRDefault=«007F26DB«
w:rsidP=«00174F3F«>
              <
w:r>
                <
w:t>Фамилия</w:t>
              </
w:r>
            </
w:p>
          </
w:tc>
          <
w:tc>
            <
w:tcPr>
              <
w:tcW
w:w=«2393«
w:type=«dxa«/>
            </
w:tcPr>
            <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
              <
w:pPr>
                <
w:cnfStyle
w:val=«100000000000«/>
              </
w:pPr>
              <
w:r>
                <
w:t>Имя</w:t>
              </
w:r>
            </
w:p>
          </
w:tc>
          <
w:tc>
            <
w:tcPr>
              <
w:tcW
w:w=«2393«
w:type=«dxa«/>
            </
w:tcPr>
            <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
              <
w:pPr>
                <
w:cnfStyle
w:val=«100000000000«/>
              </
w:pPr>
              <
w:r>
                <
w:t>День рождения</w:t>
              </
w:r>
            </
w:p>
          </
w:tc>
          <
w:tc>
            <
w:tcPr>
              <
w:tcW
w:w=«2393«
w:type=«dxa«/>
            </
w:tcPr>
            <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
              <
w:pPr>
                <
w:cnfStyle
w:val=«100000000000«/>
              </
w:pPr>
              <
w:r>
                <
w:t>Комментарий</w:t>
              </
w:r>
            </
w:p>
          </
w:tc>
        </
w:tr>
        <
w:customXml

w:element=«employee«>
          <
w:tr
w:rsidR=«00174F3F«
w:rsidTr=«00174F3F«>
            <
w:trPr>
              <
w:cnfStyle
w:val=«000000100000«/>
            </
w:trPr>
            <
w:customXml

w:element=«lastname«>
              <
w:tc>
                <
w:tcPr>
                  <
w:cnfStyle
w:val=«001000000000«/>
                  <
w:tcW
w:w=«2392«
w:type=«dxa«/>
                </
w:tcPr>
                <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                  <
w:pPr>
                    <
w:rPr>
                      <
w:lang
w:val=«en-US«/>
                    </
w:rPr>
                  </
w:pPr>
                </
w:p>
              </
w:tc>
            </
w:customXml>
            <
w:customXml

w:element=«firstname«>
              <
w:tc>
                <
w:tcPr>
                  <
w:tcW
w:w=«2393«
w:type=«dxa«/>
                </
w:tcPr>
                <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                  <
w:pPr>
                    <
w:cnfStyle
w:val=«000000100000«/>
                    <
w:rPr>
                      <
w:lang
w:val=«en-US«/>
                    </
w:rPr>
                  </
w:pPr>
                </
w:p>
              </
w:tc>
            </
w:customXml>
            <
w:customXml

w:element=«birthdate«>
              <
w:tc>
                <
w:tcPr>
                  <
w:tcW
w:w=«2393«
w:type=«dxa«/>
                </
w:tcPr>
                <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                  <
w:pPr>
                    <
w:cnfStyle
w:val=«000000100000«/>
                    <
w:rPr>
                      <
w:lang
w:val=«en-US«/>
                    </
w:rPr>
                  </
w:pPr>
                </
w:p>
              </
w:tc>
            </
w:customXml>
            <
w:customXml

w:element=«notes«>
              <
w:tc>
                <
w:tcPr>
                  <
w:tcW
w:w=«2393«
w:type=«dxa«/>
                </
w:tcPr>
                <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                  <
w:pPr>
                    <
w:cnfStyle
w:val=«000000100000«/>
                    <
w:rPr>
                      <
w:lang
w:val=«en-US«/>
                    </
w:rPr>
                  </
w:pPr>
                </
w:p>
              </
w:tc>
            </
w:customXml>
          </
w:tr>
        </
w:customXml>
      </
w:tbl>
      <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«006C46B9«
w:rsidP=«00174F3F«>
        <
w:pPr>
          <
w:rPr>
            <
w:lang
w:val=«en-US«/>
          </
w:rPr>
        </
w:pPr>
      </
w:p>
    </
w:customXml>
    <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«00883633«
w:rsidRDefault=«00174F3F«/>
    <
w:sectPr
w:rsidR=«00174F3F«
w:rsidRPr=«00883633«
w:rsidSect=«00EC3577«>
      <
w:pgSz
w:w=«11906«
w:h=«16838«/>
      <
w:pgMar
w:top=«1134«
w:right=«850«
w:bottom=«1134«
w:left=«1701«
w:header=«708«
w:footer=«708«
w:gutter=«0«/>
      <
w:cols
w:space=«708«/>
      <
w:docGrid
w:linePitch=«360«/>
    </
w:sectPr>
  </
w:body>
</
w:document>

Файл: document.xml

Здесь мной выделены начальные/конечные теги элементов
w:customXml,
относящиеся к соответствующим элементам XSD-схемы.
Обратите внимание, что содержимое MS Word-документа,
отвечающее определённому элементу схемы, располагается внутри соответствующего
w:customXml элемента. Так тег параграфа, включающего в
себя заголовок для всего документа, следует сразу за начальным элементом
<w:customXml w:uri=»Schema_Employee» w:element=»VFPData»>,
отвечающим корневому элементу XSD-схемы. Строка данных
таблицы, расположенная в элементе <w:tr>…</w:tr>,
следует сразу за групповым элементом <w:customXml
w:element=»employee»>
, наконец любой столбец таблицы, содержащий данные
таблицы (т.е. любой элемент <w:tc>…</w:tc>),
располагается внутри соответствующего w:customXml-элемента,
отвечающего соответствующему элементу данных в XSD-схеме.

Экспериментируя с MS Word-документом при
подготовке шаблона документа, созданного на основе XSD-схемы,
я обнаружил, что это правило выполняется не всегда, а возможны случаи, когда
w:customXml-элемент оказывается «пустым» и в таких
случаях, он располагается в том месте MS Word-документа,
где должен бы быть расположен текст непосредственно, точнее элемент
<w:r>…<w:t>…</w:t></w:r>, как в фрагменте
xml-документа, приведённого ниже:

    …

        <
w:customXml

w:element=«employee«>
          <
w:tr
w:rsidR=«00883633«
w:rsidRPr=«00883633«
w:rsidTr=«00883633«>
            <
w:trPr>
              <
w:cnfStyle
w:val=«000000100000«
/>
            </
w:trPr>
            <
w:tc>
              <
w:tcPr>
                <
w:cnfStyle
w:val=«001000000000«
/>
                <
w:tcW
w:w=«2392«
w:type=«dxa«
/>
              </
w:tcPr>
              <
w:p
w:rsidR=«00883633«
w:rsidRPr=«00883633«
w:rsidRDefault=«00883633«
w:rsidP=«00883633«>
                <
w:customXml

w:element=«lastname«
/>

              </
w:p>
            </
w:tc>

    …

Здесь w:customXml-элемент, отвечающий полю данных
lastname, расположен не снаружи элемента
<w:tc>…</w:tc>, а внутри его, точнее внутри его
элемента <w:p>.

Выше шаблон документа (worddocument.htm)
был получен путём прямого редактирования из среды MS
Word
2007. Однако, можно написать XSLT-преобразование,
способное для заданной xsd-схемы динамически создать
подобный шаблон. У меня это получилось так:


<?
xml
version=«1.0«
encoding=«UTF-8«
standalone=«yes«?>
<!—
file: genXsltTableTemplate.xslt
—>
<
xsl:stylesheet
version=«1.0«
    
xmlns:xsl=«http://www.w3.org/1999/XSL/Transform«
    
xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance«
    
xmlns:msxsl=«urn:schemas-microsoft-com:xslt«
    
xmlns:xsd=«http://www.w3.org/2001/XMLSchema«
    
xmlns:msdata=«urn:schemas-microsoft-com:xml-msdata«
    
xmlns:ve=«http://schemas.openxmlformats.org/markup-compatibility/2006«
    
xmlns:o=«urn:schemas-microsoft-com:office:office«
    
xmlns:r=«http://schemas.openxmlformats.org/officeDocument/2006/relationships«
    
xmlns:m=«http://schemas.openxmlformats.org/officeDocument/2006/math«
    
xmlns:v=«urn:schemas-microsoft-com:vml«
    
xmlns:wp=«http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing«
    
xmlns:w10=«urn:schemas-microsoft-com:office:word«
    
xmlns:w=«http://schemas.openxmlformats.org/wordprocessingml/2006/main«
    
xmlns:wne=«http://schemas.microsoft.com/office/word/2006/wordml«
    
exclude-result-prefixes=«xsi
msxsl xsd msdata
«>

  <

xsl:output
version=«1.0«
method=«xml«
    
encoding=«utf-8«
standalone=«yes«
/>

  <

xsl:param
name=«prmSchemaUri«>
    <
xsl:text>Schema_Employee</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmSchemaElement«>
    <
xsl:text>VFPData</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmTblStyle«>
    <
xsl:text>1-1</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmTblType«>
    <
xsl:text>auto</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmTblLook«>
    <
xsl:text>04A0</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmTblColTwipsMeasure«>
    <
xsl:text>2393</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmTblRsid«>
    <
xsl:text>00174F3F</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmTblRStyle«>
    <
xsl:text>100000000000</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmTblCStyle«>
    <
xsl:text>001000000000</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmTblCType«>
    <
xsl:text>dxa</xsl:text>
  </
xsl:param>
  <
xsl:param
name=«prmTblColumns«>
    <
tblColumns>
      <
tbl
SchemaUri=«Schema_Employee«>
        <
tblCol>
          <
header>Фамилия</header>
          <
width>2391</width>
          <
lang>en-US</lang>
        </
tblCol>
        <
tblCol>
          <
header>Имя</header>
          <
width>2392</width>
          <
lang>en-US</lang>
        </
tblCol>
        <
tblCol>
          <
header>День рождения</header>
          <
width>2393</width>
          <
lang>ru-RU</lang>
        </
tblCol>
        <
tblCol>
          <
header>Комментарий</header>
          <
width>2394</width>
          <
lang>en-US</lang>
        </
tblCol>
      </
tbl>
      <!—

—>
    </
tblColumns>
  </
xsl:param>

  <

xsl:template
match=«/«>
    <
w:document
xmlns:ve=«http://schemas.openxmlformats.org/markup-compatibility/2006«
        
xmlns:o=«urn:schemas-microsoft-com:office:office«
        
xmlns:r=«http://schemas.openxmlformats.org/officeDocument/2006/relationships«
        
xmlns:m=«http://schemas.openxmlformats.org/officeDocument/2006/math«
        
xmlns:v=«urn:schemas-microsoft-com:vml«
        
xmlns:wp=«http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing«
        
xmlns:w10=«urn:schemas-microsoft-com:office:word«
        
xmlns:w=«http://schemas.openxmlformats.org/wordprocessingml/2006/main«
        
xmlns:wne=«http://schemas.microsoft.com/office/word/2006/wordml«>
      <
w:body>
        <
xsl:call-template
name=«genTable«>
          <
xsl:with-param
name=«prmSchemaUri«
select=«$prmSchemaUri«
/>
          <
xsl:with-param
name=«prmSchemaElement«
select=«$prmSchemaElement«
/>
          <
xsl:with-param
name=«prmTblStyle«
select=«$prmTblStyle«
/>
          <
xsl:with-param
name=«prmTblType«
select=«$prmTblType«
/>
          <
xsl:with-param
name=«prmTblLook«
select=«$prmTblLook«
/>
          <
xsl:with-param
name=«prmTblRStyle«
select=«$prmTblRStyle«
/>
          <
xsl:with-param
name=«prmTblCStyle«
select=«$prmTblCStyle«
/>
          <
xsl:with-param
name=«prmTblCType«
select=«$prmTblCType«
/>
        </
xsl:call-template>
        <
w:sectPr
w:rsidR=«00174F3F«
w:rsidRPr=«00883633«
w:rsidSect=«00EC3577«>
          <
w:pgSz
w:w=«11906«
w:h=«16838«/>
          <
w:pgMar
w:top=«1134«
w:right=«850«
w:bottom=«1134«
w:left=«1701«
w:header=«708«
w:footer=«708«
w:gutter=«0«/>
          <
w:cols
w:space=«708«/>
          <
w:docGrid
w:linePitch=«360«/>
        </
w:sectPr>
      </
w:body>
    </
w:document>
  </
xsl:template>

  <

xsl:template
name=«genTable«>
    <
xsl:param
name=«prmSchemaUri«
/>
    <
xsl:param
name=«prmSchemaElement«
/>
    <
xsl:param
name=«prmTblStyle«
/>
    <
xsl:param
name=«prmTblType«
/>
    <
xsl:param
name=«prmTblLook«
/>
    <
xsl:param
name=«prmTblRStyle«
/>
    <
xsl:param
name=«prmTblCStyle«
/>
    <
xsl:param
name=«prmTblCType«
/>
    <
w:customXml
w:uri=«{$prmSchemaUri}«
w:element=«{$prmSchemaElement}«>
      <
w:p
w:rsidR=«{$prmTblRsid}«
w:rsidRDefault=«{$prmTblRsid}«/>
      <
w:tbl>
        <
w:tblPr>
          <
w:tblStyle
w:val=«{$prmTblStyle}«/>
          <
w:tblW
w:w=«0«
w:type=«{$prmTblType}«/>
          <
w:tblLook
w:val=«{$prmTblLook}«/>
        </
w:tblPr>
        <
w:tblGrid>
          <!—
Generate set of <w:gridCol …>
elements
—>
          <
xsl:for-each
select=«/.//xsd:schema/xsd:element[@name=$prmSchemaElement]//xsd:sequence/*«>
            <
xsl:variable
name=«curPos«>
              <
xsl:value-of
select=«position()«/>
            </
xsl:variable>
            <
xsl:variable
name=«curTblCol«>
              <
xsl:copy-of
select=«msxsl:node-set($prmTblColumns)/./tblColumns/tbl[@SchemaUri=$prmSchemaUri]/tblCol[number($curPos)]«/>
            </
xsl:variable>
            <!—
<w:gridCol w:w=»{$prmTblColTwipsMeasure}»/>
—>
            <
w:gridCol
w:w=«{msxsl:node-set($curTblCol)/./tblCol/width}«/>
          </
xsl:for-each>
        </
w:tblGrid>
        <
w:tr
w:rsidR=«{$prmTblRsid}«
w:rsidTr=«{$prmTblRsid}«>
          <
w:trPr>
            <
w:cnfStyle
w:val=«{$prmTblRStyle}«/>
          </
w:trPr>
          <!—
Generate set of <w:tc>…<w:tc>
elements as columns header
—>
          <
xsl:for-each
select=«/.//xsd:schema/xsd:element[@name=$prmSchemaElement]//xsd:sequence/*«>
            <
xsl:variable
name=«curPos«>
              <
xsl:value-of
select=«position()«
/>
            </
xsl:variable>
            <
xsl:variable
name=«curTblCol«>
              <
xsl:copy-of
select=«msxsl:node-set($prmTblColumns)/./tblColumns/tbl[@SchemaUri=$prmSchemaUri]/tblCol[number($curPos)]«
/>
            </
xsl:variable>
            <
xsl:variable
name=«curTblColWidth«>
              <
xsl:value-of
select=«msxsl:node-set($curTblCol)/./tblCol/width«
/>
            </
xsl:variable>
            <
w:tc>
              <
w:tcPr>
                <!—
<w:tcW w:w=»{$prmTblColTwipsMeasure}»
w:type=»{$prmTblCType}»/>
—>
                <
w:tcW
w:w=«{$curTblColWidth}«
w:type=«{$prmTblCType}«/>
              </
w:tcPr>
              <
w:p
w:rsidR=«{$prmTblRsid}«
w:rsidRPr=«{$prmTblRsid}«
w:rsidRDefault=«{$prmTblRsid}«
w:rsidP=«{$prmTblRsid}«>
                <
w:pPr>
                  <
w:cnfStyle
w:val=«{$prmTblCStyle}«/>
                </
w:pPr>
                <
w:r>
                  <
w:t>
                    <!—
<xsl:value-of select=»@name»/>
—>
                    <
xsl:value-of
select=«msxsl:node-set($curTblCol)/./tblCol/header«/>
                  </
w:t>
                </
w:r>
              </
w:p>
            </
w:tc>
          </
xsl:for-each>
        </
w:tr>
        <!—
Generate group element
<w:customXml…>…</w:customXml>
—>
        <
w:customXml
w:element=«{/.//xsd:schema/xsd:element[@name=$prmSchemaElement]//xsd:element/@name}«>
          <
w:tr
w:rsidR=«{$prmTblRsid}«
w:rsidTr=«{$prmTblRsid}«>
            <
w:trPr>
              <
w:cnfStyle
w:val=«{$prmTblRStyle}«/>
            </
w:trPr>
            <!—
Generate set of
<w:customXml…><w:tc>…<w:tc></w:customXml> elements for columns data

—>
            <
xsl:for-each
select=«/.//xsd:schema/xsd:element[@name=$prmSchemaElement]//xsd:sequence/*«>
              <
xsl:variable
name=«curPos«>
                <
xsl:value-of
select=«position()«/>
              </
xsl:variable>
              <
xsl:variable
name=«curTblCol«>
                <
xsl:copy-of
select=«msxsl:node-set($prmTblColumns)/./tblColumns/tbl[@SchemaUri=$prmSchemaUri]/tblCol[number($curPos)]«/>
              </
xsl:variable>
              <
xsl:variable
name=«curTblColWidth«>
                <
xsl:value-of
select=«msxsl:node-set($curTblCol)/./tblCol/width«
/>
              </
xsl:variable>
              <
w:customXml
w:element=«{@name}«>
                <
w:tc>
                  <
w:tcPr>
                    <!—
<w:tcW
w:w=»{$prmTblColTwipsMeasure}» w:type=»{$prmTblCType}»/>

—>
                    <
w:tcW
w:w=«{$curTblColWidth}«
w:type=«{$prmTblCType}«/>
                  </
w:tcPr>
                  <
w:p
w:rsidR=«{$prmTblRsid}«
w:rsidRDefault=«{$prmTblRsid}«
w:rsidP=«{$prmTblRsid}«>
                    <
w:pPr>
                      <
w:cnfStyle
w:val=«{$prmTblCStyle}«/>
                      <
w:rPr>
                        <
w:lang
w:val=«{msxsl:node-set($curTblCol)/./tblCol/lang}«/>
                      </
w:rPr>
                    </
w:pPr>
                  </
w:p>
                </
w:tc>
              </
w:customXml>
            </
xsl:for-each>
          </
w:tr>
        </
w:customXml>
      </
w:tbl>
      <
w:p
w:rsidR=«{$prmTblRsid}«
w:rsidRDefault=«{$prmTblRsid}«/>
    </
w:customXml>
  </
xsl:template>

</

xsl:stylesheet>

Файл: genXsltTableTemplate.xslt

Здесь параметр prmTblColumns для каждой схемы
содержит список дополнительных параметров для колонок таблиц, Вы можете
расширить его по своему усмотрению добавив соответствующий код в шаблон
genTable. Применив этот шаблон к
xsd-
схеме employee.xsd, используя командный
файл:

msxsl.exe employee.XSD ../XSLT/genXsltTableTemplate.xslt -o documentTableTemplate.xml -u '4.0'

Файл: documentTableTemplate.cmd

мы получим файл documentTableTemplate.xml,
представляющий из себя заготовку для MS Word 2007 шаблона таблицы, схема к которой была указана в качестве первого параметра у утилиты
msxsl.exe
.

Далее нам потребуется возможность из папки, содержащей в себе подпапки с
файлами, создавать zip-архив (точнее
docx-файл). Если у Вас установлен
WinRAR.exe, то это можно сделать, поместив вот такую
последовательности DOS-команд:

@echo off
mode con: cp select=1251 > _tmp.txt
"C:Program FilesWinRARWinRAR.exe" a -r -ibck -o+ -y -ep1 "Пример VFP-таблицы в виде MS Word 2007.zip" "Пример VFP-таблицы в виде MS Word 2007*.*"
if not exist "Пример VFP-таблицы в виде MS Word 2007.docx" goto cont
del "Пример VFP-таблицы в виде MS Word 2007.docx"
:cont 
rename "Пример VFP-таблицы в виде MS Word 2007.zip" "Пример VFP-таблицы в виде MS Word 2007.docx"
del _tmp.txt

Файл: fromContentsToDocxUseWinRAR.cmd

в командный файл fromContentsToDocxUseWinRAR.cmd
в подкаталог ../DOCX/.

Теперь переименовав полученный после выполнения
documentTableTemplate.cmd
файл documentTableTemplate.xml
на document.xml (в подкаталоге
XML), подменив им
файл worddocument.xml (в подкаталоге
..DOCXПример VFP-таблицы в виде MS Word 2007),
получив соответствующий docx-файл (выполнением
fromContentsToDocxUseWinRAR.cmd) и открыв его в MS Word
2007, я наблюдаю следующую картинку:

docx20.jpg

Как видим, здесь всё в полом соответствии с нашими желаниями. Таким образом,
если у нас имеется несколько таблиц с разными xsd-схемами,
то мы вполне можем создавать docx-шаблон, содержащий
их программно, используя шаблон genTable из файла
genXsltTableTemplate.xslt. Только нужно иметь ввиду, что

  • соответствующие xsd-схемы
    должны быть предварительно зарегистрированы в библиотеке схем (см. под ключом
    HKEY_CURRENT_USERSoftwareMicrosoftSchema Library.. системного реестра).
  • информация об используемых в документе xsd-схемах
    в части документа wordsettings.xml в рамках содержащихся там элементов,
    подобных: <w:attachedSchema w:val=»Schema_Employee»/>, должна быть
    корректной. Т.е. с помощью подобных элементов должны быть перечислены все
    xsd-схемы, используемые в документе.

Построение XSLT-преобразования
для заполнения шаблона MS Word 2007 документа данными из xml-файла.

Сколько-то разобравшись с содержимым файла document.xml,
каковы же будут наши дальнейшие действия? Наша цель на данном этапе: это на
основе приведённого выше document.xml, создать файл
xslt-преобразования, которое получив бы на входе
данные из файла employee.xml, возвратило бы в качестве
результата преобразований xml-документ, подобный файлу
document.xml, но уже не с пустой строкой в данных у
таблицы, а с заполненными всеми строками данных таблицы, взяв их из файла
employee.xml. Это можно сделать

  • либо прямым редактирование файла docement.xml,
  • либо создать дополнительное xslt-преобразование,
    позволяющее преобразовать файл, подобный файлу
    document.xml
    , в файл соответствующего ему xslt-преобразования.

Такая работа мной проделана и ниже xslt-файл (getXmlDocument.xslt),
представляющий результат этого дополнительного xslt-преобразования:


<?
xml
version=«1.0«
encoding=«utf-8«
standalone=«yes«?>
<
xsl:stylesheet

version=«1.0«

xmlns:xsl=«http://www.w3.org/1999/XSL/Transform«
  
  xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance«
    xmlns:msxsl=«urn:schemas-microsoft-com:xslt«


    
xmlns:msdata
=«urn:schemas-microsoft-com:xml-msdata«
  
  xmlns:ve=«http://schemas.openxmlformats.org/markup-compatibility/2006«
    xmlns:o=«urn:schemas-microsoft-com:office:office«
  
  xmlns:r=«http://schemas.openxmlformats.org/officeDocument/2006/relationships«
  
  xmlns:m=«http://schemas.openxmlformats.org/officeDocument/2006/math«
  
  xmlns:v=«urn:schemas-microsoft-com:vml«
  
  xmlns:wp=«http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing«
  
  xmlns:w10=«urn:schemas-microsoft-com:office:word«
  
  xmlns:w=«http://schemas.openxmlformats.org/wordprocessingml/2006/main«
  
  xmlns:wne=«http://schemas.microsoft.com/office/word/2006/wordml«
  
  exclude-result-prefixes=«xsi
msxsl msdata
«>

  <

xsl:output

version=«1.0«

method=«xml«



    
encoding
=«utf-8«

standalone=«yes«
/>

  <

xsl:template

match=«/«>
    <
w:document
xmlns:ve=«http://schemas.openxmlformats.org/markup-compatibility/2006«
        
xmlns:o=«urn:schemas-microsoft-com:office:office«
        
xmlns:r=«http://schemas.openxmlformats.org/officeDocument/2006/relationships«
        
xmlns:m=«http://schemas.openxmlformats.org/officeDocument/2006/math«
        
xmlns:v=«urn:schemas-microsoft-com:vml«
        
xmlns:wp=«http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing«
        
xmlns:w10=«urn:schemas-microsoft-com:office:word«
        
xmlns:w=«http://schemas.openxmlformats.org/wordprocessingml/2006/main«
        
xmlns:wne=«http://schemas.microsoft.com/office/word/2006/wordml«>
      <
w:body>
        <
w:customXml

w:uri=«Schema_Employee«

w:element=«VFPData«>
          <
w:p
w:rsidR=«00883633«
w:rsidRPr=«00AF60CD«
w:rsidRDefault=«00883633«
w:rsidP=«00AF60CD«>
            <
w:pPr>
              <
w:jc
w:val=«center«></w:jc>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
              </
w:rPr>
            </
w:pPr>
            <
w:r
w:rsidRPr=«00883633«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
              </
w:rPr>
              <
w:t>Пример</w:t>
            </
w:r>
            <
w:r
w:rsidR=«00AF60CD«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
              </
w:rPr>
              <
w:t
xml:space=«preserve«>
</
w:t>
            </
w:r>
            <
w:r
w:rsidR=«00AF60CD«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
                <
w:lang
w:val=«en-US«></w:lang>
              </
w:rPr>
              <
w:t>VFP</w:t>
            </
w:r>
            <
w:r
w:rsidR=«00AF60CD«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
              </
w:rPr>
              <
w:t></w:t>
            </
w:r>
            <
w:r
w:rsidRPr=«00883633«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
              </
w:rPr>
              <
w:t
xml:space=«preserve«>таблицы
в виде таблицы в
</w:t>
            </
w:r>
            <
w:r
w:rsidR=«00AF60CD«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
                <
w:lang
w:val=«en-US«></w:lang>
              </
w:rPr>
              <
w:t>MS</w:t>
            </
w:r>
            <
w:r
w:rsidR=«00AF60CD«
w:rsidRPr=«00AF60CD«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
              </
w:rPr>
              <
w:t
xml:space=«preserve«>
</
w:t>
            </
w:r>
            <
w:r
w:rsidR=«00AF60CD«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
                <
w:lang
w:val=«en-US«></w:lang>
              </
w:rPr>
              <
w:t>Word</w:t>
            </
w:r>
            <
w:r
w:rsidR=«00AF60CD«
w:rsidRPr=«00AF60CD«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
              </
w:rPr>
              <
w:t
xml:space=«preserve«>
2007
</w:t>
            </
w:r>
            <
w:r
w:rsidR=«00AF60CD«>
              <
w:rPr>
                <
w:rFonts
w:asciiTheme=«majorHAnsi«
w:hAnsiTheme=«majorHAnsi«></w:rFonts>
              </
w:rPr>
              <
w:t>.</w:t>
            </
w:r>
          </
w:p>
          <
w:tbl>
            <
w:tblPr>
              <
w:tblStyle
w:val=«1-1«></w:tblStyle>
              <
w:tblW
w:w=«0«
w:type=«auto«></w:tblW>
              <
w:tblLook
w:val=«04A0«></w:tblLook>
            </
w:tblPr>
            <
w:tblGrid>
              <
w:gridCol
w:w=«2392«></w:gridCol>
              <
w:gridCol
w:w=«2393«></w:gridCol>
              <
w:gridCol
w:w=«2393«></w:gridCol>
              <
w:gridCol
w:w=«2393«></w:gridCol>
            </
w:tblGrid>
            <
w:tr
w:rsidR=«00174F3F«
w:rsidTr=«00174F3F«>
              <
w:trPr>
                <
w:cnfStyle
w:val=«100000000000«></w:cnfStyle>
              </
w:trPr>
              <
w:tc>
                <
w:tcPr>
                  <
w:cnfStyle
w:val=«001000000000«></w:cnfStyle>
                  <
w:tcW
w:w=«2392«
w:type=«dxa«></w:tcW>
                </
w:tcPr>
                <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                  <
w:proofErr
w:type=«spellStart«></w:proofErr>
                  <
w:r>
                    <
w:rPr>
                      <
w:lang
w:val=«en-US«></w:lang>
                    </
w:rPr>
                    <
w:t>Фамилия</w:t>
                  </
w:r>
                  <
w:proofErr
w:type=«spellEnd«></w:proofErr>
                </
w:p>
              </
w:tc>
              <
w:tc>
                <
w:tcPr>
                  <
w:tcW
w:w=«2393«
w:type=«dxa«></w:tcW>
                </
w:tcPr>
                <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                  <
w:pPr>
                    <
w:cnfStyle
w:val=«100000000000«></w:cnfStyle>
                  </
w:pPr>
                  <
w:r>
                    <
w:t>Имя</w:t>
                  </
w:r>
                </
w:p>
              </
w:tc>
              <
w:tc>
                <
w:tcPr>
                  <
w:tcW
w:w=«2393«
w:type=«dxa«></w:tcW>
                </
w:tcPr>
                <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                  <
w:pPr>
                    <
w:cnfStyle
w:val=«100000000000«></w:cnfStyle>
                  </
w:pPr>
                  <
w:r>
                    <
w:t>День
рождения
</w:t>
                  </
w:r>
                </
w:p>
              </
w:tc>
              <
w:tc>
                <
w:tcPr>
                  <
w:tcW
w:w=«2393«
w:type=«dxa«></w:tcW>
                </
w:tcPr>
                <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                  <
w:pPr>
                    <
w:cnfStyle
w:val=«100000000000«></w:cnfStyle>
                  </
w:pPr>
                  <
w:r>
                    <
w:t>Комментарий</w:t>
                  </
w:r>
                </
w:p>
              </
w:tc>
            </
w:tr>
            <
xsl:for-each

select=«//VFPData/employee«>
              <
w:customXml

w:element=«employee«>
                <
w:tr
w:rsidR=«00174F3F«
w:rsidTr=«00174F3F«>
                  <
w:trPr>
                    <
w:cnfStyle
w:val=«000000100000«
/>
                  </
w:trPr>
                  <
w:customXml

w:element=«lastname«>
                    <
w:tc>
                      <
w:tcPr>
                        <
w:cnfStyle
w:val=«001000000000«
/>
                        <
w:tcW
w:w=«2392«
w:type=«dxa«
/>
                      </
w:tcPr>
                      <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                        <
w:pPr>
                          <
w:rPr>
                            <
w:lang
w:val=«en-US«
/>
                          </
w:rPr>
                        </
w:pPr>
                        <
w:r>
                          <
w:rPr>
                            <
w:lang

w:val=«en-US«
/>
                          </
w:rPr>
                          <
w:t>
                            <
xsl:value-of

select=«lastname«
/>
                          </
w:t>
                        </
w:r>
                      </
w:p>
                    </
w:tc>
                  </
w:customXml>
                  <
w:customXml

w:element=«firstname«>
                    <
w:tc>
                      <
w:tcPr>
                        <
w:tcW
w:w=«2393«
w:type=«dxa«
/>
                      </
w:tcPr>
                      <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                        <
w:pPr>
                          <
w:cnfStyle
w:val=«000000100000«
/>
                          <
w:rPr>
                            <
w:lang
w:val=«en-US«
/>
                          </
w:rPr>
                        </
w:pPr>
                        <
w:r>
                          <
w:rPr>
                            <
w:lang

w:val=«en-US«
/>
                          </
w:rPr>
                          <
w:t>
                            <
xsl:value-of

select=«firstname«
/>
                          </
w:t>
                        </
w:r>
                      </
w:p>
                    </
w:tc>
                  </
w:customXml>
                  <
w:customXml

w:element=«birthdate«>
                    <
w:tc>
                      <
w:tcPr>
                        <
w:tcW
w:w=«2393«
w:type=«dxa«
/>
                      </
w:tcPr>
                      <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                        <
w:pPr>
                          <
w:cnfStyle
w:val=«000000100000«
/>
                          <
w:rPr>
                            <
w:lang
w:val=«en-US«
/>
                          </
w:rPr>
                        </
w:pPr>
                        <
w:r>
                          <
w:rPr>
                            <
w:lang

w:val=«ru-RU«
/>
                          </
w:rPr>
                          <
w:t>
                            <
xsl:value-of

select=«msxsl:format-date(birthdate,
‘dd.MM.yyyy’)
« />
                          </
w:t>
                        </
w:r>
                      </
w:p>
                    </
w:tc>
                  </
w:customXml>
                  <
w:customXml

w:element=«notes«>
                    <
w:tc>
                      <
w:tcPr>
                        <
w:tcW
w:w=«2393«
w:type=«dxa«
/>
                      </
w:tcPr>
                      <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
                        <
w:pPr>
                          <
w:cnfStyle
w:val=«000000100000«
/>
                          <
w:rPr>
                            <
w:lang
w:val=«en-US«
/>
                          </
w:rPr>
                        </
w:pPr>
                        <
w:r>
                          <
w:rPr>
                            <
w:lang

w:val=«en-US«
/>
                          </
w:rPr>
                          <
w:t>
                            <
xsl:choose>
                              <
xsl:when

test=«string-length(notes)&lt;51«>
                                <
xsl:value-of

select=«notes«/>
                              </
xsl:when>
                              <
xsl:otherwise>
                                <
xsl:value-of

select=«concat(substring(notes,1,50),
substring-before(substring(notes,51), ‘ ‘), ‘ …’)
«
/>
                              </
xsl:otherwise>
                            </
xsl:choose>
                          </
w:t>
                        </
w:r>
                      </
w:p>
                    </
w:tc>
                  </
w:customXml>
                </
w:tr>
              </
w:customXml>
            </
xsl:for-each>
          </
w:tbl>
          <
w:p
w:rsidR=«00174F3F«
w:rsidRDefault=«00174F3F«
w:rsidP=«00174F3F«>
            <
w:pPr>
              <
w:rPr>
                <
w:lang
w:val=«en-US«></w:lang>
              </
w:rPr>
            </
w:pPr>
          </
w:p>
        </
w:customXml>
        <
w:p
w:rsidR=«00174F3F«
w:rsidRPr=«00883633«
w:rsidRDefault=«00174F3F«></w:p>
        <
w:sectPr
w:rsidR=«00174F3F«
w:rsidRPr=«00883633«
w:rsidSect=«00EC3577«>
          <
w:pgSz
w:w=«11906«
w:h=«16838«></w:pgSz>
          <
w:pgMar
w:top=«1134«
w:right=«850«
w:bottom=«1134«
w:left=«1701«
w:header=«708«
w:footer=«708«
w:gutter=«0«></w:pgMar>
          <
w:cols
w:space=«708«></w:cols>
          <
w:docGrid
w:linePitch=«360«></w:docGrid>
        </
w:sectPr>
      </
w:body>
    </
w:document>
  </
xsl:template>
</
xsl:stylesheet>

Файл: getXmlDocument.xslt

т.е. именно в подобный xslt-файл следовало бы
превратить наш исходный файл-шаблон document.xml путём
его редактирования. Здесь добавленные элементы слегка оттенены фоном и пояснения
к добавлениям следующие:

  • создан стандартный для xslt-преобразований
    корневой элемент <xsl:stylesheet … >, которому
    наряду с namespace-ами, обеспечивающими работу
    xslt-преобразований, добавлены все
    namespace-ы из элемента
    <w:document …>
    нашего файла-шаблона document.xml
  • корневой элемент <w:document>…<w:document>
    из document.xml погружён в
    xslt-
    конструкцию <xsl:template match=»/»>…</xsl:template>
    для обработки корневого элемента.
  • перед групповым элементом <w:customXml
    w:element=»employee»>
    организован цикл по всем элементам
    employee входных данных: <xsl:for-each
    select=»//VFPData/employee»>…</xsl:for-each>
    файла
    employee.xml
  • а в каждый элемент
    <w:tc>…</w:tc>
    столбца, точнее в конец вложенного в него элемента
    <w:p>, сделана вставка элемента
    <w:r>…</w:r>
    во вложенный элемент <w:t>…</w:t>
    которого, осуществляется вставка соответствующих значений элементов данных
    из employee.xml, возможно с небольшими
    преобразованиями, специфичными для конкретного элемента данных. Имея ввиду,
    что xslt-вычисления будут происходить внутри цикла
    <xsl:for-each select=»//VFPData/employee»>…

Код же этого дополнительного xslt-преобразования, в
результате применений которого к файлу-шаблону document.xml,
был получен вышеприведённый результат следующий:


<?
xml
version=«1.0«
encoding=«utf-8«?>
<!—
file: genXsltFromTemplate.xslt
—>
<
xsl:stylesheet
version=«1.0«
    
xmlns:xsl=«http://www.w3.org/1999/XSL/Transform«
    
xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance«
    
xmlns:msxsl=«urn:schemas-microsoft-com:xslt«
    
xmlns:msdata=«urn:schemas-microsoft-com:xml-msdata«
    
xmlns:ve=«http://schemas.openxmlformats.org/markup-compatibility/2006«
    
xmlns:o=«urn:schemas-microsoft-com:office:office«
    
xmlns:r=«http://schemas.openxmlformats.org/officeDocument/2006/relationships«
    
xmlns:m=«http://schemas.openxmlformats.org/officeDocument/2006/math«
    
xmlns:v=«urn:schemas-microsoft-com:vml«
    
xmlns:wp=«http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing«
    
xmlns:w10=«urn:schemas-microsoft-com:office:word«
    
xmlns:w=«http://schemas.openxmlformats.org/wordprocessingml/2006/main«
    
xmlns:wne=«http://schemas.microsoft.com/office/word/2006/wordml«
    
exclude-result-prefixes=«xsi
msxsl msdata
«>  <xsl:output
version=«1.0«
method=«xml«
    
encoding=«utf-8«
omit-xml-declaration=«yes«
/>  <!—
Root element in xml-data
—>
  <
xsl:param
name=«prmRootElement«>
    <
xsl:text>VFPData</xsl:text>
  </
xsl:param>
  <!—
Current encoding to replace in
output xml-PI
—>
  <
xsl:param
name=«prmEncoding«>
    <
xsl:text>utf-8</xsl:text>
  </
xsl:param>


  <
xsl:template
match=«/«>
    <xsl:text
disable-output-escaping=«yes«><![CDATA[<?xml
version=»1.0″ encoding=»
]]></xsl:text>
    <
xsl:value-of
select=«$prmEncoding«
/>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[»
standalone=»yes»?>
]]> </xsl:text>

    <!—
Generate open <xsl:stylesheet… &
<xsl:template… elements into output
—>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:stylesheet
version=»1.0″
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:xsl=»http://www.w3.org/1999/XSL/Transform»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:xsi=»http://www.w3.org/2001/XMLSchema-instance»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:msxsl=»urn:schemas-microsoft-com:xslt»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:msdata=»urn:schemas-microsoft-com:xml-msdata»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:ve=»http://schemas.openxmlformats.org/markup-compatibility/2006″
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:o=»urn:schemas-microsoft-com:office:office»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:r=»http://schemas.openxmlformats.org/officeDocument/2006/relationships»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:m=»http://schemas.openxmlformats.org/officeDocument/2006/math»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:v=»urn:schemas-microsoft-com:vml»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:wp=»http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:w10=»urn:schemas-microsoft-com:office:word»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:w=»http://schemas.openxmlformats.org/wordprocessingml/2006/main»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
xmlns:wne=»http://schemas.microsoft.com/office/word/2006/wordml»
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[
exclude-result-prefixes=»xsi msxsl msdata»>
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:output
version=»1.0″ method=»xml» encoding=»utf-8″ standalone=»yes» />
]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:template
match=»/»>
]]></xsl:text>
    <!—
Open w:document element
—>
    <
w:document
xmlns:ve=«http://schemas.openxmlformats.org/markup-compatibility/2006«
        
xmlns:o=«urn:schemas-microsoft-com:office:office«
        
xmlns:r=«http://schemas.openxmlformats.org/officeDocument/2006/relationships«
        
xmlns:m=«http://schemas.openxmlformats.org/officeDocument/2006/math«
        
xmlns:v=«urn:schemas-microsoft-com:vml«
        
xmlns:wp=«http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing«
        
xmlns:w10=«urn:schemas-microsoft-com:office:word«
        
xmlns:w=«http://schemas.openxmlformats.org/wordprocessingml/2006/main«
        
xmlns:wne=«http://schemas.microsoft.com/office/word/2006/wordml«>
      <!—
… continue parsing for
children’s
of w:document element …
—>
      <
xsl:apply-templates
select=«/./w:document/*«
/>
    <!—
Close w:document element
—>
    </
w:document>
    <!—
Generate close … </xsl:template>
& …</xsl:stylesheet> elements into output

—>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[</xsl:template>]]></xsl:text>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[</xsl:stylesheet>]]></xsl:text>
  </
xsl:template>

  <!—

Parsing any current input…
—>
  <
xsl:template
match=«@*|node()«>
    <!—
Get current name from input
—>
    <
xsl:variable
name=«varName«>
      <
xsl:value-of
select=«name()«
/>
    </
xsl:variable>
    <
xsl:choose>
      <
xsl:when
test=«string-length($varName)>0«>
        <
xsl:choose>
          <
xsl:when
test=«$varName=’w:customXml’«>
            <!—
Parsing
<w:customXml…>…</w:customXml>
—>
            <
xsl:call-template
name=«genCustomXml«>
              <
xsl:with-param
name=«prmElementName«
select=«@w:element«
/>
              <
xsl:with-param
name=«prmChildren«
select=«*«
/>
            </
xsl:call-template>
          </
xsl:when>
          <
xsl:otherwise>
            <!—
Parsing any other…
—>
            <!—
… copy current element into
output
—>
            <
xsl:element
name=«{$varName}«>
              <
xsl:for-each
select=«@*«>
                <
xsl:attribute
name=«{name()}«>
                  <
xsl:value-of
select=«.«
/>
                </
xsl:attribute>
              </
xsl:for-each>
              <!—
… continue parsing for child &
as child …
—>
              <
xsl:apply-templates
select=«child::*«
/>
              <
xsl:value-of
select=«text()«
/>
            </
xsl:element>
          </
xsl:otherwise>
        </
xsl:choose>
      </
xsl:when>
      <
xsl:otherwise>
        <
xsl:apply-templates
select=«@*|node()«
/>
      </
xsl:otherwise>
    </
xsl:choose>
  </
xsl:template>

  <!—

Generate w:customXml element
—>
  <
xsl:template
name=«genCustomXml«>
    <
xsl:param
name=«prmElementName«/>
    <
xsl:param
name=«prmChildren«/>
    <!—
Get current name of element from
input
—>
    <
xsl:variable
name=«varName«>
      <
xsl:value-of
select=«name()«
/>
    </
xsl:variable>
    <
xsl:choose>
      <
xsl:when
test=«$prmElementName=$prmRootElement«>
        <!—
Case w:customXml element is root
—>
        <!—
… copy current element
(w:customXml)
—>
        <
xsl:element
name=«{$varName}«>
          <
xsl:for-each
select=«@*«>
            <
xsl:attribute
name=«{name()}«>
              <
xsl:value-of
select=«.«
/>
            </
xsl:attribute>
          </
xsl:for-each>
          <!—
Continue parsing for child…
—>
          <
xsl:apply-templates
select=«msxsl:node-set($prmChildren)/.«
/>
        </
xsl:element>
      </
xsl:when>
      <
xsl:when
test=«count(msxsl:node-set($prmChildren)/.)=0«>
        <!—
Case w:customXml element with
empty childrens
—>
        <!—
… copy current element
(w:customXml)
—>
        <
xsl:element
name=«{$varName}«>
          <
xsl:for-each
select=«@*«>
            <
xsl:attribute
name=«{name()}«>
              <
xsl:value-of
select=«.«
/>
            </
xsl:attribute>
          </
xsl:for-each>
          <!—
Generate <w:r>…</w:r> as child
—>
          <
xsl:call-template
name=«genRunElement«>
            <
xsl:with-param
name=«prmElementName«
select=«$prmElementName«
/>
          </
xsl:call-template>
        </
xsl:element>
      </
xsl:when>
      <
xsl:when
test=«name(msxsl:node-set($prmChildren)/.)=’w:tc’«>
        <!—
Case w:customXml element with
<w:tc>…</w:tc> as first child
—>
        <!—
… copy current element
(w:customXml)
—>
        <
xsl:element
name=«{$varName}«>
          <
xsl:for-each
select=«@*«>
            <
xsl:attribute
name=«{name()}«>
              <
xsl:value-of
select=«.«
/>
            </
xsl:attribute>
          </
xsl:for-each>
          <!—
Generate <w:tc>…</w:tc> as child
—>
          <
w:tc>
            <!—
Scan all children elements in
current <w:tc>…</w:tc> in input
—>
            <
xsl:for-each
select=«msxsl:node-set($prmChildren)/./*«>
              <
xsl:choose>
                <!—
… in case <w:p>…</w:p>
—>
                <
xsl:when
test=«name()=’w:p’«>
                  <!—
Copy current element (w:p)
—>
                  <
xsl:element
name=«{name()}«>
                    <
xsl:for-each
select=«@*«>
                      <
xsl:attribute
name=«{name()}«>
                        <
xsl:value-of
select=«.«
/>
                      </
xsl:attribute>
                    </
xsl:for-each>
                    <!—
… copy all current child
contents as child
—>
                    <!—
<xsl:apply-templates
select=»child::*» />
—>
                    <
xsl:copy-of
select=«child::*«
/>
                    <!—
… insert <w:t>…</w:t> as child
—>
                    <
xsl:call-template
name=«genRunElement«>
                      <
xsl:with-param
name=«prmElementName«
select=«$prmElementName«
/>
                    </
xsl:call-template>
                  </
xsl:element>
                </
xsl:when>
                <!—
… for any other case…
—>
                <
xsl:otherwise>
                  <!—
Copy all current contents into
output
—>
                  <!—
<xsl:apply-templates select=»*» />
—>
                  <
xsl:copy>
                    <
xsl:copy-of
select=«*«
/>
                  </
xsl:copy>
                </
xsl:otherwise>
              </
xsl:choose>
            </
xsl:for-each>
          </
w:tc>
        </
xsl:element>
      </
xsl:when>
      <
xsl:when
test=«name(msxsl:node-set($prmChildren)/.)=’w:tr’«>
        <!—
Case w:customXml element with
<w:tr>…</w:tr> as first child
—>
        <!—
… generate <xsl:for-each …
—>
        <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:for-each
select=»//
]]></xsl:text>
        <
xsl:value-of
select=«$prmRootElement«
/>
        <
xsl:text
disable-output-escaping=«yes«><![CDATA[/]]></xsl:text>
        <
xsl:value-of
select=«$prmElementName«
/>
        <
xsl:text
disable-output-escaping=«yes«><![CDATA[«>]]></xsl:text>
        <!—
… copy current element
(w:customXml)
—>
        <
xsl:element
name=«{$varName}«>
          <
xsl:for-each
select=«@*«>
            <
xsl:attribute
name=«{name()}«>
              <
xsl:value-of
select=«.«
/>
            </
xsl:attribute>
          </
xsl:for-each>
          <!—
Select current <w:tr>…</w:tr> in
input
—>
          <
xsl:for-each
select=«msxsl:node-set($prmChildren)/.«>
            <!—
Copy <w:tr>…</w:tr> into output
—>
            <
xsl:element
name=«{name()}«>
              <
xsl:for-each
select=«@*«>
                <
xsl:attribute
name=«{name()}«>
                  <
xsl:value-of
select=«.«
/>
                </
xsl:attribute>
              </
xsl:for-each>
              <!—
Scan all children elements in
current <w:tr>…</w:tr> in input
—>
              <
xsl:for-each
select=«msxsl:node-set($prmChildren)/./*«>
                <
xsl:choose>
                <!—
… in case not
<w:customXml>…</w:customXml>
—>
                  <
xsl:when
test=«name()!=’w:customXml’«>
                    <!—
Copy all current contents into
output
—>
                    <
xsl:copy-of
select=«.«
/>
                  </
xsl:when>
                  <!—
… in case
<w:customXml>…</w:customXml>
—>
                  <
xsl:otherwise>
                    <!—
Continue parsing …
—>
                    <
xsl:apply-templates
select=«.«
/>
                  </
xsl:otherwise>
                </
xsl:choose>
              </
xsl:for-each>
            </
xsl:element>
          </
xsl:for-each>
        </
xsl:element>
        <!—
… close </xsl:for-each>
—>
        <
xsl:text
disable-output-escaping=«yes«><![CDATA[</xsl:for-each>]]></xsl:text>
      </
xsl:when>
      <
xsl:otherwise>
        <!—
In any other case: insert your
code here …
        <xsl:text disable-output-escaping=»yes»><![CDATA[<xsl:for-each
select=»//]]></xsl:text>
        <xsl:value-of select=»$prmRootElement» />
        <xsl:text disable-output-escaping=»yes»><![CDATA[/]]></xsl:text>
        <xsl:value-of select=»$prmElementName» />
        <xsl:text disable-output-escaping=»yes»><![CDATA[«>]]></xsl:text>
        <xsl:apply-templates select=»msxsl:node-set($prmChildren)/./*» />
        <xsl:text
disable-output-escaping=»yes»><![CDATA[</xsl:for-each>]]></xsl:text>
        
—>
      </
xsl:otherwise>
    </
xsl:choose>
  </
xsl:template>

  <!—

Generate <w:r>… <w:r> element
—>
  <
xsl:template
name=«genRunElement«>
    <
xsl:param
name=«prmElementName«
/>
    <
w:r>
      <
w:rPr>
        <!—
<w:cnfStyle w:val=»000000100000″ />—>
        <
xsl:choose>
          <
xsl:when
test=«$prmElementName=’birthdate’«>
            <
w:lang
w:val=«ru-RU«
/>
          </
xsl:when>
          <
xsl:otherwise>
            <
w:lang
w:val=«en-US«
/>
          </
xsl:otherwise>
        </
xsl:choose>
      </
w:rPr>
      <!—
Generate <w:t>… <w:t> element
—>
      <
xsl:call-template
name=«genTextContent«>
        <
xsl:with-param
name=«prmElementName«
select=«$prmElementName«
/>
      </
xsl:call-template>
    </
w:r>
  </
xsl:template>

  <!—

Generate <w:t>… <w:t> element
—>
  <
xsl:template
name=«genTextContent«>
    <
xsl:param
name=«prmElementName«
/>
    <
w:t>
      <!—
Generate output according to name
of element
—>
      <
xsl:choose>
        <
xsl:when
test=«$prmElementName=’notes’«>
          <!—
… for long input string only
first 50 chars of string
—>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:choose>]]></xsl:text>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:when
test=»string-length(notes)&lt;51″>
]]></xsl:text>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:value-of
select=»notes»/>
]]></xsl:text>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[</xsl:when>]]></xsl:text>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:otherwise>]]></xsl:text>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:value-of
select=»concat(substring(notes,1,50), substring-before(substring(notes,51), ‘
‘), ‘ …’)» />
]]></xsl:text>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[</xsl:otherwise>]]></xsl:text>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[</xsl:choose>]]></xsl:text>
        </
xsl:when>
        <
xsl:when
test=«$prmElementName=’birthdate’«>
          <!—
… convert to russian date format
—>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:value-of
select=»msxsl:format-date(birthdate, ‘dd.MM.yyyy’)» />
]]></xsl:text>
        </
xsl:when>
        <
xsl:otherwise>
          <!—
… simple copy
—>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[<xsl:value-of
select=»
]]></xsl:text>
          <
xsl:value-of
select=«$prmElementName«/>
          <
xsl:text
disable-output-escaping=«yes«><![CDATA[»
/>
]]></xsl:text>
        </
xsl:otherwise>
      </
xsl:choose>
    </
w:t>
  </
xsl:template>
</
xsl:stylesheet>

Файл: genXsltFromTemplate.xslt

Так вот, если теперь применить xslt-преобразование,
определенное в файле getXmlDocument.xslt, к данным из
файла employee.xml, а полученный при этом результат, в
качестве файла document.xml, заменить в шаблоне
документа «Пример VFP-таблицы в виде MS Word 2007.docx» (точнее,
подменить файл ..DOCXПример VFP-таблицы в виде MS Word 2007worddocument.xml на полученный
результат и
применить командный файл fromContentsToDocxUseWinRAR.cmd), то после
всех этих преобразований документ показан на картинке ниже:

docx12.jpg
Рис.12

Как видите, таблица с данными сформирована и наша цель достигнута! :-) Здесь
чтобы показать/скрыть элементы схемы в документе, установите/снимите выделенный
красным кружком в панели «Структура XML»
соответствующий флажок. Обратите внимание, что MS Word
предупреждает о неверном формате даты в поле birthdate,
хотя предлагаемый им формат для даты в точности совпадает с тем, который
находиться в документе. Полагаю, что проблема в том, что предупреждение
возникает из-за того, что дата в документе противоречит не формату даты в
документе, а именно формату даты в XSD-схеме, т.е.
не в YYYY-MM-DD формате, как требуют того xml-данные.
Наконец, в столбце «Комментарий» данные урезаны до длины примерно в 50 символов,
т.е. в полном соответствии с тем, как мы преобразовали данные по полю
note в xslt-преобразовании
getXmlDocument.xslt.

Таким образом, окончательный вид нашей исходной VFP-таблички
employee в MS Word документе
подобен следующему:

docx13.jpg
Рис.13

C#-код приложения для
заполнения шаблона MS Word 2007 документа данными
из xml-файла.

Показанный на Рис.13 результат был получен с помощью вот такого
C#-кода консольного приложения в MS
VS .NET 2005 (SP1VS2005)
:


#define
RUS_LANG

#define
TMP_OUT
using
System;

using
System.Collections.Generic;

using
System.Text;
using
System.Xml;

using
System.Xml.Xsl;

using
System.IO;

using
System.IO.Packaging;

using
System.Diagnostics;

namespace
xml2docx
{
  
class
xml2docx
  
{
    
const
string PATH_XML =
@»……..XML»;
    
const
string PATH_DOCX =
@»……..DOCX»;
    
const
string PATH_XSLT =
@»……..XSLT»;
    
const
string XML_DATA_FILE =
«employee.XML»;
    
const
string XSLT_FILE =
«genXsltFromTemplate.xslt»;
    
const
string URI_DOCUMENT_XML =
«/word/document.xml»;
    
const
string USE_ENCODING =
«utf-8»;
    
const
string XML_PI =
«<?xml version=»1.0″ encoding=»»
+ USE_ENCODING +
«»
standalone=»yes»?>rn»
;

#if
RUS_LANG
    
const
string TEMPLATE_DOCX =
«Пример VFP-таблицы в виде MS Word 2007.docx»;
    
const
string ERR_DIR_NOTFOUND =
«Не найден каталог: «;
    
const
string ERR_FILE_NOTFOUND =
«Не найден файл: «;
    
const
string ERR_EMPTY_XML =
«Нет xml-данных для: «;
    
const
string ERR_EMPTY_XSLTRES =
«Результат xslt-преобразований пуст для: «;
    
const
string DOC_PROP_DESCRIPTION
=
«Документ создан на основе шаблона и программно заполнен данными из xml-файла.»;
    
const
string DOC_PROP_SUBJECT =
«Использование Open XML формата в MS Word 2007»;

#else
    
const string TEMPLATE_DOCX = «Example VFP-table in MS Word 2007.docx»;
    const string ERR_DIR_NOTFOUND = «Not found directory: «;
    const string ERR_FILE_NOTFOUND = «Not found file: «;
    const string ERR_EMPTY_XML = «Empty xml-data for: «;
    const string ERR_EMPTY_XSLTRES = «Empty result of xslt-transformation for: «;
    const string DOC_PROP_DESCRIPTION = «The document is created on the basis of
template and is filled by data from a xml-file from code program.»;
    const string DOC_PROP_SUBJECT = «Use Open XML format in MS Word 2007»;

#endif
    static

void Main(string[]
args)
    {
     

bool
bStartAppWord =
false;

      try
      
{
        
///////////////////////////////////////////////////////////////////////

        
// Check existence of external files

        
if (!Directory.Exists(PATH_XML))
        {
        
  throw (new
DirectoryNotFoundException(ERR_DIR_NOTFOUND
+ PATH_XML));
        }
        
if (!Directory.Exists(PATH_DOCX))
        {
        
  throw (new
DirectoryNotFoundException(ERR_DIR_NOTFOUND
+ PATH_DOCX));
        }
        
if (!Directory.Exists(PATH_XSLT))
        {
        
  throw (new
DirectoryNotFoundException(ERR_DIR_NOTFOUND
+ PATH_XSLT));
        }
        
string
FileXmlData = PATH_XML + XML_DATA_FILE;
        
if (!File.Exists(FileXmlData))
        {
        
  throw (new
FileNotFoundException(ERR_FILE_NOTFOUND
+ FileXmlData));
        }
        
string
FileTemplateDocx = PATH_DOCX + TEMPLATE_DOCX;
        
if (!File.Exists(FileTemplateDocx))
        {
        
  throw (new
FileNotFoundException(ERR_FILE_NOTFOUND
+ FileTemplateDocx));
        }
        
string
FileGenXsltFromTemplate = PATH_XSLT + XSLT_FILE;
        
if (!File.Exists(FileTemplateDocx))
        {
        
  throw (new
FileNotFoundException(ERR_FILE_NOTFOUND
+ FileGenXsltFromTemplate));
        }
///////////////////////////////////////////////////////////////////////

        
// Copy template to target folder

        
string
OutputFolder =
Environment.GetFolderPath(Environment.SpecialFolder.Personal);
        
string
OutputFileDocx = OutputFolder +
@»_»
+ TEMPLATE_DOCX;
        
File.Copy(FileTemplateDocx,
OutputFileDocx,
true);
        
if (!File.Exists(OutputFileDocx))
        {
        
  throw (new
FileNotFoundException(ERR_FILE_NOTFOUND
+ OutputFileDocx));
        }
///////////////////////////////////////////////////////////////////////

        
// Load xml-data file and get name of
its root element

        
XmlDocument
xmlDocData =
new
XmlDocument();
        xmlDocData.Load(FileXmlData);
        
if
(xmlDocData.DocumentElement ==
null
|| xmlDocData.DocumentElement.ChildNodes.Count == 0)
        {
          
throw (new
NullReferenceException(ERR_EMPTY_XML
+ FileXmlData));
        }
        
string
rootElementName = xmlDocData.DocumentElement.Name;
///////////////////////////////////////////////////////////////////////

        
// Get package copy of template and
prepare for its processing

        
Encoding enc =
Encoding.GetEncoding(USE_ENCODING);
        
string strXmlDoc
= System.
String.Empty;
        
string docxTitle
= System.
String.Empty;
        
Uri uriWordDocXml
=
new
Uri(URI_DOCUMENT_XML,
UriKind.Relative);
        
using (Package
package =
Package.Open(OutputFileDocx,
FileMode.Open))
        {
          
///////////////////////////////////////////////////////////////////////
          // Get PackagePart of template for
URI_DOCUMENT_XML(/word/document.xml)
          
PackagePart
packagePart = package.GetPart(uriWordDocXml);
          
// … and its stream in ReadWrite mode
          
using (Stream
ppStrmXmlDoc = packagePart.GetStream(
FileMode.Open,
FileAccess.ReadWrite))
          {
            
///////////////////////////////////////////////////////////////////////////////
            // Prepare and execute the first
xslt-transformation
            // with document.xml and by using
xslt-file FileGenXsltFromTemplate
            
XmlDocument
xmlDoc =
new
XmlDocument();
            xmlDoc.Load(ppStrmXmlDoc);
            
XsltArgumentList
xsltArgumentList =
new
XsltArgumentList();
            xsltArgumentList.AddParam(
«prmRootElement»,
«»,
rootElementName);
            xsltArgumentList.AddParam(
«prmEncoding»,
«», USE_ENCODING);
            
XslCompiledTransform
xslXsltDocTrans =
new
XslCompiledTransform();
            xslXsltDocTrans.Load(FileGenXsltFromTemplate);
            
string strXsltDoc
= System.
String.Empty;
            
using (MemoryStream
msXsltDoc =
new
MemoryStream())
            {
              
using (XmlTextWriter
xtwXsltDoc =
new
XmlTextWriter(msXsltDoc,
enc))
              {
                xslXsltDocTrans.Transform(xmlDoc.CreateNavigator(), xsltArgumentList,
xtwXsltDoc);
                strXsltDoc = enc.GetString(msXsltDoc.GetBuffer(), 0, (
int)msXsltDoc.Length);
                xtwXsltDoc.Close();
              }
              msXsltDoc.Close();
            }
            
int nPos =
strXsltDoc.IndexOf(
‘<‘);
            
if (nPos == (-1))
            {
            
  throw (new
NullReferenceException(ERR_EMPTY_XSLTRES
+ FileGenXsltFromTemplate));
            }
            strXsltDoc = strXsltDoc.Substring(nPos);
            nPos = strXsltDoc.IndexOf(
«<?xml»);
            
if (nPos == (-1))
            {
              strXsltDoc = XML_PI + strXsltDoc;
            }
///////////////////////////////////////////////////////////////////////////////
            // Load result of first
xslt-transformation
            
XmlDocument
xmlDocXslt =
new
XmlDocument();
            xmlDocXslt.LoadXml(strXsltDoc);

#if
TMP_OUT
            xmlDocXslt.Save(
«getXmlDocument.xslt»);

#endif
            
///////////////////////////////////////////////////////////////////////////////
            // Prepare and execute the second
xslt-transformation
            // with input xml-data-file and by
using first result of xslt-transformation
            
XslCompiledTransform
xslXmlDocTrans =
new
XslCompiledTransform();
            xslXmlDocTrans.Load(xmlDocXslt.CreateNavigator());
            
using (MemoryStream
msXmlDoc =
new
MemoryStream())
            {
              
using (XmlTextWriter
xtwXmlDoc =
new
XmlTextWriter(msXmlDoc,
enc))
              {
                xslXmlDocTrans.Transform(xmlDocData.CreateNavigator(), xtwXmlDoc);
                strXmlDoc = enc.GetString(msXmlDoc.GetBuffer(), 0, (
int)msXmlDoc.Length);
                xtwXmlDoc.Close();
              }
              msXmlDoc.Close();
            }
            nPos = strXmlDoc.IndexOf(
‘<‘);
            
if (nPos == (-1))
            {
            
  throw (new
NullReferenceException(ERR_EMPTY_XSLTRES
+ FileXmlData));
            }
            strXmlDoc = strXmlDoc.Substring(nPos);
            nPos = strXmlDoc.IndexOf(
«<?xml»);
            
if (nPos == (-1))
            {
              strXmlDoc = XML_PI + strXmlDoc;
            }
///////////////////////////////////////////////////////////////////////////////
            // Load result of second
xslt-transformation as xml-document
            
XmlDocument
xmlDocOut =
new
XmlDocument();
            xmlDocOut.LoadXml(strXmlDoc);

#if
TMP_OUT
            xmlDocOut.Save(
«document.xml»);

#endif
            
/////////////////////////////////////////////////////////////////////////////////
            // Replace PackagePart-stream with
result of second xslt-transformation
            
ppStrmXmlDoc.Seek(0, SeekOrigin.Begin);
            xmlDocOut.Save(ppStrmXmlDoc);
            ppStrmXmlDoc.Flush();
            ppStrmXmlDoc.Close();
////////////////////////////////////////////////////////////////////////////////////////
            // Get the text of the first paragraph
of the document as title of the document
            
string ns_w =
xmlDocOut.DocumentElement.GetNamespaceOfPrefix(
«w»);
            
XmlNamespaceManager
nsManager =
new
XmlNamespaceManager(xmlDocOut.NameTable);
            nsManager.AddNamespace(
«w»,
ns_w);
            
XmlNode xnTitle =
xmlDocOut.DocumentElement.SelectSingleNode(
«//w:customXml[@w:element='»
+ rootElementName +
«‘]/w:p»,
nsManager);
            
if (xnTitle !=
null)
            {
            docxTitle = xnTitle.InnerText;
            }
          }
          
if
(docxTitle.Length > 0)
          {
          
  ///////////////////////////////////////////////////////////////////////////////
            // Replace title and some other
PackageProperties

            
package.PackageProperties.Title = docxTitle;
            package.PackageProperties.Subject = DOC_PROP_SUBJECT;
            package.PackageProperties.Description = DOC_PROP_DESCRIPTION;
          }
          package.Flush();
          package.Close();
        }
        


        
///////////////////////////////////////////////////////////////////////////////
        // Open the new docx file in Word 2007
        
Process proc =
new
Process();
        proc.EnableRaisingEvents =
false;
        proc.StartInfo.FileName =
«winword»;
        proc.StartInfo.Arguments =
«»»
+ OutputFileDocx +
«»»;
        proc.Start();
        bStartAppWord =
true;

      }
      catch (Exception
ex)
      {
        
Console.WriteLine(«***
Error: «
+ ex.ToString());
      }
     

if (!bStartAppWord)
     
{
       
Console.WriteLine(«Press
any key to continue…»
);
       
Console.ReadKey();
     
}

    }
  }
}

Файл: xml2docx.cs

Чтобы Вам самим создать подобное приложение в MS VS .NET
2005
, проделайте следующее:

  • создайте консольное C#-приложение,
  • в
    панели Solution Explorerв папку References
    добавьте файл C:Program FilesReference AssembliesMicrosoftFrameworkv3.0WindowsBase.dll
  • переименуйте файл Program.cs на
    xml2docx.cs
  • замените только что переименованный файл вышеприведённым.

Обратите внимание на значения констант: PATH_XML, PATH_DOCX, PATH_XSLT в коде.
Они указывают на папки, являющиеся родительскими по отношению в парки
Solution Вашего C#-приложения.
Соответственно родительскими должны быть созданы папки: DOCX,
XML и XSLT, в которые должны
быть помещены файлы:

  • DOCX«Пример VFP-таблицы в виде MS Word 2007.docx»
    — шаблон
    документа;
  • XMLemployee.XML, employee.XSD — данные
    VFP-таблицы в xml-формате;
  • XSLTgenXsltFromTemplate.xslt
    предварительное xslt-преобразование для получения
    xslt-преобразования заполнения части
    docx-документа (document.xml)
    данными;

соответственно. А после успешного выполнения скомпилированного
приложения xml2docx.exe, в текущей папке будут созданы
файлы: document.xml, getXmlDocument.xslt, а
заполненный данными документ «_Пример VFP-таблицы в виде MS Word 2007.docx» будет помещён в папку: C:Documents and Settings<ИмяПользователя>Мои документы

VFP-код приложения для
заполнения шаблона MS Word 2007 документа данными
из xml-файла.

Используя технику, изложенную в статье Hosting the .NET Runtime in Visual FoxPro, by Rick Strahl external.gif usa.gif, приведённый
выше C#-код можно переписать и для использования
из-под MS Visual FoxPro 8.0 (или выше). И вот каким он
у меня получился:


#
INCLUDE
«SolWestWindEx.h»
#
INCLUDE
«FrameworkWrap.h»

#

DEFINE
RUS_LANG .T.
#
DEFINE
TMP_OUT .T.
#
DEFINE
CRLF
CHR(13)
+
CHR(10)
#
DEFINE
SW_NORMAL 1

#

DEFINE
PATH_XML «….XML»
#
DEFINE
PATH_DOCX «….DOCX»
#
DEFINE
PATH_XSLT «….XSLT»
#
DEFINE
XML_DATA_FILE «employee.XML»
#
DEFINE
XSLT_FILE «genXsltFromTemplate.xslt»
#
DEFINE
XSLT_FILE_FIRST_RESULT «getXmlDocument.xslt»
#
DEFINE
XML_FILE_SECOND_RESULT «document.xml»
#
DEFINE
URI_DOCUMENT_XML «/word/document.xml»
#
DEFINE
USE_ENCODING «utf-8»
#
DEFINE
XML_PI ‘<?xml version=»1.0″ encoding=»‘ +
USE_ENCODING + ‘» standalone=»yes»?>’ + CRLF
#
DEFINE
MYDOC_FOLDER «MyDocuments»

#

IF
RUS_LANG
 
#
DEFINE
TEMPLATE_DOCX «Пример VFP-таблицы в виде MS
Word 2007.docx»
 
#
DEFINE
ERR_VFP_VERSION «Извините, данный код
предназначен для VFP 8.0 (или выше).»
 
#
DEFINE
ERR_LABEL_OUTPUT «*** Возникла ошибка: «
 
#
DEFINE
ERR_DIR_NOTFOUND «Не найден каталог: «
 
#
DEFINE
ERR_FILE_NOTFOUND «Не найден файл: «
 
#
DEFINE
ERR_EMPTY_XML «Нет xml-данных для: «
 
#
DEFINE
ERR_EMPTY_XSLTRES «Результат
xslt-преобразований пуст для: «
 
#
DEFINE
DOC_PROP_DESCRIPTION «Документ создан на
основе шаблона и программно заполнен данными из xml-файла.»
 
#
DEFINE
DOC_PROP_SUBJECT «Использование Open XML
формата в MS Word 2007»
 
#
DEFINE
MSG_SUCCESS_RESUT «Процесс успешно завершён!
Ожидание открытия полученного нового документа в MS Word 2007…»
#
ELSE
 
#DEFINE
TEMPLATE_DOCX «Example VFP-table in MS
Word 2007.docx»
 
#
DEFINE
ERR_VFP_VERSION «Sorry, this code for VFP 8.0
(or later).»
 
#
DEFINE
ERR_LABEL_OUTPUT «*** Error appears: «
 
#
DEFINE
ERR_DIR_NOTFOUND «Not found directory: «
 
#
DEFINE
ERR_FILE_NOTFOUND «Not found file: «
 
#
DEFINE
ERR_EMPTY_XML «Empty xml-data for: «
 
#
DEFINE
ERR_EMPTY_XSLTRES «Empty result of
xslt-transformation for: «
 
#
DEFINE
DOC_PROP_DESCRIPTION «The document is created
on the basis of template and is filled by data from a xml-file from code
program.»
 
#
DEFINE
DOC_PROP_SUBJECT «Use Open XML format in MS
Word 2007»
 
#
DEFINE
MSG_SUCCESS_RESUT «Process is successfully
completed! Waiting of opening of the received new document in MS Word 2007…»
#
ENDIF

CLEAR
SET DEFAULT TO

(LEFT(SYS(16),
RAT(«»,
SYS(16))))

IF VERSION
(5) < 800
 
? ERR_VFP_VERSION

  RETURN

.F.

ENDIF

PRIVATE

poBridge
&& as DotNetBridge OF
FULLPATH(«DotNetBridge.prg»)

LOCAL
loException
as Exception;
  ,lbError
as
Boolean;
  ,bIsWordDoc
as
Boolean
TRY
 

*///////////////////////////////////////////////////////////////////////
  *// Check existence of external files
 
IF
!DIRECTORY(PATH_XML)
   
ERROR
101, ERR_DIR_NOTFOUND +
LOWER(FULLPATH(PATH_XML))
 
ENDIF
 
IF
!DIRECTORY(PATH_DOCX)
   
ERROR
101, ERR_DIR_NOTFOUND +
LOWER(FULLPATH(PATH_DOCX))
 
ENDIF
 
IF
!DIRECTORY(PATH_XSLT)
   
ERROR
101, ERR_DIR_NOTFOUND +
LOWER(FULLPATH(PATH_XSLT))
 
ENDIF
 
LOCAL
lcFileXmlData
as String
 
lcFileXmlData =
LOWER(FULLPATH(PATH_XML
+ XML_DATA_FILE))
 
IF
!FILE(lcFileXmlData)
   
ERROR
101, ERR_FILE_NOTFOUND + lcFileXmlData
 
ENDIF
 
LOCAL
lcFileTemplateDocx
as String
 
lcFileTemplateDocx =
LOWER(FULLPATH(PATH_DOCX))
+ TEMPLATE_DOCX
 
IF
!FILE(lcFileTemplateDocx)
   
ERROR
101, ERR_FILE_NOTFOUND + lcFileTemplateDocx
 
ENDIF
 
LOCAL
lcFileGenXsltFromTemplate
as String
 
lcFileGenXsltFromTemplate =
LOWER(FULLPATH(PATH_XSLT))
+ XSLT_FILE
 
IF
!FILE(lcFileTemplateDocx)
   
ERROR
101, ERR_FILE_NOTFOUND +
lcFileGenXsltFromTemplate
 
ENDIF
*////////////////////////////////////////////////////////////////
  *// Create DotNetBridge object
 
LOCAL
lcBridgeFileProc
as String
 
lcBridgeFileProc =
LOWER(FULLPATH(BRIDGE_PROC))
 
IF
!FILE(lcBridgeFileProc)
   
ERROR
101, lcBridgeFileProc
 
ENDIF
 
IF
NOT (JUSTSTEM(lcBridgeFileProc)
$
LOWER(SET(«Procedure»)))
   
SET PROCEDURE TO
(lcBridgeFileProc)
ADDITIVE
 
ENDIF
 
poBridge =
CREATEOBJECT(BRIDGE_CLASS)
 
IF VARTYPE(poBridge)
# ‘O’
   
ERROR
‘Can not create ‘ + BRIDGE_CLASS + ‘ object.’
 
ENDIF
*////////////////////////////////////////////////////////////////
  *// Set procedure to «FrameworkWrap.prg»
 
LOCAL
lcFrameworkWrapFileProc
as String
 
lcFrameworkWrapFileProc =
LOWER(FULLPATH(«FrameworkWrap.prg»))
 
IF
!FILE(lcFrameworkWrapFileProc)
   
ERROR
101, lcFrameworkWrapFileProc
 
ENDIF
 
IF
NOT (JUSTSTEM(lcFrameworkWrapFileProc)
$
LOWER(SET(«Procedure»)))

   
SET PROCEDURE TO
(lcFrameworkWrapFileProc)
ADDITIVE
 
ENDIF

*////////////////////////////////////////////////////////////////
  *// LoadAssembly ASSEMBLY_SYSTEM
 
IF
!poBridge.LoadAssembly(ASSEMBLY_SYSTEM)
   
ERROR
poBridge.cErrorMsg
 
ENDIF
*///////////////////////////////////////////////////////////////////////
 
*// Copy docx-template to target folder
 
LOCAL
loWSH
as
WScript.Shell
 
loWSH =
CREATEOBJECT(«WScript.Shell»)
 
LOCAL
lcOutputFolder
as String
 
lcOutputFolder =
loWSH.SpecialFolders(MYDOC_FOLDER)
 
LOCAL
lcOutputFileDocx
as String
 
lcOutputFileDocx = lcOutputFolder +
«_» + TEMPLATE_DOCX
 
poBridge.InvokeStaticMethod(«System.IO.File», «Copy», lcFileTemplateDocx,
lcOutputFileDocx, .T.)
 
IF
!EMPTY(poBridge.cErrorMsg)
   
ERROR
poBridge.cErrorMsg
 
ENDIF
*//////////////////////////////////////////////////////////////////////////////
 
*// Creation some objects from ASSEMBLY_SYSTEM, necessary during processing
 
LOCAL
loEnc
as
Encoding
OF FULLPATH(«FrameworkWrap.prg»)
 
loEnc =
CREATEOBJECT(«Encoding»,
USE_ENCODING)
 
LOCAL
lnUriKindRelative
as Integer
 
lnUriKindRelative =
poBridge.GetStaticProperty(«System.UriKind», «Relative»)
 
IF EMPTY(lnUriKindRelative)
   
ERROR
poBridge.cErrorMsg
 
ENDIF
 
LOCAL
loUri
as
Uri
OF FULLPATH(«FrameworkWrap.prg»)
 
loUri =
CREATEOBJECT(«Uri»,
URI_DOCUMENT_XML, lnUriKindRelative)
 
LOCAL
lnFileModeOpen
as Integer
 
lnFileModeOpen =
poBridge.GetStaticProperty(«System.IO.FileMode», «Open»)
 
IF EMPTY(lnFileModeOpen)
   
ERROR
poBridge.cErrorMsg
 
ENDIF
 
LOCAL
lnFileAccessReadWrite
as Integer
 
lnFileAccessReadWrite =
poBridge.GetStaticProperty(«System.IO.FileAccess», «ReadWrite»)
 
IF EMPTY(lnFileAccessReadWrite)
   
ERROR
poBridge.cErrorMsg
 
ENDIF
 
LOCAL
loMstrXsltDoc
as
MemoryStream
OF FULLPATH(«FrameworkWrap.prg»)
 
loMstrXsltDoc =

CREATEOBJECT
(«MemoryStream»)
 
LOCAL
loMstrXmlDoc
as
MemoryStream
OF FULLPATH(«FrameworkWrap.prg»)
 
loMstrXmlDoc =

CREATEOBJECT
(«MemoryStream») 

*////////////////////////////////////////////////////////////////
 
*// LoadAssembly ASSEMBLY_WINDOWSBASE
 
IF
!poBridge.LoadAssembly(ASSEMBLY_WINDOWSBASE)
   
ERROR
(poBridge.cErrorMsg)
 
ENDIF
*///////////////////////////////////////////////////////////////////////
 
*// Get package copy of template and prepare for its processing
 
LOCAL
loPackage
as
Package
OF FULLPATH(«FrameworkWrap.prg»)
 
loPackage =

CREATEOBJECT
(«Package»,
lcOutputFileDocx)
 
*— Get PackagePart
of template for URI_DOCUMENT_XML(/word/document.xml)
 
LOCAL
loPackagePart
as
PackagePart
OF FULLPATH(«FrameworkWrap.prg»)
 
loPackagePart =

CREATEOBJECT
(«PackagePart»;
   
,loPackage.GetPart(loUri.GetOwnerFwObject()))
 
*— … and its
stream in ReadWrite mode
 
LOCAL
loPpStrmXmlDoc
as
Stream
OF FULLPATH(«FrameworkWrap.prg»)
  loPpStrmXmlDoc =

CREATEOBJECT
(«Stream»;
   
,loPackagePart.GetStream(lnFileModeOpen, lnFileAccessReadWrite))

  *////////////////////////////////////////////////////////////////
  *// LoadAssembly ASSEMBLY_SYSTEM_XML
  
IF
!poBridge.LoadAssembly(ASSEMBLY_SYSTEM_XML)

    
ERROR
poBridge.cErrorMsg

  
ENDIF
  
*///////////////////////////////////////////////////////////////////////
  *// Load xml-data file and get name of its root element
  
LOCAL
loXmlDocData
as
XmlDocument
OF FULLPATH(«FrameworkWrap.prg»)

  
loXmlDocData =

CREATEOBJECT
(«XmlDocument»)

  
loXmlDocData.
Load(lcFileXmlData)

  
LOCAL
loXmlDocDataDocumentElement
as
XmlElement
OF FULLPATH(«FrameworkWrap.prg»)

  
loXmlDocDataDocumentElement =

CREATEOBJECT(«XmlElement»,
loXmlDocData.DocumentElement)

  
LOCAL
lcRootElementName
as String

  
lcRootElementName =
loXmlDocDataDocumentElement.ElementName
  
*///////////////////////////////////////////////////////////////////////////////
  *// Prepare and execute the first xslt-transformation
  *// with document.xml and by using xslt-file FileGenXsltFromTemplate
  
LOCAL
lcStrXsltDoc
as String

  
LOCAL
loXmlDoc
as
XmlDocument
OF FULLPATH(«FrameworkWrap.prg»)

  
loXmlDoc =

CREATEOBJECT
(«XmlDocument»)

  
loXmlDoc.
Load(loPpStrmXmlDoc.GetOwnerFwObject())

  
LOCAL
loXsltArgumentList
as
XsltArgumentList
OF FULLPATH(«FrameworkWrap.prg»)

  
loXsltArgumentList =

CREATEOBJECT(«XsltArgumentList»)

  
loXsltArgumentList.AddParam(«prmRootElement», «», lcRootElementName)

  
loXsltArgumentList.AddParam(«prmEncoding», «», USE_ENCODING)

  
LOCAL
loXtwXsltDoc
as
XmlTextWriter
OF FULLPATH(«FrameworkWrap.prg»)

  
loXtwXsltDoc =

CREATEOBJECT
(«XmlTextWriter»;

    
,loMstrXsltDoc.GetOwnerFwObject(), loEnc.GetOwnerFwObject())

  
LOCAL
loXslXsltDocTrans
as
XslCompiledTransform
OF FULLPATH(«FrameworkWrap.prg»)

  
loXslXsltDocTrans =

CREATEOBJECT
(«XslCompiledTransform»)

  
loXslXsltDocTrans.
Load(lcFileGenXsltFromTemplate)

  
loXslXsltDocTrans.
Transform(loXmlDoc.CreateNavigator();

    
,loXsltArgumentList.GetOwnerFwObject(), loXtwXsltDoc.GetOwnerFwObject())

  
lcStrXsltDoc = loEnc.GetString(loMstrXsltDoc.GetBuffer(), 0,
loMstrXsltDoc.Length)

  
LOCAL
lnPos
as Integer

  
lnPos =
ATC(«<«,
lcStrXsltDoc)

  
IF
lnPos = 0

    
ERROR
(ERR_EMPTY_XSLTRES +
lcFileGenXsltFromTemplate)

  
ENDIF

  
lcStrXsltDoc =
SUBSTRC(lcStrXsltDoc,
lnPos)

  
lnPos =
ATC(«<?xml»,
lcStrXsltDoc)

  
IF
lnPos = 0

    
lcStrXsltDoc = XML_PI + lcStrXsltDoc

  
ENDIF
  
*///////////////////////////////////////////////////////////////////////////////
  *// Load result of first xslt-transformation
  
LOCAL
loXmlDocXslt
as
XmlDocument
OF FULLPATH(«FrameworkWrap.prg»)

  
loXmlDocXslt =

CREATEOBJECT
(«XmlDocument»)

  
loXmlDocXslt.
LoadXml(lcStrXsltDoc)
#
IF
TMP_OUT

  
loXmlDocXslt.
Save(XSLT_FILE_FIRST_RESULT)
#
ENDIF

  
*///////////////////////////////////////////////////////////////////////////////
  *// Prepare and execute the second xslt-transformation
  *// with input xml-data-file and by using first result of xslt-transformation
  
LOCAL
lcStrXmlDoc
as String

  
LOCAL
loXslXmlDocTrans
as
XslCompiledTransform
OF FULLPATH(«FrameworkWrap.prg»)

  
loXslXmlDocTrans =

CREATEOBJECT
(«XslCompiledTransform»)

  
loXslXmlDocTrans.
Load(loXmlDocXslt.CreateNavigator())

  
LOCAL
loXtwXmlDoc
as
XmlTextWriter
OF FULLPATH(«FrameworkWrap.prg»)

  
loXtwXmlDoc =

CREATEOBJECT
(«XmlTextWriter»;

    
,loMstrXmlDoc.GetOwnerFwObject(), loEnc.GetOwnerFwObject())

  
loXslXmlDocTrans.
Transform(loXmlDocData.CreateNavigator(),
loXtwXmlDoc.GetOwnerFwObject())

  
lcStrXmlDoc = loEnc.GetString(loMstrXmlDoc.GetBuffer(), 0,
loMstrXmlDoc.Length)

  
lnPos =
ATC(«<«,
lcStrXmlDoc)

  
IF
lnPos = 0

    
ERROR
(ERR_EMPTY_XSLTRES + lcFileXmlData)

  
ENDIF

  
lcStrXmlDoc =
SUBSTRC(lcStrXmlDoc,
lnPos)

  
lnPos =
ATC(«<?xml»,
lcStrXmlDoc)

  
IF
lnPos = 0

    
lcStrXmlDoc = XML_PI + lcStrXmlDoc

  
ENDIF
  
*///////////////////////////////////////////////////////////////////////////////
  *// Load result of second xslt-transformation as xml-document
  
LOCAL
loXmlDocOut
as
XmlDocument
OF FULLPATH(«FrameworkWrap.prg»)

  
loXmlDocOut =

CREATEOBJECT
(«XmlDocument»)

  
loXmlDocOut.
LoadXml(lcStrXmlDoc)
#
IF
TMP_OUT

  
loXmlDocOut.
Save(XML_FILE_SECOND_RESULT)
#
ENDIF

  
*///////////////////////////////////////////////////////////////////////////////
  *// Replace PackagePart-stream with result of second xslt-transformation
  
loPpStrmXmlDoc.Position
= 0

  
loXmlDocOut.
Save(loPpStrmXmlDoc.GetOwnerFwObject())

  
loPpStrmXmlDoc.Flush()

  
loPpStrmXmlDoc.
Close()

  
bIsWordDoc = .T.

CATCH TO
loException

  
lbError = .T.

  
LOCAL
lcMsg
as String

  
lcMsg = «Error: » +
LTRIM(STR(loException.ErrorNo))
+ CRLF ;

    
+ «LineNo: » +
LTRIM(STR(loException.LineNo))
+ CRLF ;

    
+ «Message: » +
IIF(EMPTY(loException.Message),
‘Unknown error’, loException.
Message)

  
? ERR_LABEL_OUTPUT

  
? lcMsg

FINALLY

  
loWSH =
NULL

  
IF TYPE
(‘poBridge’) = ‘O’ AND !ISNULL(poBridge)

    
*— Remove all
objects, which keeping resources of memory
    
loXtwXmlDoc =
NULL

    
loXtwXsltDoc =
NULL

    
loPpStrmXmlDoc =
NULL

    
loMstrXsltDoc =
NULL

    
loMstrXmlDoc =
NULL

    
loPackage =
NULL

    
poBridge.Unload()

    
poBridge =
NULL

  
ENDIF
ENDTRY

LOCAL

lbResult
as
Boolean

IF
bIsWordDoc

  
*///////////////////////////////////////////////////////////////////////////////
  *// Open the new docx file in Word 2007
  
WAIT
MSG_SUCCESS_RESUT
WINDOW NOWAIT TIMEOUT
10

  
DECLARE INTEGER
ShellExecute
IN
shell32.dll
;

    
INTEGER hwnd,
;

    
STRING
@lsOperation, ;

    
STRING
@lsFile, ;

    
STRING
@lsParameters, ;

    
STRING
@lsDirectory, ;

    
INTEGER
liShowCmd

  
DECLARE INTEGER
GetDesktopWindow
IN
user32.dll

  
LOCAL
lnRetCode
as Integer

  
lnRetCode = 0

  
lnRetCode = ShellExecute(GetDesktopWindow(), «open», lcOutputFileDocx, «»,
«», SW_NORMAL)

  
CLEAR DLLS

  
IF
lnRetCode > 32

    
lbResult = .T.

  
ELSE

    
ERROR
«ShellExecute: errorcode = » +
LTRIM(STR(lnRetCode))

  
ENDIF
ENDIF
RETURN
lbResult

Файл: xml2docx.prg из проекта FillDocxByUseFramework_20.pjx

При поверхностном сравнении кода приведённых выше файлов
xml2docx.prg
и xml2docx.cs,
и если не принимать во внимание различия синтаксиса используемых языков, то может показаться, что
отличия в коде минимальны… :-) Однако, посмотрев внимательнее, можно легко
заметить, что никакого прямого обращения к классам MS
Framework
конечно же нет, а используются классы, определённый в файле
FrameworkWrap.prg и образующие т.н. «покрытия»
классов из MS Framework.

Давайте теперь бегло заглянем в подкаталоге CommonTools в файл FrameworkWrap.prg
и там мы увидим, что базовым для любого MS Framework-класса
является вот такой класс:


*////////////////////////////////////////////////////////
*// baseFwWrap — base for any wrap to MS Framework class

DEFINE CLASS
baseFwWrap
as Session
  
oFwObj =
NULL
  
cClassName = «»
  
PROCEDURE Init
    IF
!this.IsBridgeObject()
      
ERROR
«Not found instance of » + BRIDGE_CLASS + »
object»
    
ENDIF
    IF
!this.IsClassObject()
        
ERROR
«Not define «+
this.Name
+ «.cClassName property»
    
ENDIF
  ENDPROC
  PROCEDURE
IsBridgeObject
    
RETURN
(TYPE(‘poBridge’)
= ‘O’ AND !
ISNULL(poBridge))
  
ENDPROC
  PROCEDURE
IsClassObject
    
RETURN
!EMPTY(this.cClassName)
  
ENDPROC
  PROCEDURE
IsFrameworkObject
    
RETURN
(VARTYPE(this.oFwObj)
= ‘O’)
  
ENDPROC
  PROCEDURE
ToString
    
IF
!this.IsBridgeObject()
      
ERROR
«Not found instance of » + BRIDGE_CLASS + »
object» ;
        + » in » +
this.Name
+ ‘.toString()’
    
ENDIF
    LOCAL
lcRetVal
as String
    
lcRetVal = poBridge.InvokeMethod(this.oFwObj,
‘ToString’)
    
IF
!EMPTY(poBridge.cErrorMsg)
      
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.ToString()’)
    
ENDIF
    RETURN
lcRetVal
  
ENDPROC
  PROCEDURE
GetOwnerFwObject
    
RETURN this.oFwObj
  
ENDPROC
  PROCEDURE Destroy
    this
.oFwObj =
NULL
  ENDPROC
  
*// Insert your code
here for implement any other PEMs for this class…

ENDDEFINE

Класс: baseFwWrap

Как видим, пока нет ничего интересного. В классе определены два свойства:
oFwObj и cClassName и судя
по названию метода GetOwnerFwObject(), возвращающего значение свойства
this.oFwObj, последнее и есть истинная ссылка на
экземпляр MS Framework-класса, которая разрушается в
событии Destroy() данного класса. Свойство
cClassName
предназначено для хранения полного названия
Framework-
класса. В классе имеется ряд самоочевидных предикатов,
контролирующих типы некоторых переменных/свойств, и один единственный сколько-то
интересный метод ToString(). В нём, после удачного
контроля на существование приватной переменной poBridge
(ссылки на экземпляр класса DotNetBridge, о чём ниже),
через последнюю вызывается метод InvokeMethod() для
объекта this.oFwObj и названия метода
ToString. Результат выполнения метода
poBridge.InvokeMethod() собственно и возвращается в
качестве результата выполнения метода ToString()
данного класса. По такой же схеме будут работать и любые другие методы/свойства
классов-покрытий из файла FrameworkWrap.prg, т.е.
после формирования значений ряда параметров, через ссылку
poBridge
будет вызываться один из методов класса
DotNetBridge, в большинстве случаев, это будет одна из
разновидностей методов DotNetBridge.Invoke…().
Давайте на примерах кода нескольких классов, посмотрим как это реально делается.

Ну вот, посмотрим на код совершенно «стандартного» в этом смысле класса
System.Xml.Xsl.XsltArgumentList:


*///////////////////////////////////////////////////////////////
*// XsltArgumentList — wrap to System.Xml.Xsl.XsltArgumentList

DEFINE CLASS
XsltArgumentList
as
baseFwWrap
  cClassName = «System.Xml.Xsl.XsltArgumentList»
  
PROCEDURE Init
    IF
!DODEFAULT()
      
RETURN
.F.
    
ENDIF
    this
.oFwObj =
poBridge.CreateInstance(
this.cClassName)
    
IF VARTYPE(this.oFwObj)
# ‘O’
      
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.Init()’)
    
ENDIF
  ENDPROC
  PROCEDURE
AddParam
    
LPARAMETERS
tcParamName, tcNamespaceUri, toParamValue
    
IF
!this.IsBridgeObject()
      
ERROR
«Not found instance of » + BRIDGE_CLASS + »
object» ;
        + » in » +
this.Name
+ ‘.AddParam()’
  
  ENDIF
    
poBridge.InvokeMethod(this.oFwObj,
‘AddParam’, tcParamName, tcNamespaceUri, toParamValue)
    
IF
!EMPTY(poBridge.cErrorMsg)
      
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.AddParam()’)
    
ENDIF
  ENDPROC

  *// Insert your code
here for implement any other PEMs for this class…

ENDDEFINE

Класс: XsltArgumentList

Как видим, в событии Init():

  • VFP событие Init() грубо говоря, подобно
    конструктору класса в других языках, вызывается лишь однажды при создании
    экземпляра объекта) делается явный вызов baseFwWrap.Init()
    (через спец. VFP-функцию
    DODEFAULT()
    ), при неудаче, событие
    Init() данного класса возвращает
    .F., что в свою очередь для клиента означает, что
    экземпляр объекта не создаётся и возникает ошибка создания объекта класса.
  • Далее, делается попытка через метод
    poBridge.CreateInstance()
    создать экземпляр соответствующего
    MS Framework-класса, поместив ссылку на него в свойство
    this.oFwObj. Если при этом свойство не получило в
    качестве значения экземпляр объекта (VARTYPE(this.oFwObj)
    # ‘O’
    ), то в свойстве poBridge.cErrorMsg
    должны быть подробности о возникшей ошибке. В такой ситуации прекращаем
    выполнение, выдав клиенту соответствующее сообщение об ошибке. Если ссылка
    на экземпляр объекта получена, т.е. ошибок не возникло, то «ничего не
    делаем». В этом случае, событие Init() вернёт
    .T. и клиент получит ссылку на экземпляр объекта
    данного класса.

В методе AddParam():

  • Проверяется наличие переменной-ссылки poBridge,
    при отсутствии выдаётся сообщение об ошибке и метод завершается.
  • При наличии переменной ссылки poBridge,
    делается попытка обращения к её методу InvokeMetod()
    для объекта из свойства класса this.oFwObj,
    названия метода AddParam и с пришедшими на входе к
    данному методу тремя параметрами. Если по завершению выполнения метода
    свойство poBridge.cErrorMsg окажется не пустым,
    т.е. возникла ошибка, то формируем соответствующее сообщение об ошибке
    клиенту.

Если посмотреть описание класса System.Xml.Xsl.XsltArgumentList в
MSDN, то обнаружим, что мной реализована только
небольшая часть того, что может делать этот класс. Применительно к решаемой мной
задачи написанного оказалось достаточно, а если в решаемой Вами задачи потребуется
большая функциональность, то что же?… Ну допишите соответствующий код в
данный класс так, чтобы он удовлетворил все Ваши потребности… :-)

Не следует думать, что всё так гладко, как это может показаться… :-) Ну мол что
достаточно выполнить рутинную работу по наполнению методов/свойств
классов-покрытий, переадресовав вызовы на соответствующие
poBridge.Invoke…()
, и таким образом поиметь
доступ из-под VFP к любому требуемому для Вашей задачи подмножеству
классов библиотеки MS Framework. Не всё так уж
безоблачно на этом пути… :-(

Например, взяв код у Rick Strahl по указанной выше
ссылке, и при попытке использовать его в задаче, изложенной в Как получить таблицу данных из xlsx-файла у приложения Excel из MS Office 2007 используя структуру данных формата Open XML? я быстро наткнулся на проблему
вызова перегруженных конструкторов/методов, имеющих одинаковое количество
параметров, но отличающихся их типами. Чтобы попытаться устранить эту проблемы,
пришлось несколько дополнить код, взятый с первоисточника. Т.е. обратите
внимание, что код, прилагаемый к этой статье, не будет работать, если попытаться
использовать dll-файлы от Rick
Strahl
, а нужны именно dll-ки с небольшими
добавлениям из файла xlsxtbl.zip со страницы Примеры кода и утилиты.

Давайте на примере кода из файла FrameworkWrap.prg посмотрим, как можно использовать эти дополнительные
возможности, так в определении класса XmlTextWriter видим:


*////////////////////////////////////////////////////////////////////////
*// XmlTextWriter — wrap to System.Xml.XmlTextWriter

DEFINE CLASS
XmlTextWriter
as
baseFwWrap
  cClassName = «System.Xml.XmlTextWriter»
  
PROCEDURE Init
      LPARAMETERS
tuArg1, tuArg2
    
IF
!DODEFAULT()
      
RETURN
.F.
    
ENDIF
    LOCAL
lnCntArgs

as Integer

    
lnCntArgs =

PCOUNT()
    
DO CASE

    CASE
lnCntArgs = 1
      
this.oFwObj
=
poBridge.CreateInstance(
this.cClassName,
tuArg1)
    
CASE

lnCntArgs = 2
      
LOCAL

loConstr
as Object,
lcArg1Type

as String

      
lcArg1Type = tuArg1.ToString()
      loConstr = poBridge.GetConstructor(
this.cClassName,
lcArg1Type, «System.Text.Encoding»)
      
IF VARTYPE(loConstr)
# ‘O’
        
ERROR

(poBridge.cErrorMsg + » In » +

this.Name

+ ‘.Init()’)
      
ENDIF

      this
.oFwObj =
poBridge.CreateInstanceObject(loConstr, tuArg1, tuArg2)
    
OTHERWISE

      ERROR
(«Error number of args.» + »
In » +

this
.Name

+ ‘.Init()’)
    
ENDCASE

    IF VARTYPE
(this.oFwObj)
# ‘O’
      
ERROR

(poBridge.cErrorMsg + » In » +

this.Name

+ ‘.Init()’)
    
ENDIF

  ENDPROC
  PROCEDURE
Flush
    
IF
!this.IsBridgeObject()
      
ERROR
«Not found instance of » + BRIDGE_CLASS + »
object» ;
        + » in » +
this.Name
+ ‘.Flush()’
    
ENDIF
    
poBridge.InvokeMethod(this.oFwObj,
‘Flush’)
    
IF
!EMPTY(poBridge.cErrorMsg)
      
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.Flush()’)
    
ENDIF
  ENDPROC
  PROCEDURE Close
    IF
!this.IsBridgeObject()
      
ERROR
«Not found instance of » + BRIDGE_CLASS + »
object» ;
        + » in » +
this.Name
+ ‘.Close()’
    
ENDIF
    
poBridge.InvokeMethod(this.oFwObj,
‘Close’)
    
IF
!EMPTY(poBridge.cErrorMsg)
      
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.Close()’)
    
ENDIF
  ENDPROC
  PROCEDURE Destroy
    IF
!this.IsBridgeObject()
      
ERROR
«Not found instance of » + BRIDGE_CLASS + »
object» ;
      + » in » +
this.Name
+ ‘.Destroy()’
    
ENDIF
    
poBridge.InvokeMethod(this.oFwObj,
‘Close’)
    
DODEFAULT()
  
ENDPROC
*// Insert your code
here for implement any other PEMs for this class…

ENDDEFINE

Здесь в событии Init() (аналоге перегруженного
конструктора в обычных языках программирования) с помощью дописанных мной
методов: GetConstructor() и CreateInstanceObject()
в классе DotNetBridge
устраняется
проблема: какой именно конструктор у MS Framework-класса
System.Xml.XmlTextWriter необходимо вызвать, в
зависимости от типов входных параметров. Так, в выделенном цветом фона участке
кода, с помощью VFP-функции
PCOUNT(), предварительно определяется действительное
количество входных параметров, использованных при создании экземпляра класса и
далее, в зависимости от их количества, производится:

  • либо прямое обращение к
    poBridge.CreateInstance(), если на входе был один
    параметр, т.е. отсутствовала неоднозначность по типам параметров,
  • либо производится предварительное получение типа первого параметра (в
    переменную lcArg1Type), в
    зависимости от этого, с помощью метода GetConstructor(), создаётся экземпляр объекта
    System.Reflection.ConstructorInfo и только затем, с помощью
    CreateInstanceObject(), создаётся экземпляр класса
    для однозначно предопределённого конструктора, именно для этого случая
    создания экземпляра объекта.

После таких пояснений, видимо понятным будет код класса
Uri
:


*/////////////////////////////////////////////////////
*// Uri — wrap to System.Uri

DEFINE CLASS
Uri
as
baseFwWrap
  cClassName = «System.Uri»
  
PROCEDURE Init
      LPARAMETERS
tuUrl, tuArg2, tuArg3
    
IF
!DODEFAULT()
      
RETURN
.F.
    
ENDIF
    LOCAL
lnCntArgs
as Integer
    
lnCntArgs =
PCOUNT()
    
DO CASE
    CASE
lnCntArgs = 1
      
this.oFwObj
= poBridge.CreateInstance(
this.oConstr,
tuUrl)
    
CASE
lnCntArgs = 2
      
LOCAL
lcTypeArg1
as String;
        ,lcTypeArg2
as
String
;
        ,loConstr
as Object

      
lcTypeArg1 =
VARTYPE(tuUrl)
      lcTypeArg2 =
VARTYPE(tuArg2)
      
DO CASE
      CASE
lcTypeArg1 = ‘C’ AND lcTypeArg2
= ‘L’
        loConstr = poBridge.GetConstructor(
this.cClassName,
«System.String», «System.Boolean»)
      
CASE
lcTypeArg1 = ‘C’ AND lcTypeArg2 = ‘N’
        loConstr = poBridge.GetConstructor(
this.cClassName,
«System.String», «System.UriKind»)
      
CASE
lcTypeArg1 = ‘O’ AND lcTypeArg2 = ‘C’
        loConstr = poBridge.GetConstructor(
this.cClassName,
«System.Uri», «System.String»)
      
CASE
lcTypeArg1 = ‘O’ AND lcTypeArg2 = ‘O’
        loConstr = poBridge.GetConstructor(
this.cClassName,
«System.Uri», «System.Uri»)
      
OTHERWISE
        ERROR
(«Error type of args. In » +
this.Name
+ ‘.Init()’)
      
ENDCASE
      IF VARTYPE
(loConstr) # ‘O’
        
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.Init()’)
      
ENDIF
      this
.oFwObj =
poBridge.CreateInstanceObject(loConstr, tuUrl, tuArg2)
    
CASE
lnCntArgs = 3
      
LOCAL
lcTypeArg1
as String;
        ,lcTypeArg2
as
String
;
        ,lcTypeArg3
as
String
      
lcTypeArg1 =
VARTYPE(tuUrl)
      lcTypeArg2 =
VARTYPE(tuArg2)
      lcTypeArg3 =
VARTYPE(tuArg3)
      
IF
lcTypeArg1 = ‘O’ AND lcTypeArg2 = ‘C’ AND
lcTypeArg3 = ‘L’
        
LOCAL
loConstr
as Object
        
loConstr = poBridge.GetConstructor(this.cClassName,
«System.Uri», «System.String», «System.Boolean»)
        
IF VARTYPE(loConstr)
# ‘O’
          
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.Init()’)
        
ENDIF
        this
.oFwObj =
poBridge.CreateInstanceObject(loConstr, tuUrl, tuArg2, tuArg3)
      
ELSE
        ERROR
(«Error type of args. In » +
this.Name
+ ‘.Init()’)
      
ENDIF
    ENDCASE
    IF VARTYPE
(this.oFwObj)
# ‘O’
      
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.Init()’)
    
ENDIF
  ENDPROC
  PROCEDURE Destroy
    DODEFAULT
()
  
ENDPROC

  *// Insert your code
here for implement any other PEMs for this class…

ENDDEFINE

Здесь выбор конструктора для MS Framework-класса
производится на основе анализа VFP-типов параметров, а
не типов Framework-классов для параметров, как это
было в примере выше.

Можно ещё в качестве примера использования мной дополненной функциональности
по вызову перегруженных методов привести сюда код класса PackagePart:


*/////////////////////////////////////////////////////
*// Package — wrap to System.IO.Packaging.PackagePart

DEFINE CLASS
PackagePart
as
baseFwWrap
  cClassName = «System.IO.Packaging.PackagePart»
  oMethodGetStream2 =

NULL
  PROCEDURE Init
      LPARAMETERS
toPackagePart
    
IF
!DODEFAULT()
      
RETURN
.F.
    
ENDIF
    this
.oFwObj = toPackagePart
    
IF VARTYPE(this.oFwObj)
# ‘O’
      
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.Init()’)
    
ENDIF
  ENDPROC
  PROCEDURE
GetStream
      
LPARAMETERS
tnFileMode, tnFileAccess
    
IF
!this.IsBridgeObject()
      
ERROR
«Not found instance of » + BRIDGE_CLASS + »
object» ;
        + » in » +
this.Name
+ ‘.GetStream()’
    
ENDIF
    LOCAL
loRetVal
as Object;
      ,lnCntArgs
as
Integer
    
lnCntArgs =
PCOUNT()
    
DO CASE
    CASE
lnCntArgs = 0
      loRetVal = poBridge.InvokeMethod(
this.oFwObj,
«GetStream»)
    
CASE
lnCntArgs = 1
      loRetVal = poBridge.InvokeMethod(
this.oFwObj,
«GetStream», tnFileMode)
    
CASE

lnCntArgs = 2
      
IF VARTYPE(this.oMethodGetStream2)
# ‘O’
        
this.oMethodGetStream2
= poBridge.GetMethod(
this.oFwObj,
«GetStream»;
          ,»System.IO.FileMode», «System.IO.FileAccess»)
        
IF VARTYPE(this.oMethodGetStream2)
# ‘O’
          
ERROR

(poBridge.cErrorMsg + » In » +

this.Name

+ ‘.GetStream()’)
        
ENDIF

      ENDIF

      
loRetVal =
poBridge.InvokeMethodObject(
this.oFwObj,

this.oMethodGetStream2;
        ,tnFileMode, tnFileAccess)
    
OTHERWISE
      ERROR
(«Error number of args.» + »
In » +
this.Name
+ ‘.Init()’)
    
ENDCASE
    IF VARTYPE
(loRetVal) # ‘O’
      
ERROR
(poBridge.cErrorMsg + » In » +
this.Name
+ ‘.GetStream()’)
    
ENDIF
    RETURN
loRetVal

  ENDPROC
  PROCEDURE Destroy
    this
.oMethodGetStream2
=
NULL
    DODEFAULT
()
  
ENDPROC
  
*// Insert your code
here for implement any other PEMs for this class…

ENDDEFINE

Здесь в методе GetStream(), в случае двух параметров, использованы методы: GetMethod()
и InvokeMethodObject() из класса
DotNetBridge
для получения объекта класса System.Reflection.MethodInfo и вызова метода,
соответствующего типам параметров.

Всё ли гладко на этом пути? Да я бы не сказал так… :-( Конечно, можно многое
сделать, но проблемы остаются. Например, я не смог вызвать метод
Stream.Seek() у объекта, полученного от
PackagePart.GetStream() как не старался… :-(
Пришлось переустанавливать указатель потока через его свойство
Stream.Position. Мне не удалось также воспользоваться методом
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
подозреваю, что для этого снова нужно править C#-код в
классе DotNetBredge… Но в целом, подход всё-таки
работает! :-)

Построение XSLT-преобразования
для извлечения xml-данных из таблицы в MS Word 2007 документе.

Попробуем теперь решить обратную задачу: извлечь из MS
Word 2007
документа, содержащего таблицу с данными, сами эти табличные
данные в xml-формате. Для этого нам потребуется
XSLT-преобразование над данными в /word/document.xml,
позволившее бы нам это сделать. При этом надо иметь ввиду, что некоторые типы
данных, например тип даты, необходимо преобразовать из обычного представления
(того, что в документе) в
формат, удовлетворяющий требованиям xml. Для нашей
таблички данных я это проделал и вот какой код у  меня при этом получился:


<?
xml
version=«1.0«
encoding=«utf-8«?>
<!—
File: genXmlFromDocument.xslt
—>
<
xsl:stylesheet
version=«1.0«
    
xmlns:xsl=«http://www.w3.org/1999/XSL/Transform«
    
xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance«
    
xmlns:msxsl=«urn:schemas-microsoft-com:xslt«
    
xmlns:msdata=«urn:schemas-microsoft-com:xml-msdata«
    
xmlns:xsd=«http://www.w3.org/2001/XMLSchema«
    
xmlns:ve=«http://schemas.openxmlformats.org/markup-compatibility/2006«
    
xmlns:o=«urn:schemas-microsoft-com:office:office«
    
xmlns:r=«http://schemas.openxmlformats.org/officeDocument/2006/relationships«
    
xmlns:m=«http://schemas.openxmlformats.org/officeDocument/2006/math«
    
xmlns:v=«urn:schemas-microsoft-com:vml«
    
xmlns:wp=«http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing«
    
xmlns:w10=«urn:schemas-microsoft-com:office:word«
    
xmlns:w=«http://schemas.openxmlformats.org/wordprocessingml/2006/main«
    
xmlns:wne=«http://schemas.microsoft.com/office/word/2006/wordml«
    
exclude-result-prefixes=«xsi
msxsl msdata xsd ve o r m v wp w10 w wne
«>  <xsl:output
method=«xml«
version=«1.0«
    
encoding=«utf-8«
omit-xml-declaration=«yes«
/>  <!— Current encoding to replace in output
xml-PI
—>
  <
xsl:param
name=«prmEncoding«>
    <
xsl:text>utf-8</xsl:text>
  </
xsl:param>

  <!—

Schema name —>
  <xsl:param
name=«prmSchemaUri«>
    <
xsl:text>Schema_Employee</xsl:text>
  </
xsl:param>

  <!—

xsd:schema as first child element in
output
—>
  <xsl:param
name=«prmDocXsd«
/>  <!—
Get attribute w:element form
request w:customXml element in input
—>
  <
xsl:variable
name=«varRoot«>
    <
xsl:value-of
select=«/./w:document/w:body//w:customXml[@w:uri=$prmSchemaUri]/@w:element«/>
  </
xsl:variable>  <xsl:template
match=«/«>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[<?xml
version=»1.0″ encoding=»
]]></xsl:text>
    <
xsl:value-of
select=«$prmEncoding«
/>
    <
xsl:text
disable-output-escaping=«yes«><![CDATA[»
standalone=»yes»?>
]]> </xsl:text>
    <
xsl:choose>
      <
xsl:when
test=«$varRoot«>
        <!—
Generate root element into output
—>
        <
xsl:element
name=«{$varRoot}«>
          <
xsl:if
test=«$prmDocXsd«>
            <!—
Insert schema element into output
—>
            <
xsl:copy-of
select=«$prmDocXsd«/>
          </
xsl:if>
          <!—
… continue parsing for childrens
of request w:customXml element …
—>
          <
xsl:apply-templates
select=«/./w:document/w:body//w:customXml[@w:uri=$prmSchemaUri]/*«
/>
        </
xsl:element>
      </
xsl:when>
      <
xsl:otherwise>
        <!—
Error: request element w:customXml
not found
—>
        <
xsl:element
name=«{concat($prmSchemaUri,
‘_Not_Found’)}
« />
      </
xsl:otherwise>
    </
xsl:choose>
  </
xsl:template>

  <!—

Parsing any current input…
—>
  <
xsl:template
match=«@*|node()«>
    <
xsl:variable
name=«varName«>
      <
xsl:value-of
select=«name()«
/>
    </
xsl:variable>
    <
xsl:choose>
      <
xsl:when
test=«string-length($varName)>0«>
        <
xsl:choose>
          <
xsl:when
test=«$varName=’w:customXml’«>
            <!—
Parsing
<w:customXml…>…</w:customXml>
—>
            <
xsl:variable
name=«varElementName«>
              <
xsl:value-of
select=«@w:element«
/>
            </
xsl:variable>
            <
xsl:variable
name=«varChildName«>
              <
xsl:value-of
select=«name(child::*[1])«/>
            </
xsl:variable>
            <!—
Open <{@w:element}…>…
—>
            <
xsl:element
name=«{$varElementName}«>
              <
xsl:choose>
                <
xsl:when
test=«$varElementName=$varRoot
or $varChildName=’w:tr’
«>
                  <!—
… for any group: continue
parsing for child & as child …
—>
                  <
xsl:apply-templates
select=«@*|node()«
/>
                </
xsl:when>
                <
xsl:otherwise>
                  <!—
… get xml-value for current
w:customXml element
—>
                  <
xsl:call-template
name=«getXmlValue«>
                    <
xsl:with-param
name=«prmElementName«
select=«$varElementName«
/>
                    <
xsl:with-param
name=«prmValue«
select=«normalize-space(string(child::*))«
/>
                  </
xsl:call-template>
                </
xsl:otherwise>

              </
xsl:choose>
              <!—
Close … </{@w:element}>
—>
            </
xsl:element>
          </
xsl:when>
          <
xsl:otherwise>
            <
xsl:apply-templates
select=«@*|node()«
/>
          </
xsl:otherwise>

        </
xsl:choose>
      </
xsl:when>
      <
xsl:otherwise>
        <
xsl:apply-templates
select=«@*|node()«
/>
      </
xsl:otherwise>
    </
xsl:choose>
  </
xsl:template>

  <!—

Generate value of w:customXml element
—>
  <xsl:template
name=«getXmlValue«>
    <xsl:param
name=«prmElementName«
/>
    <xsl:param
name=«prmValue«
/>
    <xsl:choose>
      <xsl:when
test=«$prmDocXsd«>
        <!— Try get element type from xsd-schema…
—>
        <xsl:variable
name=«varType«>
          <xsl:choose>
            <xsl:when
test=«msxsl:node-set($prmDocXsd)/.//xsd:element[@name=$prmElementName]/@type«>
              <xsl:value-of
select=«msxsl:node-set($prmDocXsd)/.//xsd:element[@name=$prmElementName]/@type«/>
            </xsl:when>
            <xsl:when
test=«msxsl:node-set($prmDocXsd)/.//xsd:element[@name=$prmElementName]/xsd:simpleType/xsd:restriction/@base«>
              <xsl:value-of
select=«msxsl:node-set($prmDocXsd)/.//xsd:element[@name=$prmElementName]/xsd:simpleType/xsd:restriction/@base«/>
            </xsl:when>
            <!— … insert your code here for any other
types…
—>
            <xsl:otherwise>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
        <!— Convert current value according to
element type…
—>
        <xsl:choose>
          <xsl:when
test=«$varType=’xsd:date’«>
            <!— … for date value: convert element value
to xml-date format into ouptut
—>
            <xsl:value-of
select=«concat(substring-after(substring-after($prmValue,
‘.’), ‘.’), ‘-‘, substring-before(substring-after($prmValue, ‘.’), ‘.’), ‘-‘,
substring-before($prmValue, ‘.’))« />
          </xsl:when>
          <!— … insert your code here for any other
types…
—>
          <xsl:otherwise>
            <!— … for otherwise case: simple copy
current value into ouptut
—>
            <xsl:value-of
select=«$prmValue«
/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <!— … for otherwise case: simple copy
current value into ouptut
—>
        <xsl:value-of
select=«$prmValue«
/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</

xsl:stylesheet>

Файл: genXmlFromDocument.xslt

C#-код приложения для
извлечения xml-данных из таблицы в MS Word 2007 документе. top

Чтобы применить приведённое выше XSLT-преобразование
из файла genXmlFromDocument.xslt
к содержанию нашего MS Word 2007 документа, потребуется ряд
действий. Код консольного C#-приложения,
выполняющего такие действия, мог бы быть следующим:


#define
RUS_LANG
using
System;

using
System.Collections.Generic;

using
System.Text;
using
Microsoft.Win32;

using
System.Xml;

using
System.Xml.Xsl;

using
System.IO;

using
System.IO.Packaging;
namespace
docx2xml
{

  class
docx2xml

  {
    
const
string PATH_DOCX =
@»……..DOCX»;
    
const
string PATH_XSLT =
@»……..XSLT»;
    
const
string PATH_XML =
@»……..XML»;
    
const
string XSLT_FILE =
«genXmlFromDocument.xslt»;
    
const
string XML_OUTPUT_FILE =
«documentData.xml»;
    
const
string URI_SETTINGS_XML =
«/word/settings.xml»;
    
const
string URI_DOCUMENT_XML =
«/word/document.xml»;
    
const
string KEY_SCHEMA_LIBRARY =
@»SoftwareMicrosoftSchema Library»;
    
const
string LOCATION_NAME_VALUE
=
«Location»;
    
const
string USE_ENCODING =
«utf-8»;
    
const
string XML_PI =
«<?xml version=»1.0″ encoding=»»
+ USE_ENCODING +
«»
standalone=»yes»?>rn»
;

#if
RUS_LANG
    
const
string TEMPLATE_DOCX =
«Пример VFP-таблицы в виде MS Word 2007.docx»;
    
const
string ERR_LABEL_OUTPUT =
«*** Возникла ошибка: «;
    
const
string ERR_DIR_NOTFOUND =
«Не найден каталог: «;
    
const
string ERR_FILE_NOTFOUND =
«Не найден файл: «;
    
const
string ERR_EMPTY_XML =
«Нет xml-данных для: «;
    
const
string ERR_EMPTY_XSLTRES =
«Результат xslt-преобразований пуст для: «;
    
const
string ERR_CALL_GENDOCX =
«Выполните xml2docx.ex для создания этого
документа.»
;
    
const
string MSG_PRESS_ANY_KEY =
«Нажмите любую клавишу для продолжения…»;
    
const
string MSG_SUCCESS_RESUT =
«Процесс успешно завершён!»;

#else

    
const string TEMPLATE_DOCX = «Example VFP-table in MS Word 2007.docx»;

    
const string ERR_LABEL_OUTPUT = «*** Error appears: «;

    
const string ERR_DIR_NOTFOUND = «Not found directory: «;

    
const string ERR_FILE_NOTFOUND = «Not found file: «;

    
const string ERR_EMPTY_XML = «Empty xml-data for: «;

    
const string ERR_EMPTY_XSLTRES = «Empty result of xslt-transformation for: «;

    
const string ERR_CALL_GENDOCX = «Call xml2docx.ex to generating this
document.»;

    
const string MSG_PRESS_ANY_KEY = «Press any key to continue…»;

    
const string MSG_SUCCESS_RESUT = «Process was successfully completed!»;

#endif

    
static

void Main(string[]
args)
    {
      
RegistryKey
rkDocxAsValue =
null;

      
RegistryKey
rkDocxAttachedSchema =
null;
      
RegistryKey
rkSchemaLibrary =
null;
      
RegistryKey
rkCurrentUser =
null;
      
bool bIsResult =
false;try
      
{
      
  ///////////////////////////////////////////////////////////////////////
        // Check existence of external files
        
if (!Directory.Exists(PATH_XML))
        {
          
throw (new
DirectoryNotFoundException(ERR_DIR_NOTFOUND
+
Path.GetFullPath(PATH_XML)));
        }
        
if (!Directory.Exists(PATH_DOCX))
        {
          
throw (new
DirectoryNotFoundException(ERR_DIR_NOTFOUND
+
Path.GetFullPath(PATH_DOCX)));
        }
        
if (!Directory.Exists(PATH_XSLT))
        {
          
throw (new
DirectoryNotFoundException(ERR_DIR_NOTFOUND
+
Path.GetFullPath(PATH_XSLT)));
        }        
///////////////////////////////////////////////////////////////////////
        // Get docx-file from Docx folder
        
string DocxFolder
=
Environment.GetFolderPath(Environment.SpecialFolder.Personal);
        
string FileDocx =
DocxFolder +
@»_» +
TEMPLATE_DOCX;
        
if (!File.Exists(FileDocx))
        {
          
throw (new
FileNotFoundException(ERR_FILE_NOTFOUND
+ FileDocx +
«. « +
ERR_CALL_GENDOCX));
        }
        
string FileXslt =
Path.GetFullPath(PATH_XSLT
+ XSLT_FILE);
        
if (!File.Exists(FileXslt))
        {
          
throw (new
FileNotFoundException(ERR_FILE_NOTFOUND
+ FileXslt));
        }        
Encoding enc =
Encoding.GetEncoding(USE_ENCODING);
        
Uri
uriWordSettingsXml =
new
Uri(URI_SETTINGS_XML,
UriKind.Relative);
        
Uri
uriWordDocumentXml =
new
Uri(URI_DOCUMENT_XML,
UriKind.Relative);
        
string strXmlDoc
= System.
String.Empty;
        
using (Package
package =
Package.Open(FileDocx,
FileMode.Open))
        {
          
string
DocxAttachedSchema = System.
String.Empty;          ///////////////////////////////////////////////////////////////////////
          // Get PackagePart of template for
URI_SETTINGS_XML(/word/settings.xml)
          
PackagePart
packagePart = package.GetPart(uriWordSettingsXml);
          
// … and its stream in Read mode
          
using (Stream
ppStrmSettingsXml = packagePart.GetStream(
FileMode.Open,
FileAccess.Read))
          {
            
///////////////////////////////////////////////////////////////////////
            // Load stream as xml-document
            
XmlDocument
xmlDocSettings =
new
XmlDocument();
            xmlDocSettings.Load(ppStrmSettingsXml);
            
XmlElement xeRoot
= xmlDocSettings.DocumentElement;
            
string nsp_w =
xeRoot.GetNamespaceOfPrefix(
«w»);

            
XmlNamespaceManager
nsMngr =
new
XmlNamespaceManager(xmlDocSettings.NameTable);
            
if (nsp_w !=
null && nsp_w.Length
> 0)
            {
              nsMngr.AddNamespace(
«w»,
nsp_w);
            }            
///////////////////////////////////////////////////////////////////////
            // Get AttachedSchema name
            
XmlNode
xnAttachedSchema = xeRoot.SelectSingleNode(
«w:attachedSchema»,
nsMngr);
            
if (xnAttachedSchema
!=
null &&
xnAttachedSchema.Attributes.Count > 0)
            {
              
XmlAttribute
xaVal = xnAttachedSchema.Attributes[
«w:val»];
              
if (xaVal !=
null)
              {
                DocxAttachedSchema = xaVal.Value;
              }
            }
            ppStrmSettingsXml.Close();
          }
          
string
PathSchemaLocation = System.
String.Empty;
          
if (DocxAttachedSchema.Length
> 0)
          {
            
///////////////////////////////////////////////////////////////////////////////////////////
            // Get Location for XSD-schema from
Microsoft Schema Library in system registry
            
rkCurrentUser = Registry.CurrentUser;
            
// Get RegistryKey: Microsoft Schema
Library
            
rkSchemaLibrary = rkCurrentUser.OpenSubKey(KEY_SCHEMA_LIBRARY,
false);
            
// Get RegistryKey: AttachedSchema by
name
            
rkDocxAttachedSchema = rkSchemaLibrary.OpenSubKey(DocxAttachedSchema,
false);
            
// Get RegistryKey: first child of
AttachedSchema
            
string[]
asSubKeys = rkDocxAttachedSchema.GetSubKeyNames();
            rkDocxAsValue = rkDocxAttachedSchema.OpenSubKey(asSubKeys[0],

false);
            
// Get Location
            
PathSchemaLocation = (string)rkDocxAsValue.GetValue(LOCATION_NAME_VALUE);            /////////////////////////////////////
            // Close all RegistryKeys
            
rkDocxAsValue.Close();
            rkDocxAsValue =
null;
            rkDocxAttachedSchema.Close();
            rkDocxAttachedSchema =
null;
            rkSchemaLibrary.Close();
            rkSchemaLibrary =
null;
            rkCurrentUser.Close();
            rkCurrentUser =
null;
          }          
///////////////////////////////////////////////////////////////////////////////////////
          // Get PackagePart of template for
URI_DOCUMENT_XML(/word/document.xml)
          
packagePart = package.GetPart(uriWordDocumentXml);
          
// … and its stream in Read mode
          
using (Stream
ppStrmDocumentXml = packagePart.GetStream(
FileMode.Open,
FileAccess.Read))
          {
            
///////////////////////////////////////////////////////////////////////
            // Load stream as xml-document
            
XmlDocument
xmlDocumentXml =
new
XmlDocument();
            xmlDocumentXml.Load(ppStrmDocumentXml);
            ///////////////////////////////////////////////////////////////////////////////////////
            // Load XSD-schema from file SchemaLocation
(as info from system registry)
            
XmlDocument
xmlDocXsd =
null;
            
if (File.Exists(PathSchemaLocation))
            {
              xmlDocXsd =
new
XmlDocument();
              xmlDocXsd.Load(PathSchemaLocation);
            }
            ///////////////////////////////////////////////////////////////////////////////
            // Prepare and execute the xslt-transformation

            // with document.xml and by using xslt-file
FileXslt
            
XslCompiledTransform
xsltXmlTransform =
new
XslCompiledTransform();
            
XsltArgumentList
xsltArgList =
new
XsltArgumentList();
            xsltArgList.AddParam(
«prmEncoding»,
«», USE_ENCODING);
            
if (DocxAttachedSchema.Length
> 0)
            {
              xsltArgList.AddParam(
«prmSchemaUri»,
«»,
DocxAttachedSchema);
            }
            
if (xmlDocXsd !=
null &&
xmlDocXsd.DocumentElement !=
null)
            {
              xsltArgList.AddParam(
«prmDocXsd»,
«»,
xmlDocXsd.DocumentElement.CreateNavigator());
            }
            xsltXmlTransform.Load(FileXslt);
            
using (MemoryStream
msXmlDoc =
new
MemoryStream())
            {
              
using (XmlTextWriter
xtwXmlDoc =
new
XmlTextWriter(msXmlDoc,
enc))
              {
                xsltXmlTransform.Transform(xmlDocumentXml.CreateNavigator(), xsltArgList,
xtwXmlDoc);
                strXmlDoc = enc.GetString(msXmlDoc.GetBuffer(), 0, (
int)msXmlDoc.Length);
                xtwXmlDoc.Close();
              }
              msXmlDoc.Close();
            }
            
int nPos =
strXmlDoc.IndexOf(
‘<‘);
            
if (nPos == (-1))
            {
              
throw (new
NullReferenceException(ERR_EMPTY_XSLTRES
+ FileXslt));
            }
            strXmlDoc = strXmlDoc.Substring(nPos);
            nPos = strXmlDoc.IndexOf(
«<?xml»);
            
if (nPos == (-1))
            {
              strXmlDoc = XML_PI + strXmlDoc;
            }
            ppStrmDocumentXml.Close();
          }
          package.Close();
        }        
//////////////////////////////////////
        // Save result
        
string
xmlOutputFile =
Path.GetFullPath(PATH_XML
+ XML_OUTPUT_FILE);
        
if (strXmlDoc.Length
> 0)
        {
          
if (File.Exists(xmlOutputFile))
          {
            
File.Delete(xmlOutputFile);

          }
          
XmlDocument
xmlDocOut =
new
XmlDocument();
          xmlDocOut.LoadXml(strXmlDoc);
          xmlDocOut.Save(xmlOutputFile);
          bIsResult =
File.Exists(xmlOutputFile);
        }
        
else
        
{
          
throw (new
FileNotFoundException(ERR_EMPTY_XML
+ xmlOutputFile));
        }
      }
      
catch (Exception
ex)
      {
        
Console.WriteLine(ERR_LABEL_OUTPUT
+ ex.ToString());
      }      
/////////////////////////////////////
      // Close all RegistryKeys
      
if (rkDocxAsValue
!=
null)
      {
        rkDocxAsValue.Close();
        rkDocxAsValue =
null;

      }
      
if (rkDocxAttachedSchema
!=
null)
      {
        rkDocxAttachedSchema.Close();
        rkDocxAttachedSchema =
null;
      }
      
if (rkSchemaLibrary
!=
null)
      {
        rkSchemaLibrary.Close();
        rkSchemaLibrary =
null;
      }
      
if (rkCurrentUser
!=
null)
      {
        rkCurrentUser.Close();
        rkCurrentUser =
null;
      }
      
if (bIsResult)
      {
        
Console.WriteLine(MSG_SUCCESS_RESUT);
      }
      
Console.WriteLine(MSG_PRESS_ANY_KEY);
      
Console.ReadKey();
    }
  }
}

Файл: docx2xml.cs

Здесь из части /word/settings.xml MS Word 2007 документа
выбирается значение атрибута w:val у элемента
w:attachedSchema, содержащее название
xsd-схемы в библиотеке схем для содержания данного документа
(в переменную DocxAttachedSchema). Далее,
в библиотеке схем под ключом HKEY_CURRENT_USERSoftwareMicrosoftSchema Library в системном реестре
по названию схемы (у подключа: Schema_Employee
в нашем случае) у значения Location определяется
место расположения файла xsd-схемы на локальном диске
(в переменную PathSchemaLocation). Затем выбирается
часть документа /word/document.xml, над которой
совершается xslt-преобразование из файла
genXmlFromDocument.xslt для получения
xml-данных содержания документа, а в качестве первого
дочернего элемента к корневому элементу добавляется корневой элемент из
xsd-файла-схемы, передаваемой как параметр
prmDocXsd, тем самым формируется так называемая
inline-схема внутри нашего xml-файла
результата. Наконец, если всё вышеописанное было выполнено без ошибок, то
формируется файл результата с названием: XML_OUTPUT_FILE(documentData.xml)
в подкаталоге XML.

Выполнив код из docx2xml.cs для нашего MS
Word 2007
документа в подкаталоге XML мной был
получен следующий результат:


<?
xml
version=«1.0«
encoding=«utf-8«
standalone=«yes«?>
<
VFPData>
  <
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
mixed=«true«>
        <
xsd:choice
maxOccurs=«unbounded«>
          <
xsd:element
name=«employee«
minOccurs=«0«
maxOccurs=«unbounded«>
            <
xsd:complexType
mixed=«true«>
              <
xsd:sequence>
                <
xsd:element
name=«lastname«>
                  <
xsd:simpleType>
                    <
xsd:restriction
base=«xsd:string«>
                      <
xsd:maxLength
value=«20«
/>
                    </
xsd:restriction>
                  </
xsd:simpleType>
                </
xsd:element>
                <
xsd:element
name=«firstname«>
                  <
xsd:simpleType>
                    <
xsd:restriction
base=«xsd:string«>
                      <
xsd:maxLength
value=«10«
/>
                    </
xsd:restriction>
                  </
xsd:simpleType>
                </
xsd:element>
                <
xsd:element
name=«birthdate«
type=«xsd:date«
minOccurs=«0«
/>
                <
xsd:element
name=«notes«
minOccurs=«0«>
                  <
xsd:simpleType>
                    <
xsd:restriction
base=«xsd:string«>
                      <
xsd:maxLength
value=«2147483647«
/>
                    </
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>
  <
employee>
    <
lastname>Buchanan</lastname>
    <
firstname>Steven</firstname>
    <
birthdate>1955-03-04</birthdate>
    <
notes>Steven
Buchanan graduated from St. Andrews University, …
</notes>
  </
employee>
  <
employee>
    <
lastname>Callahan</lastname>
    <
firstname>Laura</firstname>
    <
birthdate>1958-01-09</birthdate>
    <
notes>Laura
received a BA in psychology from the University …
</notes>
  </
employee>
  <
employee>
    <
lastname>Davolio</lastname>
    <
firstname>Nancy</firstname>
    <
birthdate>1968-12-08</birthdate>
    <
notes>Education
includes a BA in psychology from Colorado …
</notes>
  </
employee>
  <
employee>
    <
lastname>Dodsworth</lastname>
    <
firstname>Anne</firstname>
    <
birthdate>1969-07-02</birthdate>
    <
notes>Anne
has a BA degree in English from St. Lawrence College. …
</notes>
  </
employee>
  <
employee>
    <
lastname>Fuller</lastname>
    <
firstname>Andrew</firstname>
    <
birthdate>1952-02-19</birthdate>
    <
notes>Andrew
received his BTS commercial and a Ph.D. in international …
</notes>
  </
employee>
</
VFPData>

Файл: documentData.xml

В подкаталог XML примеров кода я также поместил
VFP-код CreateVfpCursorFromXml.prg,
результат выполнения которого показан на рисунке ниже:

docx14.jpg
Рис.14

Как видим, наша цель оказалась достигнутой! :-) Нами получены данные
исходного MS Word 2007 документа в виде таблицы в
xml-формате, причём используя VFP-класс
XMLAdapter, такие xml-данные
легко преобразуются в соответствующий VFP-курсор.

VFP-код приложения для
извлечения xml-данных из таблицы в MS Word 2007 документе.

Приведённый выше код из файла docx2xml.cs может быть переписан и для выполнения из-под
VFP. У меня это получилось так:


#
DEFINE
PATH_COMMON_TOOLS «….CommonTools»
#
INCLUDE
«….CommonToolsSolWestWindEx.h»
#
INCLUDE
«….CommonToolsFrameworkWrap.h»

#

DEFINE
RUS_LANG .T.
#
DEFINE
TMP_OUT .T.
#
DEFINE
CRLF
CHR(13)
+
CHR(10)
#
DEFINE
SW_NORMAL 1
#
DEFINE
PATH_XML «….XML»
#
DEFINE
PATH_DOCX «….DOCX»
#
DEFINE
PATH_XSLT «….XSLT»
#
DEFINE
XSLT_FILE «genXmlFromDocument.xslt»
#
DEFINE
XML_OUTPUT_FILE «documentData.xml»
#
DEFINE
XSLT_FILE_FIRST_RESULT «getXmlDocument.xslt»
#
DEFINE
XML_FILE_SECOND_RESULT «document.xml»
#
DEFINE
URI_SETTINGS_XML «/word/settings.xml»
#
DEFINE
URI_DOCUMENT_XML «/word/document.xml»
#
DEFINE
KEY_SCHEMA_LIBRARY «SoftwareMicrosoftSchema
Library»
#
DEFINE
LOCATION_NAME_VALUE «Location»
#
DEFINE
USE_ENCODING «utf-8»
#
DEFINE
XML_PI ‘<?xml version=»1.0″ encoding=»‘ +
USE_ENCODING + ‘» standalone=»yes»?>’ + CRLF
#
DEFINE
MYDOC_FOLDER «MyDocuments»
#
IF
RUS_LANG
  #
DEFINE
TEMPLATE_DOCX «Пример VFP-таблицы в виде MS
Word 2007.docx»
  #
DEFINE
ERR_VFP_VERSION «Извините, данный код
предназначен для VFP 8.0 (или выше).»
  #
DEFINE
ERR_LABEL_OUTPUT «*** Возникла ошибка: «
  #
DEFINE
ERR_DIR_NOTFOUND «Не найден каталог: «
  #
DEFINE
ERR_FILE_NOTFOUND «Не найден файл: «
  #
DEFINE
ERR_EMPTY_XML «Нет xml-данных для: «
  #
DEFINE
ERR_EMPTY_XSLTRES «Результат
xslt-преобразований пуст для: «
  #
DEFINE
MSG_SUCCESS_RESUT «Процесс успешно завершён!»
#
ELSE
  
#DEFINE
TEMPLATE_DOCX «Example VFP-table in MS
Word 2007.docx»
  #
DEFINE
ERR_VFP_VERSION «Sorry, this code for VFP 8.0
(or later).»
  #
DEFINE
ERR_LABEL_OUTPUT «*** Error appears: «
  #
DEFINE
ERR_DIR_NOTFOUND «Not found directory: «
  #
DEFINE
ERR_FILE_NOTFOUND «Not found file: «
  #
DEFINE
ERR_EMPTY_XML «Empty xml-data for: «
  #
DEFINE
ERR_EMPTY_XSLTRES «Empty result of
xslt-transformation for: «
  #
DEFINE
MSG_SUCCESS_RESUT «Process is successfully
completed!»
#
ENDIF

CLEAR
SET DEFAULT TO

(LEFT(SYS(16),
RAT(«»,
SYS(16))))

IF VERSION
(5) < 800
  ? ERR_VFP_VERSION
  
RETURN
.F.

ENDIF

PRIVATE

poBridge
&& as DotNetBridge OF
….CommonToolsDotNetBridge.prg && FULLPATH(PATH_COMMON_TOOLS +
«DotNetBridge.prg»)

LOCAL
loException
as Exception;
  ,lbError
as
Boolean;
  ,lbResult
as
Boolean
TRY
  
*///////////////////////////////////////////////////////////////////////
  *// Check existence of external files
  
IF
!DIRECTORY(PATH_COMMON_TOOLS)
    
ERROR
(ERR_DIR_NOTFOUND +
LOWER(FULLPATH(PATH_COMMON_TOOLS)))
  
ENDIF
  IF
!DIRECTORY(PATH_XML)
    
ERROR
(ERR_DIR_NOTFOUND +
LOWER(FULLPATH(PATH_XML)))
  
ENDIF
  IF
!DIRECTORY(PATH_DOCX)
    
ERROR
(ERR_DIR_NOTFOUND +
LOWER(FULLPATH(PATH_DOCX)))
  
ENDIF
  IF
!DIRECTORY(PATH_XSLT)
    
ERROR
(ERR_DIR_NOTFOUND +
LOWER(FULLPATH(PATH_XSLT)))
  
ENDIF
  LOCAL
lcFileXslt
as String
  
lcFileXslt =
LOWER(FULLPATH(PATH_XSLT))
+ XSLT_FILE
  
IF
!FILE(lcFileXslt)
    
ERROR
101, lcFileXslt
  
ENDIF  *///////////////////////////////////////////////////////////////////////
  *// Get docx-file from My Documents folder
  
LOCAL
loWSH
as
WScript.Shell
  
loWSH =
CREATEOBJECT(«WScript.Shell»)
  
LOCAL
lcDocxFolder
as String
  
lcDocxFolder =
loWSH.SpecialFolders(MYDOC_FOLDER)
  
LOCAL
lcFileDocx
as String
  
lcFileDocx = lcDocxFolder + «_» +
TEMPLATE_DOCX
  
IF
!FILE(lcFileDocx)
    
ERROR
101, lcFileDocx
  
ENDIF  *////////////////////////////////////////////////////////////////
  *// Create DotNetBridge object
  
LOCAL
lcBridgeFileProc
as String
  
lcBridgeFileProc =
LOWER(FULLPATH(PATH_COMMON_TOOLS
+ BRIDGE_PROC))
  
IF
!FILE(lcBridgeFileProc)
    
ERROR
101, lcBridgeFileProc
  
ENDIF
  IF
NOT (JUSTSTEM(lcBridgeFileProc)
$
LOWER(SET(«Procedure»)))
    
SET PROCEDURE TO
(lcBridgeFileProc)
ADDITIVE
  ENDIF
  
poBridge =
CREATEOBJECT(BRIDGE_CLASS)
  
IF VARTYPE(poBridge)
# ‘O’
    
ERROR
‘Can not create ‘ + BRIDGE_CLASS + ‘ object.’
  
ENDIF  *////////////////////////////////////////////////////////////////
  *// Set procedure to «FrameworkWrap.prg»
  
LOCAL
lcFrameworkWrapFileProc
as String
  
lcFrameworkWrapFileProc =
LOWER(FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»))
  
IF
!FILE(lcFrameworkWrapFileProc)
    
ERROR
101, lcFrameworkWrapFileProc
  
ENDIF
  IF
NOT (JUSTSTEM(lcFrameworkWrapFileProc)
$
LOWER(SET(«Procedure»)))

    
SET PROCEDURE TO
(lcFrameworkWrapFileProc)
ADDITIVE
  ENDIF  
*////////////////////////////////////////////////////////////////
  *// LoadAssembly ASSEMBLY_SYSTEM
  
IF
!poBridge.LoadAssembly(ASSEMBLY_SYSTEM)
    
ERROR
poBridge.cErrorMsg
  
ENDIF  *//////////////////////////////////////////////////////////////////////////////
  *// Creation some objects from ASSEMBLY_SYSTEM, necessary during processing
  
LOCAL
loEnc
as
Encoding
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loEnc =
CREATEOBJECT(«Encoding»,
USE_ENCODING)
  
LOCAL
lnUriKindRelative
as Integer
  
lnUriKindRelative =
poBridge.GetStaticProperty(«System.UriKind», «Relative»)
  
IF EMPTY(lnUriKindRelative)
    
ERROR
poBridge.cErrorMsg
  
ENDIF
  LOCAL
loUriWordSettingsXml
as
Uri
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loUriWordSettingsXml =
CREATEOBJECT(«Uri»,
URI_SETTINGS_XML, lnUriKindRelative)
  
LOCAL
loUriWordDocumentXml
as
Uri
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loUriWordDocumentXml =
CREATEOBJECT(«Uri»,
URI_DOCUMENT_XML, lnUriKindRelative)
  
LOCAL
lnFileModeOpen
as Integer
  
lnFileModeOpen =
poBridge.GetStaticProperty(«System.IO.FileMode», «Open»)
  
IF EMPTY(lnFileModeOpen)
    
ERROR
poBridge.cErrorMsg
  
ENDIF
  LOCAL
lnFileAccessRead
as Integer
  
lnFileAccessRead =
poBridge.GetStaticProperty(«System.IO.FileAccess», «Read»)
  
IF EMPTY(lnFileAccessRead)
    
ERROR
poBridge.cErrorMsg
  
ENDIF
  LOCAL
loMstrXmlDoc
as
MemoryStream
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loMstrXmlDoc =
CREATEOBJECT(«MemoryStream»)  *////////////////////////////////////////////////////////////////
  *// LoadAssembly ASSEMBLY_WINDOWSBASE
  
IF
!poBridge.LoadAssembly(ASSEMBLY_WINDOWSBASE)
    lcErrMsg = loBridge.cErrorMsg
    
ERROR
(lcErrMsg)
  
ENDIF  *///////////////////////////////////////////////////////////////////////
  *// Get package of FileDocx and prepare for its processing
  
LOCAL
loPackage
as
Package
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loPackage =
CREATEOBJECT(«Package»,
lcFileDocx)
  
*— Get PackagePart
of FileDocx for URI_SETTINGS_XML(/word/settings.xml)
  
LOCAL
loPackagePartSettings
as
PackagePart
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loPackagePartSettings =
CREATEOBJECT(«PackagePart»;
    ,loPackage.GetPart(loUriWordSettingsXml.GetOwnerFwObject()))
  
*— … and its
stream in Read mode
  
LOCAL
loPpStrmSettingsXml
as
Stream
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loPpStrmSettingsXml =
CREATEOBJECT(«Stream»;
    ,loPackagePartSettings.GetStream(lnFileModeOpen, lnFileAccessRead))
  
*— Get PackagePart
of FileDocx for URI_DOCUMENT_XML(/word/document.xml)
  
LOCAL
loPackagePartDocument
as
PackagePart
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loPackagePartDocument =
CREATEOBJECT(«PackagePart»;
    ,loPackage.GetPart(loUriWordDocumentXml.GetOwnerFwObject()))
  
*— … and its
stream in Read mode
  
LOCAL
loPpStrmDocumentXml
as
Stream
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loPpStrmDocumentXml =
CREATEOBJECT(«Stream»;
    ,loPackagePartDocument.GetStream(lnFileModeOpen, lnFileAccessRead))  
*////////////////////////////////////////////////////////////////
  *// LoadAssembly ASSEMBLY_SYSTEM_XML
  
IF
!poBridge.LoadAssembly(ASSEMBLY_SYSTEM_XML)
    
ERROR
poBridge.cErrorMsg
  
ENDIF  *///////////////////////////////////////////////////////////////////////
  *// Load stream SettingsXml as xml document
  
LOCAL
loXmlDocSettings
as
XmlDocument
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXmlDocSettings =
CREATEOBJECT(«XmlDocument»)
  loXmlDocSettings.
Load(loPpStrmSettingsXml.GetOwnerFwObject())
  
LOCAL
loXmlDocSettingsRoot
as
XmlElement
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXmlDocSettingsRoot =
CREATEOBJECT(«XmlElement»,
loXmlDocSettings.DocumentElement)
  
LOCAL
lcNsp_w
as String
  
lcNsp_w =
loXmlDocSettingsRoot.GetNamespaceOfPrefix(«w»)
  
LOCAL
loNsMngr
as
XmlNamespaceManager
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loNsMngr =
CREATEOBJECT(«XmlNamespaceManager»,
loXmlDocSettings.NameTable)
  loNsMngr.AddNamespace(«w», lcNsp_w)  
*///////////////////////////////////////////////////////////////////////
  *// Get AttachedSchema name
  
LOCAL
loXnAttachedSchema
as
XmlNode
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXnAttachedSchema =
CREATEOBJECT(«XmlNode»;
    ,loXmlDocSettingsRoot.SelectSingleNode(«w:attachedSchema/@w:val»,
loNsMngr.getOwnerFwObject()))
  
LOCAL
lcDocxAttachedSchema
as String
  
lcDocxAttachedSchema =
loXnAttachedSchema.
Value  *///////////////////////////////////////////////////////////////////////////////
  *// Get Location for XSD-schema from Microsoft Schema Library in system
registry
  
LOCAL
lcPathSchemaLocation
as String
  LOCAL
loRkCurrentUser
as
RegistryKey
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loRkCurrentUser =
CREATEOBJECT(«RegistryKey»;
    ,poBridge.GetStaticProperty(«Microsoft.Win32.Registry», «CurrentUser»))
  
LOCAL
loRkSchemaLibrary
as
RegistryKey
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loRkSchemaLibrary =
CREATEOBJECT(«RegistryKey»,
loRkCurrentUser.OpenSubKey(KEY_SCHEMA_LIBRARY))
  
LOCAL
loRkDocxAttachedSchema
as
RegistryKey
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loRkDocxAttachedSchema =
CREATEOBJECT(«RegistryKey»,
loRkSchemaLibrary.OpenSubKey(lcDocxAttachedSchema + «»))
  lcPathSchemaLocation = loRkDocxAttachedSchema.GetValue(LOCATION_NAME_VALUE)  
*///////////////////////////////////////////////////////////////////////////////
  *// Load xsd-schema and get its root element
  
LOCAL
loXmlDocXsd
as
XmlDocument
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXmlDocXsd =
CREATEOBJECT(«XmlDocument»)
  loXmlDocXsd.
Load(lcPathSchemaLocation)
  
LOCAL
loXmlDocXsdDocumentElement
as
XmlElement
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXmlDocXsdDocumentElement =
CREATEOBJECT(«XmlElement»,
loXmlDocXsd.DocumentElement)  
*///////////////////////////////////////////////////////////////////////////////
  *// Prepare and execute the xslt-transformation
  *// with document.xml and by using xslt-file genXmlFromDocument.xslt
  
LOCAL
lcStrXmlDoc
as String
  LOCAL
loXmlDoc
as
XmlDocument
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXmlDoc =
CREATEOBJECT(«XmlDocument»)
  loXmlDoc.
Load(loPpStrmDocumentXml.GetOwnerFwObject())
  
LOCAL
loXsltArgumentList
as
XsltArgumentList
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXsltArgumentList =
CREATEOBJECT(«XsltArgumentList»)
  loXsltArgumentList.AddParam(«prmEncoding», «», USE_ENCODING)
  
IF
!EMPTY(lcDocxAttachedSchema)
    loXsltArgumentList.AddParam(«prmSchemaUri», «», lcDocxAttachedSchema)
  
ENDIF
  IF VARTYPE
(loXmlDocXsdDocumentElement)
= ‘O’
    loXsltArgumentList.AddParam(«prmDocXsd», «»,
loXmlDocXsdDocumentElement.CreateNavigator())
  
ENDIF
  LOCAL
loXtwXmlDoc
as
XmlTextWriter
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXtwXmlDoc =
CREATEOBJECT(«XmlTextWriter»;
    ,loMstrXmlDoc.GetOwnerFwObject(), loEnc.GetOwnerFwObject())
  
LOCAL
loXslXmlDocTrans
as
XslCompiledTransform
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXslXmlDocTrans =
CREATEOBJECT(«XslCompiledTransform»)
  loXslXmlDocTrans.
Load(lcFileXslt)
  loXslXmlDocTrans.
Transform(loXmlDoc.CreateNavigator();
    ,loXsltArgumentList.GetOwnerFwObject(), loXtwXmlDoc.GetOwnerFwObject())
  lcStrXmlDoc = loEnc.GetString(loMstrXmlDoc.GetBuffer(), 0,
loMstrXmlDoc.Length)
  
LOCAL
lnPos
as Integer
  
lnPos =
ATC(«<«,
lcStrXmlDoc)
  
IF
lnPos = 0
    
ERROR
(ERR_EMPTY_XSLTRES +
lcFileGenXsltFromTemplate)
  
ENDIF
  
lcStrXmlDoc =
SUBSTRC(lcStrXmlDoc,
lnPos)
  lnPos =
ATC(«<?xml»,
lcStrXmlDoc)
  
IF
lnPos = 0
    lcStrXmlDoc = XML_PI + lcStrXmlDoc
  
ENDIF  *///////////////////////////////////////////////////////////////////////////////
  *// Load & save result of xslt-transformation
  
LOCAL
loXmlDocXml
as
XmlDocument
OF
….CommonToolsFrameworkWrap.prg
&& FULLPATH(PATH_COMMON_TOOLS
+ «FrameworkWrap.prg»)
  
loXmlDocXml =
CREATEOBJECT(«XmlDocument»)
  loXmlDocXml.
LoadXml(lcStrXmlDoc)
  loXmlDocXml.
Save(FULLPATH(PATH_XML)
+ XML_OUTPUT_FILE)
  lbResult = .T.
  ? MSG_SUCCESS_RESUT

CATCH TO
loException
  lbError = .T.
  
LOCAL
lcMsg
as String
  
lcMsg = «Error: » +
LTRIM(STR(loException.ErrorNo))
+ CRLF ;
    + «LineNo: » +
LTRIM(STR(loException.LineNo))
+ CRLF ;
    + «Message: » +
IIF(EMPTY(loException.Message),
‘Unknown error’, loException.
Message)
  ? ERR_LABEL_OUTPUT
  ? lcMsg

FINALLY
  
loWSH =
NULL
  IF TYPE
(‘poBridge’) = ‘O’ AND !ISNULL(poBridge)
    
*— Remove all
objects, which keeping resources of memory
    
loRkDocxAttachedSchema =
NULL
    
loRkSchemaLibrary =
NULL
    
loRkCurrentUser =
NULL
    
loPpStrmSettingsXml =
NULL
    
loPpStrmDocumentXml =
NULL
    
loMstrXmlDoc =
NULL
    
loXtwXmlDoc =
NULL
    
loPackage =
NULL
    
poBridge.Unload()
    poBridge =
NULL
  ENDIF
ENDTRY
RETURN
lbResult

Файл: docx2xml.prg из проекта DocxToXmlByUseFramework_20.pjx

Не стал я здесь упрощать код для улучшения восприятия… как надеюсь
восприятие от этого не пострадало. :-) Обратите внимание: разрушение объектов,
удерживающих ресурсы, производится явно в блоке FINALLY.
Это потому, что в противном случае, мы не управляем последовательностью
уничтожения объектов, а такая последовательность может быть важна. Например,
уничтожение любых объектов-покрытий MS Framework-классов
должно выполняться до уничтожений переменной ссылки poBridge,
т.к. в событиях Destroy() соответствующих классов
именно используя её происходит освобождение соответствующих ресурсов.

XSLT-преобразование
таблицы данных из xml-формата
в html-представление.

В заключении коснёмся ещё одной темы: преобразование xml-данных
в html-представление, т.к. MS Word
2007 способен воспринимать такие данные. Если предположить, что на входе
у нас всегда будет «плоская таблица» с данными в xml-формате
(возможно в качестве первого элемента, имеющие элемент xsd-схемы),
то XSLT-преобразование таких данных в
html
-формат могло бы выглядеть так:


<?
xml
version=«1.0«
encoding=«utf-8«?>
<!—
file: XmlTableToHtm.xslt
—>
<
xsl:stylesheet
version=«1.0«
    
xmlns=«http://www.w3.org/TR/html4«
    
xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance«
    
xmlns:xsd=«http://www.w3.org/2001/XMLSchema«
    
xmlns:xs=«http://www.w3.org/2001/XMLSchema«
    
xmlns:msxsl=«urn:schemas-microsoft-com:xslt«
    
xmlns:xsl=«http://www.w3.org/1999/XSL/Transform«
    
exclude-result-prefixes=«xsi
xsd xs msxsl xsl
«>

  <

xsl:output
method=«html«
version=«4.0«
    
encoding=«utf-8«
indent=«no«

    
doctype-public=«-//W3C//DTD
HTML 4.0 Transitional//EN
«
/>

  <

xsl:variable
name=«prmTableLoc«>
    <
xsl:text>таблица</xsl:text>

  </
xsl:variable>

  <!—

Test: is the first data element as
element of xsd-schema
—>
  <
xsl:variable
name=«varIsSchema«>
    <
xsl:choose>
      <
xsl:when
test=«local-name(/./*/*[1])=’schema’«>
        <
xsl:text>true</xsl:text>

      </
xsl:when>
      <
xsl:otherwise>
        <
xsl:text>false</xsl:text>

      </
xsl:otherwise>

    </
xsl:choose>

  </
xsl:variable>

  <

xsl:variable
name=«varFirstDataElement«>
    <
xsl:choose>
      <
xsl:when
test=«$varIsSchema=’true’«>
        <
xsl:text>2</xsl:text>
      </
xsl:when>
      <
xsl:otherwise>
        <
xsl:text>1</xsl:text>
      </
xsl:otherwise>

    </
xsl:choose>

  </
xsl:variable>

  <!—

Get name of first data element as
table name
—>
  <
xsl:variable
name=«varTable«>
    <
xsl:value-of
select=«local-name(/./*/*[number($varFirstDataElement)])«
/>
  </
xsl:variable>

  <!—

Get xsd-schema if exist
—>
  <
xsl:variable
name=«varSchema«>
    <
xsl:if
test=«$varIsSchema=’true’«>
      <
xsl:copy-of
select=«/./*/*[1]«
/>
    </
xsl:if>
  </
xsl:variable>

  <!—

Parsing from root …
—>
  <
xsl:template
match=«/«>
    <
html>
      <
head>
        <
xsl:text
disable-output-escaping=«yes«><![CDATA[<meta
http-equiv=»Content-Type» content=»text/html; charset=utf-8″>
]]></xsl:text>
        <
title>
          <
xsl:value-of
select=«$varTable«
/>
          <
xsl:text>
</xsl:text>
          <
xsl:value-of
select=«$prmTableLoc«
/>
        </
title>
        <
style
type=«text/css«>
          
body
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            font-size: 8.5pt;}
          h4
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            color: darkblue;
            font-size: 9.0pt;}
          th
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            font-size: 8.5pt;
            color: darkblue;
            background-color: WhiteSmoke;}
          td
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            font-size: 8.5pt;}
          .even
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            font-size: 8.5pt;
            background-color: Azure;}
        
</style>
      </
head>
      <
body>
        <
h4
align=«center«>
          <
xsl:value-of
select=«$varTable«
/>
          <
xsl:text>
</xsl:text>
          <
xsl:value-of
select=«$prmTableLoc«
/>
        </
h4>
        <
table
align=«center«
border=«1«
cellspacing=«0«
cellpadding=«2«>
          <!—
For each elements with
name=$varTable
—>
          <
xsl:for-each
select=«/./*/*[local-name()=$varTable]«>
            <
xsl:if
test=«position()=1«>
              <!—
In case first position: generate
table row with columns headers
—>
              <
tr>
                <!—
For each column…
—>
                <
xsl:for-each
select=«*«>
                  <
th>
                    <!—
Select name of element
—>
                    <
xsl:value-of
select=«local-name()«
/>
                  </
th>
                </
xsl:for-each>
              </
tr>
            </
xsl:if>
            <!—
Define class to current row
—>
            <
xsl:variable
name=«varClass«>
              <
xsl:if
test=«position()
mod 2
«>
                <
xsl:text>even</xsl:text>
              </
xsl:if>
            </
xsl:variable>
            <!—
Generate table row with data…
—>
            <
tr>
              <!—
For each column…
—>
              <
xsl:for-each
select=«*«>
                <!—
Generate column value
—>
                <
xsl:call-template
name=«getColValue«>
                  <
xsl:with-param
name=«prmClass«
select=«$varClass«
/>
                  <
xsl:with-param
name=«prmValue«
select=«.«
/>
                </
xsl:call-template>
              </
xsl:for-each>
            </
tr>
          </
xsl:for-each>
        </
table>
      </
body>
    </
html>
  </
xsl:template>

  <!—

Generate column value
(<td>…</td>)
—>
  <
xsl:template
name=«getColValue«>
    <
xsl:param
name=«prmClass«
/>
    <
xsl:param
name=«prmValue«
/>
    <
td>
      <
xsl:attribute
name=«vAlign«>
        <
xsl:text>top</xsl:text>
      </
xsl:attribute>
      <
xsl:if
test=«string-length($prmClass)>0«>
        <!—
Add class attribute if need…
—>
        <
xsl:attribute
name=«class«>
          <
xsl:value-of
select=«$prmClass«
/>
        </
xsl:attribute>
      </
xsl:if>
      <!—
Get name of current element
—>
      <
xsl:variable
name=«varName«>
        <
xsl:value-of
select=«local-name()«
/>
      </
xsl:variable>
      <!—
Try get type of current element
—>
      <
xsl:variable
name=«varType«>
        <
xsl:choose>
          <
xsl:when
test=«$varIsSchema=’true’«>
            <!—
… try get type from xsd-schema
—>
            <
xsl:choose>
              <
xsl:when
test=«msxsl:node-set($varSchema)/.//*[local-name()=’element’
and @name=$varName]/@type
«>
                <
xsl:value-of
select=«msxsl:node-set($varSchema)/.//*[local-name()=’element’
and @name=$varName]/@type
«
/>
              </
xsl:when>
              <
xsl:when
test=«msxsl:node-set($varSchema)/.//*[local-name()=’element’
and
@name=$varName]/*[local-name()=’simpleType’]/*[local-name()=’restriction’]/@base
«>
                <
xsl:value-of
select=«msxsl:node-set($varSchema)/.//*[local-name()=’element’
and
@name=$varName]/*[local-name()=’simpleType’]/*[local-name()=’restriction’]/@base
«
/>
              </
xsl:when>
              <!—
… insert your code here for any
other types…
—>
              <
xsl:otherwise>
              </
xsl:otherwise>
            </
xsl:choose>
          </
xsl:when>
          <
xsl:otherwise>
            <!—
… otherwise try get type
according to name of current element
—>
            <
xsl:choose>
              <
xsl:when
test=«$varName=’birthdate’«>
                <
xsl:text>date</xsl:text>
              </
xsl:when>
              <!—
… insert your code here for any
other types…
—>
              <
xsl:otherwise>
              </
xsl:otherwise>
            </
xsl:choose>
          </
xsl:otherwise>

        </
xsl:choose>
      </
xsl:variable>
      <!—
Remove prefix from element type
—>
      <
xsl:variable
name=«varTypeValue«>
        <
xsl:choose>
          <
xsl:when
test=«contains($varType,
‘:’)
«>
            <
xsl:value-of
select=«substring-after($varType,
‘:’)
«/>
          </
xsl:when>
        <
xsl:otherwise>
          <
xsl:value-of
select=«$varType«/>
        </
xsl:otherwise>

        </
xsl:choose>
      </
xsl:variable>
      <
xsl:if
test=«$varTypeValue=’decimal’
or $varTypeValue=’int’ or $varTypeValue=’date’
«>
        <!—
Add align attribute if need…
—>
        <
xsl:attribute
name=«align«>
          <
xsl:text>right</xsl:text>
        </
xsl:attribute>
      </
xsl:if>
      <!—
Convert value according to type of
current element
—>
      <
xsl:choose>
        <
xsl:when
test=«$varTypeValue=’date’«>
          <!—
… convert to russian date format
—>
          <
xsl:value-of
select=«msxsl:format-date($prmValue,
‘dd.MM.yyyy’)
« />
        </
xsl:when>
        <!—
… insert your code here for any
other types…
—>
        <
xsl:otherwise>
          <!—
… otherwise: simple copy of
current value
—>
          <
xsl:value-of
disable-output-escaping=«yes«
select=«$prmValue«
/>
        </
xsl:otherwise>

      </
xsl:choose>
    </
td>
  </
xsl:template>

</

xsl:stylesheet>

Файл: XmlTableToHtm.xslt

Чтобы попробовать применить это преобразование к данных из только что
полученного нами файла documentData.xml
можно использовать утилиту командной строки msxsl.exe,
которую можно свободно и бесплатно загрузить по ссылке, приведённой в разделе Ссылки по теме.
Для этого создадим в подкаталоге XML командный файл:

msxsl.exe documentData.xml ../XSLT/XmlTableToHtm.xslt -o documentData.htm -u '4.0'

Файл: documentDataToHtm.cmd

Здесь над данными из файла documentData.xml
текущего каталога применяется преобразование XmlTableToHtm.xslt
из подкаталога XSLT, а результат сохраняется в виде файла
documentData.htm текущего
каталога, при этом используется MSXML версии 4.0.
После выполнения командного файла documentDataToHtm.cmd
просмотр в Internet Explorer полученного файла
documentData.htm
у меня даёт вот такую картинку:

docx15.jpg
Рис.15

XSLT-преобразование над xml-данными
можно выполнить и средствами MS Word 2007, причём
полученный результат иметь в виде текущего документа. Это можно сделать либо из
командой строки, либо создав ярлык вот с такой командой:

"%PROGRAMFILES%Microsoft OfficeOffice12WINWORD.EXE" /p..XSLTXmlTableToHtm.xslt employee.XML

Команда в ярлыке: ShowByUseEmployeeToHtml

docx19.jpg
Свойства ярлыка ShowByUseEmployeeToHtml

Здесь преобразование ..XSLTXmlTableToHtm.xslt применяется над данными из файла
employee.XML
текущего каталога. Двойной клик
мышки на этом ярлыке у меня приводит к открытию MS Word
2007 и отображению в нём вот такой картинки:

docx16.jpg
Рис.16

Выполнить заданное XSLT-преобразование над
xml-данными средствами MS Word
2007 можно и из самой оболочки Word-а, для
этого следует открыть xml-данные т.с. «в чистом виде»
(файл: ..XMLdocumentData.xml в нашем случае),
а затем, через кнопку «Обзор…» в панели справа «XML
документ» открыть и файл ..XSLTXmlTableToHtm.xslt.
Примерно так, как показано на рисунке ниже:

docx17.jpg
Рис.17

Если всё Вами проделано удачно, а как надеюсь, что это действительно так :-),
то в конечном счёте у Вас должно получиться примерно то, что получилось и у меня:

docx18.jpg
Рис.18

XML-файл может содержать инструкцию типа:


<?
xml-stylesheet
type=»text/xsl»
href=»..XSLTXmlTableToHtmForIE.xslt»
?>

в которой атрибут href указывает на файл
xslt-преобразования, которое следует применить к
данным xml-файла во время отображения его в
Internet Explorer. Однако, имеется небольшая проблема
с версией XML-процессора, который будет при этом
использован. Например, у меня на компьютере это версия 3.0 (можно посмотреть под
ключом: HKEY_CLASSES_ROOTMsxml2.DOMDocumentCurVer в
системном реестре.) Функциональные возможности версии 3.0 по отношению к версии
4.0, для которой написан файл XmlTableToHtm.xslt
несколько ограничены. Например, отсутствует функция msxsl:format-date(),
позволяющая преобразовать значение даты в требуемый формат. Поэтому
специально для демонстрации этой возможность в каталог XSLT
я подложил слегка подправленный файл XmlTableToHtmForIE.xslt,
где вместо функции msxsl:format-date() использовано
явное формирование даты, путём разбора значения даты в формате
xml
на её составляющие: год, месяц, день и последующим формированием
строки в требуемом для отображения формате, а посмотреть результат можно открыв
файл documentDataForIE.xml в
Internet Explorer.

К сожалению xml-формат, в котором мы можем получить
данные из базы данных, достаточно разнообразен, чтобы утверждать, что
преобразование XmlTableToHtm.xslt способно
обработать данные любой «плоской таблицы», представленной в
xml
-формате. Так, чтобы продемонстрировать наличие «других» форматов
(отличных от того, в котором находится файл documentData.xml), в файле
getXmlFromADODB.js мной написан код на JavaScript,
позволяющий сделать выборку с MS SQL Server через
SQLOLEDB Provider с помощью SQL-команды:
«SELECT TOP 5 lastname, firstname, birthdate, notes FROM employees ORDER BY lastname, firstname» из базы данных
Northwind, а результат при этом, сохраняемый в файл documentDataSql.xml
с помощью команды oRs.Save(«documentDataSql.xml», adPersistXML), у меня получился следующим:


<
xml
xmlns:s=uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882
    
xmlns:dt=uuid:C2F41010-65B3-11d1-A29F-00AA00C14882
    
xmlns:rs=urn:schemas-microsoft-com:rowset
    
xmlns:z=#RowsetSchema>
  <
s:Schema
id=RowsetSchema>
    <
s:ElementType
name=row
content=eltOnly>
      <
s:AttributeType
name=lastname
rs:number=1
rs:writeunknown=true>
        <
s:datatype
dt:type=string
dt:maxLength=20
rs:maybenull=false/>
      </
s:AttributeType>
      <
s:AttributeType
name=firstname
rs:number=2
rs:writeunknown=true>
        <
s:datatype
dt:type=string
dt:maxLength=10
rs:maybenull=false/>
      </
s:AttributeType>
      <
s:AttributeType
name=birthdate
rs:number=3
rs:nullable=true
rs:writeunknown=true>
        <
s:datatype
dt:type=dateTime
rs:dbtype=timestamp
dt:maxLength=16
rs:scale=3
rs:precision=23
rs:fixedlength=true/>
      </
s:AttributeType>
      <
s:AttributeType
name=notes
rs:number=4
rs:nullable=true
rs:writeunknown=true>
        <
s:datatype
dt:type=string
dt:maxLength=1073741823
rs:long=true/>
      </
s:AttributeType>
      <
s:extends
type=rs:rowbase/>
    </
s:ElementType>
  </
s:Schema>
  <
rs:data>
    <
z:row
lastname=Buchanan
firstname=Steven
birthdate=1955-03-04T00:00:00
notes=Steven
Buchanan graduated from St. Andrews University, Scotland, with a BSC degree in
1976. Upon joining the company as a sales representative in 1992, he spent 6
months in an orientation program at the Seattle office and then returned to his
permanent post in London. He was promoted to sales manager in March 1993. Mr.
Buchanan has completed the courses
"Successful
Telemarketing
"
and
"International
Sales Management.
"
He is fluent in French.
/>
    <
z:row
lastname=Callahan
firstname=Laura
birthdate=1958-01-09T00:00:00
notes=Laura
received a BA in psychology from the University of Washington. She has also
completed a course in business French. She reads and writes French.
/>
    <
z:row
lastname=Davolio
firstname=Nancy
birthdate=1948-12-08T00:00:00
notes=Education
includes a BA in psychology from Colorado State University in 1970. She also
completed
"The
Art of the Cold Call.
"
Nancy is a member of Toastmasters International.
/>
    <
z:row
lastname=Dodsworth
firstname=Anne
birthdate=1966-01-27T00:00:00
notes=Anne
has a BA degree in English from St. Lawrence College. She is fluent in French
and German.
/>
    <
z:row
lastname=Fuller
firstname=Andrew
birthdate=1952-02-19T00:00:00
notes=Andrew
received his BTS commercial in 1974 and a Ph.D. in international marketing from
the University of Dallas in 1981. He is fluent in French and Italian and reads
German. He joined the company as a sales representative, was promoted to sales
manager in January 1992 and to vice president of sales in March 1993. Andrew is
a member of the Sales Management Roundtable, the Seattle Chamber of Commerce,
and the Pacific Rim Importers Association.
/>
  </
rs:data>
</
xml>

Файл: documentDataSql.xml

Взгляните как он устроен и сравните его с содержимым файла documentData.xml.
Как надеюсь, разница заметна? :-) Очевидно, что ранее приведённое здесь
xslt-преобразование
XmlTableToHtm.xslt
не отработает корректно для
данных такого формата. Что же, придётся подправить… и после внесения
соответствующих изменений у меня получился во такой результат:


<?
xml
version=«1.0«
encoding=«utf-8«?>
<!—
file: XmlTableToHtmForADODB.xslt
—>
<
xsl:stylesheet
version=«1.0«
    
xmlns=«http://www.w3.org/TR/html4«
    
xmlns:s=uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882
    
xmlns:dt=uuid:C2F41010-65B3-11d1-A29F-00AA00C14882
    
xmlns:rs=urn:schemas-microsoft-com:rowset
    
xmlns:z=#RowsetSchema

    
xmlns:msxsl=«urn:schemas-microsoft-com:xslt«
    
xmlns:xsl=«http://www.w3.org/1999/XSL/Transform«
    
exclude-result-prefixes=«s
dt rs z msxsl xsl
«>  <xsl:output
method=«html«
version=«4.0«
    
encoding=«utf-8«
indent=«no«

    
doctype-public=«-//W3C//DTD
HTML 4.0 Transitional//EN
«
/>  <
xsl:variable
name=«varSchema«>
    <
xsl:copy-of
select=«/./*/*[local-name()=’Schema’]«
/>
  </
xsl:variable>  <xsl:template
match=«/«>
    <
html>
      <
head>
        <
xsl:text
disable-output-escaping=«yes«><![CDATA[<meta
http-equiv=»Content-Type» content=»text/html; charset=utf-8″>
]]></xsl:text>
        <
style
type=«text/css«>
          
body
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            font-size: 8.5pt;}
          h4
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            color: darkblue;
            font-size: 9.0pt;}
          th
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            font-size: 8.5pt;
            color: darkblue;
            background-color: WhiteSmoke;}
          td
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            font-size: 8.5pt;}
          .even
          {font-family: Verdana, Arial, Helvetica, sans-serif;
            font-size: 8.5pt;
            background-color: Azure;}
        
</style>
      </
head>
      <
body>
        <
table
align=«center«
border=«1«
cellspacing=«0«
cellpadding=«2«>
          <!—
For each
row-element
—>
          <
xsl:for-each
select=«/./*/*[local-name()=’data’]/*[local-name()=’row’]«>
            <
xsl:if
test=«position()=1«>
              <!—
In case first position: generate
table row with columns headers
—>
              <
tr>
                <!—
For each column…
—>
                <
xsl:for-each
select=«@*«>
                  <
th>
                    <!—
Select name of
attribute

—>
                    <
xsl:value-of
select=«local-name()«
/>
                  </
th>
                </
xsl:for-each>
              </
tr>
            </
xsl:if>
            <!—
Define class to current row
—>
            <
xsl:variable
name=«varClass«>
              <
xsl:if
test=«position()
mod 2
«>
                <
xsl:text>even</xsl:text>
              </
xsl:if>
            </
xsl:variable>
            <!—
Generate table row with data…
—>
            <
tr>
              <!—
For each column…
—>
              <
xsl:for-each
select=«@*«>
                <!—
Generate column value
—>
                <
xsl:call-template
name=«getColValue«>
                  <
xsl:with-param
name=«prmClass«
select=«$varClass«
/>
                  <
xsl:with-param
name=«prmValue«
select=«.«
/>
                </
xsl:call-template>
              </
xsl:for-each>
            </
tr>
          </
xsl:for-each>
        </
table>
      </
body>
    </
html>
  </
xsl:template>

  <!—

Generate column value
(<td>…</td>)
—>
  <
xsl:template
name=«getColValue«>
    <
xsl:param
name=«prmClass«
/>
    <
xsl:param
name=«prmValue«
/>
    <
td>
      <
xsl:attribute
name=«vAlign«>
        <
xsl:text>top</xsl:text>
      </
xsl:attribute>
      <
xsl:if
test=«string-length($prmClass)>0«>
        <!—
Add class attribute if need…
—>
        <
xsl:attribute
name=«class«>
          <
xsl:value-of
select=«$prmClass«
/>
        </
xsl:attribute>
      </
xsl:if>
      <!—
Get name of current
attribute

—>
      <
xsl:variable
name=«varName«>
        <
xsl:value-of
select=«local-name()«
/>
      </
xsl:variable>
      <!—
Try get type of current
value
—>
      <
xsl:variable
name=«varType«>
        <!—
… try get type from schema
—>
        <
xsl:choose>
          <
xsl:when
test=«msxsl:node-set($varSchema)/.//*[local-name()=’AttributeType’
and @name=$varName]/*[local-name()=’datatype’]/@dt:type
«>
            <
xsl:value-of
select=«msxsl:node-set($varSchema)/.//*[local-name()=’AttributeType’
and @name=$varName]/*[local-name()=’datatype’]/@dt:type
«
/>
          </
xsl:when>
          <!—
… insert your code here for any
other types…
—>
          <
xsl:otherwise>
          </
xsl:otherwise>
        </
xsl:choose>
      </
xsl:variable>
      <
xsl:if
test=«$varType=’decimal’
or $varType=’int’ or $varType=’dateTime’
«>
        <!—
Add align attribute if need…
—>
        <
xsl:attribute
name=«align«>
          <
xsl:text>right</xsl:text>
        </
xsl:attribute>
      </
xsl:if>
      <!—
Convert value according to type of
current value
—>
      <
xsl:choose>
        <
xsl:when
test=«$varType=’dateTime’«>
          <!—
… convert to russian date format
—>
          <
xsl:value-of
select=«msxsl:format-date($prmValue,
‘dd.MM.yyyy’)
« />
        </
xsl:when>
        <!—
… insert your code here for any
other types…
—>
        <
xsl:otherwise>
          <!—
… otherwise: simple copy of
current value
—>
          <
xsl:value-of
disable-output-escaping=«yes«
select=«$prmValue«
/>
        </
xsl:otherwise>

      </
xsl:choose>
    </
td>
  </
xsl:template>

</

xsl:stylesheet>

Файл: XmlTableToHtmForADODB.xslt

Запуск преобразования
можно осуществить воспользовавшись командным файлом
documentDataToHtmForADODB.cmd
,
а результат можно посмотреть в созданном при этом файле:
documentDataForADODB.htm
.

На другой подход решения подобной задачи, можно посмотреть в сообщении К
использованию dbf2docx external.gif, где в тексте документа используются «элементы
управления содержимым» в MS Word Office 2007/2010, в то время как xml-данные встраиваются в документ в виде CustomXmlPart-части.
Код преобразования шаблона документа, содержащего одну таблицу с одной строкой
данных в xml-формате, преобразующего такой шаблон в
документ, содержащий множество строк, взятых из xml-файла
данных, написан на C# в MS VS .NET
2010(SP1)
с использованием
Open XML
SDK 2.0 for Microsoft Office external.gif usa.gif.

Краткое описание кода примеров.

Код примеров, xml-данные и xsd-схемы
к ним помещены мной в файл docxtbl.zip на странице Примеры кода и утилиты.
Ниже краткое описание содержания подкаталогов в файле docxtbl.zip:

  • VFP_CreateXmlXsd — VFP-код для генерации xml+xsd данных
    в подкаталог XML
  • XML — employee.XML — исходные данные для MS Word 2007
    документа, employee.XSD — соответствующая схема, используемая при создании
    шаблона «Пример VFP-таблицы в виде MS Word 2007.docx» из подкаталога DOCX.
    Здесь также расположены файлы: documentData.xml, documentData.htm, documentDataForADODB.htm, documentDataForIE.xml, documentDataSql.xml, documentDataToHtm.cmd, documentDataToHtmForADODB.cmd, getXmlFromADODB.js
    и ярлык ShowByUseEmployeeToHtml
  • XSLT — содержит файлы:
    genXmlFromDocument.xslt, genXsltFromTemplate.xslt, XmlTableToHtm.xslt, XmlTableToHtmForADODB.xslt, XmlTableToHtmForIE.xslt
  • DOCX — шаблон MS Word 2007 документа («Пример
    VFP-таблицы в виде MS Word 2007.docx»), использующего схему из
    XMLemployee.XSD. Здесь также расположены файлы: getContentsUseWinRAR.cmd, fromContentsToDocxUseWinRAR.cmd
  • solVS_NET2005_Framework_20
    C#-код приложений xml2docx
    и docx2xml
  • VFP_FillDocx — VFP-код для
    наполнения шаблона DOCX»Пример VFP-таблицы в виде MS Word 2007.docx»
    данными из XMLemployee.XML
  • VFP_DocxToXml
    VFP-код для извлечения xml-данных
    XMLdocumentData.xml)
    из файла MS Word 2007 документа (DOCX«Пример
    VFP-таблицы в виде MS Word 2007.docx»)
  • CommonTools
    VFP-код для поддержки обращений из-под VFP к
    MS Framework 2.0 библиотекам классов
  • bin — ClrHost.dll, DotNetBridge.dll — см. назначение и
    код на стр. Примеры кода и утилиты в
    xlsxtbl.zip

Ссылки по теме.

  • Краткое введение в XML.
  • Office 2007 Open XML Format, Алексей Федоров.
    external.gif
  • Microsoft Office 2007 Open XML. Часть 2, Алексей Федоров. external.gif
  • Форматы Ecma Office Open XML: вопросы и ответы. external.gif
  • Работа с XML. external.gif
  • OpenXML Developer external.gif usa.gif
  • Hosting the .NET Runtime in Visual FoxPro, by Rick Strahl external.gif usa.gif
  • Building Word 2007 Documents Using Office Open XML Formats, by Erika Ehrli, Brian Jones external.gif usa.gif
  • Walkthrough: Word 2007 XML Format, by Erika Ehrli external.gif usa.gif
  • Introducing the Office (2007) Open XML File Formats, by Frank Rice external.gif usa.gif
  • How to: Manipulate Office Open XML Formats Documents, by Frank Rice external.gif usa.gif
  • Manipulating Word 2007 Files with the Open XML Object Model (Part 1 of 3), by Frank Rice external.gif usa.gif
  • How To Use Compressed (Zipped) Folders in Windows XP external.gif usa.gif
  • Convert an InfoPath 2007 form into a Word 2007 document using XSLT and C#, by S.Y.M. Wong-A-Ton external.gif usa.gif
  • Command Line Transformation Utility (msxsl.exe). external.gif usa.gif
  • MSXML 4.0 Service Pack 2 (Microsoft XML Core Services). external.gif usa.gif
  • Технология XSLT, Алексей Валиков external.gif
  • Как получить таблицу данных из xlsx-файла у приложения Excel из MS Office 2007 используя структуру данных формата Open XML?
  • Как получить dbf-таблицы из xls-файла при наличии групп в данных?.
  • К
    использованию dbf2docx external.gif
  • Open XML
    SDK 2.0 for Microsoft Office external.gif usa.gif
  • Примеры кода и утилиты — см. код и данные к этой статье в docxtbl.zip


Hosted by uCoz

как создать XML-файлы из слова

Формат XML предназначен для хранения данных, которые могут быть полезны в работе некоторых программ, сайтов и поддержке определенных языков разметки. Создать и открыть файл в этом формате несложно. Это можно сделать, даже если на вашем компьютере не установлено специализированное программное обеспечение.

XML сам по себе является языком разметки, чем-то похожим на HTML, который используется на веб-страницах. Но если последний используется только для отображения информации и ее правильной разметки, XML позволяет структурировать ее определенным образом, что делает этот язык похожим на аналог базы данных, не требующий СУБД.

Вы можете создавать файлы XML, используя как специализированные программы, так и встроенный текстовый редактор Windows. Удобство написания кода и уровень его функциональности зависят от типа используемого программного обеспечения.

Способ 1: Visual Studio

Вместо этого редактора кода Microsoft вы можете использовать любые его аналоги от других разработчиков. Фактически, Visual Studio — это более продвинутая версия обычного Блокнота. Код теперь имеет специальную подсветку, ошибки автоматически выделяются или исправляются, а специальные шаблоны уже загружены в программу, что упрощает создание больших файлов XML.

Для начала вам необходимо создать файл. Щелкните элемент «Файл» на верхней панели и выберите «Создать…» в раскрывающемся меню. Откроется список, в котором указана запись «Файл».

как создать XML-файлы из слова

Вам будет перенесено окно с выбором расширения файла, соответственно выберите пункт «XML файл».

как создать XML-файлы из слова

Во вновь созданном файле уже будет первая строка с кодировкой и версией. По умолчанию записывается первая версия и кодировка UTF-8, которую вы можете изменить в любой момент. Затем, чтобы создать полный XML-файл, вам нужно записать все, что было в предыдущем операторе.

По окончании работы снова выберите в верхней панели «Файл», затем из выпадающего меню пункт «Сохранить все».

Способ 2: Microsoft Excel

Вы можете создать XML-файл без написания кода, например, используя современные версии Microsoft Excel, что позволяет сохранять таблицы с этим расширением. Однако нужно понимать, что в этом случае у вас не получится создать что-то более функциональное, чем обычный стол.

Этот метод лучше всего подходит для тех, кто не хочет или не умеет работать с кодом. Однако в этом случае пользователь может столкнуться с некоторыми проблемами при перезаписи файла в формате XML. К сожалению, преобразование обычной таблицы в XML возможно только в более новых версиях MS Excel. Для этого воспользуйтесь следующими пошаговыми инструкциями:

  1. Дополните таблицу некоторым содержанием.
  2. Нажмите кнопку «Файл» в верхнем меню.
  3. Откроется специальное окно, в котором нужно нажать «Сохранить как…». Этот пункт находится в левом меню.
  4. Укажите папку, в которой вы хотите сохранить файл. Папка указана в центре экрана.
  5. Теперь вам нужно указать имя файла и в разделе «Тип файла» из выпадающего меню выбрать
    Данные XML».
  6. Нажмите кнопку «Сохранить».

Способ 3: Блокнот

Даже обычный Блокнот вполне подходит для работы с XML, но у пользователя, незнакомого с синтаксисом языка, возникнут трудности, так как в нем придется писать различные команды и теги. Несколько проще и продуктивнее процесс будет в специализированных программах для редактирования кода, например, в Microsoft Visual Studio. В них есть специальные метки и подсказки, которые значительно упрощают работу человеку, не знающему синтаксиса этого языка.

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

  1. Создайте простой текстовый документ с расширением TXT. Вы можете разместить его где угодно. Открой это.
  2. Начните набирать в нем первые команды. Во-первых, вам нужно установить кодировку для всего файла и указать версию XML, это делается с помощью следующей команды:

    Первое значение — это версия, менять ее не нужно, а второе значение — это кодировка. Рекомендуется использовать кодировку UTF-8, так как с ней прекрасно работает большинство программ и обработчиков. Однако его можно изменить на любое другое, просто набрав желаемое имя.

  3. Создайте первый каталог в вашем файле, написав тег и закрыв его вот так .
  4. Теперь вы можете написать какой-то контент внутри этого тега. Создаем тег и даем ему любое имя, например «Иван Иванов». Готовая конструкция должна выглядеть так:
  5. Внутри тега теперь можно писать более подробные параметры, в данном случае это информация об определенном Иване Иванове. Мы пропишем ваш возраст и местонахождение. 
  6. Если вы следовали инструкциям, вы должны получить тот же код, что и ниже. Когда закончите, найдите «Файл» в верхнем меню и выберите «Сохранить как…» в раскрывающемся меню. При сохранении в поле «Имя файла» после точки должно стоять расширение не TXT, а XML.

как создать XML-файлы из слова

Компиляторам XML необходимо обработать этот код в виде таблицы с одним столбцом, содержащей данные о некоем Иване Иванове.

В «Блокноте» вполне можно создавать такие простые таблицы, но при создании массивов более объемных данных могут возникнуть трудности, так как в обычном «Блокноте» нет функций для исправления ошибок в коде или их выделения.

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

Open Packaging Conventions #2. Собираем MS Word документ руками

Сегодня мы в качестве практики создадим пару документов для MS Word, не используя никаких специальных инструментов (за исключением XML-редактора и Zip-архиватора).

imageВ предыдущей статье я постарался “просто и доступно” Улыбкарассказать о том, что такое Open Package Convention (или иначе говоря, как устроены изнутри документы MS Office 2007+).

Сегодня мы в качестве практики создадим пару документов для MS Word, не используя никаких специальных инструментов (за исключением XML-редактора и Zip-архиватора).

Сразу же оговорюсь, что мы не будем сильно вдаваться в особенности разметки документов Word (хотя, конечно же, минимальные представления о ней все же понадобятся, но всё необходимое для понимания я постараюсь рассказать по мере развития статьи)! Наша задача: увидеть как строятся реальные пакеты на основе OPC – что такое компоненты, связи и как они хранятся.

Документ #1 – простой текст

Если попробовать заглянуть внутрь готового Word-документа, созданного в Office, можно легко прийти в ужас от обилия различных компонент непонятного формата назначения.

В реальности структура самого простого рабочего документа (такого, который сможет открыть и показать Word) включает всего 3 элемента:

  •    главный компонент, который содержит разметку всего документа
  •    1 компонент связи (который содержит связь между пакетом и главным компонентом
  •    описание типов (файл [Content_Types].xml)

Примерно так:

image

Давайте теперь создадим пустую папку, которая будет представлять содержимое всего пакета, и будем последовательно её заполнять.

Главный компонент документа. Создадим в нашей папке файл main.xml (в стандарте OpenXML нет жесткого требования к именованию компонент, поэтому мы будем использовать свои имена, не такие как в Word).

Этот файл будет представлять содержимое главного компонента (Main Document ы терминологии стандарта). В лучших традициях книг по программированию зададим ему следующее содержимое:

<document xmlns=»http://schemas.openxmlformats.org/wordprocessingml/2006/main»>
    <body>
        <!—
            Структра тела документа включает в себя:
                параграф (тэг <p>)
                элемент текста с форматированием (тэг <r>)
                собственно отображаемый текст (тэг <t>)
        —>
        <p>
            <r>
                <t>Hello, World!</t>
            </r>
        </p>
    </body>
</document>

Компонент связи. Теперь мы должны указать что именно компонент /main.xml содержит разметку документа. В OpenXML для этого используется механизм связей. В нашем документе будет только одна связь – от пакета к главному компоненту (главный компонент пока связей не имеет)

Как я писал в предыдущей статье у компонента, который хранит связи всего пакета имя будет /_rels/.rels. Для эмуляции такого имени (чтобы оно потом правильно создалось в конечном ZIP-архиве) мы создадим подпапку _rels, а в ней файл с именем .rels. Содержать этот файл будет всего одну связь:

<Relationships xmlns=»http://schemas.openxmlformats.org/package/2006/relationships»>
    <Relationship
        Id=»rId1″
        Type=»http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument»
        Target=»main.xml» />
</Relationships>

Описание типов. По большому счету, в нашем примере используется всего 2 типа контента: в компоненте с основным содержимым документа и в компоненте связи. Однако, хотя мы дали главному компоненту “расширение” .xml, его тип содержимого по стандарту OpenXML должен быть не просто application/xml, поэтому мы опишем 3 типа контента: для всех компонентов связей, для “некого произвольного xml” и явно для компонента /main.xml.

Итак, создадим в нашей папке файл [Content_Types].xml следующего содержания:

<Types xmlns=»http://schemas.openxmlformats.org/package/2006/content-types»>
        <Default Extension=»rels»
            ContentType=»application/vnd.openxmlformats-package.relationships+xml»/>
        <Default Extension=»xml»
            ContentType=»application/xml»/>
        <Override PartName=»/main.xml»
            ContentType=»application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml»/>
</Types>

Итак, содержимое нашей папки составляют 3 файла:

  •    _rels.rels
  •    [Content_Types].xml
  •    main.xml

Осталось упаковать их в отдельный архив, переименовать архив в, например, result.docx, и открыть полученный файл в Word. Наш результат будет:

image

Давайте теперь усложним пример, добавив в документ изображение.

Какие изменения нам потребуется внести в предыдущий пример? Вот они:

  •    добавить компонент с изображением и дополнить описание типов содержимого
  •    создать связь от главного компонента к компоненту с изображением (а это значит, что добавить еще один компонент связей)
  •    дополнить разметку самого документа (указать место и параметры вставляемой картинки)

Структура нашего пакета приобретет во такой вид:

image

В принципе, ничего сверхъестественного, поэтому приступим.

Компонент картинки и тип содержимого для него. Для добавления компонента просто скопируем готовый файл с картинкой в папку, к остальным компонентам (пусть это будет файл cat.jpeg).

После этого обновим содержимое файла типов содержимого ([Content_Types].xml):

<Types xmlns=»http://schemas.openxmlformats.org/package/2006/content-types»>
        <Default Extension=»jpeg»
            ContentType=»image/jpeg»/>
        <Default Extension=»rels»
            ContentType=»application/vnd.openxmlformats-package.relationships+xml»/>
        <Default Extension=»xml»
            ContentType=»application/xml»/>
        <Override PartName=»/main.xml»
            ContentType=»application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml»/>
</Types>

Связь от /main.xml к /cat.jpeg. Так как мы создаем связь от компонента /main.xml, имя компонента связей для него будет /_rels/main.xml.rels, а значит создадим в папке _rels еще один файл с именем main.xml.rels и содержащем описание 1 связи:

<Relationships xmlns=»http://schemas.openxmlformats.org/package/2006/relationships»>
    <Relationship
        Id=»rId1″
        Type=»http://schemas.openxmlformats.org/officeDocument/2006/relationships/image»
        Target=»cat.jpeg» />
</Relationships>

Осталось самое сложное – поправить разметку самого документа.

Разметка главного компонента. Вообще, надо признать, что разметка документов OpenXML местами весьма далека от “интуитивно понятной” и это справедливо, в том числе для описания изображений (в OpenXML используется единый подъязык для описания любых изображений – DrawingML, у которого есть еще несколько внутренних диалектов: для описания картинок, графиков, …).

Единственный предварительный комментарий нужно дать по поводу размерности единиц… В OpenXML используется специальная придуманная единица EMU (English Metric Unit) – единица, которая позволяет относительно удобно переводить размеры из метрической (метры/сантиметры) и американской (дюймы) систем единиц. Соотношения следующие:

1 см

360000 EMU

1 дюйм

914400 EMU

Все, можно оценивать (размеры областей вычислены на основе размеров картинки): Пример.

В нашей папке-пакете теперь содержатся такие файлы:

  •      _rels.rels
  •      _relsmain.xml.rels
  •      [Content_Types].xml
  •      cat.jpeg
  •      main.xml

Вновь собираем все в один Zip-архив и открываем в Word:

image

Вот и все.

P.S. Для желающих продолжить эксперименты – все исходные файлы, а также результаты можно найти на Codeplex в проекте https://msosamples.codeplex.com (здесь и далее я планирую размещать все приводимые примеры). Прямая ссылка на нужную папку.

Источник: Блог Михаила Романова.

Понравилась статья? Поделить с друзьями:
  • Xml шаблон для word
  • Xml map in excel
  • Xml in word 2013
  • Xml in microsoft office word 2010
  • Xml in excel cell