Xml2spreadsheet — различия между версиями

Материал из Course Orchestra
Перейти к: навигация, поиск
(Общая информация)
(Перенаправление на Xylophone)
 
Строка 1: Строка 1:
{{Платформа КУРС}}
+
#REDIRECT [[Xylophone]]
= Общая информация =
 
 
 
Система предназначена для формирования сложноструктурированных отчётов в формате электронных таблиц (XLS или XLSX), PDF-файлов или прямого вывода отчётов на принтер на основе данных, представленных в формате XML.
 
 
 
Система имеет два режима чтения исходных XML-файлов: DOM и SAX. Режим DOM допускает б'''о'''льшую гибкость в определении структуры отчёта и чтении XML-данных за счёт увеличенных требований к памяти и ресурсам. Режим SAX вводит ряд несущественных ограничений на структуру XML-документа и дескриптора отчета, однако является на порядок более эффективным по скорости и потреблению памяти, а потому пригоден для формирования огромных отчётов.
 
 
 
Система не требует установки MS Excel, Adobe Acrobat и любого иного программного обеспечения на машину, формирующую отчёты. Система для своей работы использует следующие (устанавливаемые вместе с системой) библиотеки с открытым исходным кодом:
 
 
 
* [https://poi.apache.org/ Apache POI] — для разбора и создания Excel-файлов.
 
* [https://xmlgraphics.apache.org/fop/ Apache FOP] — для создания PDF-файлов и вывода на печать на принтер.
 
 
 
Общий принцип работы системы показан на диаграмме:
 
 
 
[[Файл:xml2spreadsheet.png]]
 
 
 
На вход подаётся:
 
 
 
* XML-файл с исходными данными.
 
* Шаблон в формате .XLS или .XSLX, в котором заданы визуальная раскладка и форматирование будущего документа.
 
* Написанный на языке XML дескриптор, описывающий способ обхода тэгов исходного XML-файла.
 
 
 
На выходе получается файл, соответствующий формату шаблона (.XLS, если шаблон имел формат .XLS, или .XLSX, если шаблон имел формат .XSLX). Далее, если в том есть необходимость, документ XLS может быть передан на вторую фазу обработки и превращён в .PDF-файл или отправлен напрямую на печать.
 
 
 
==Установка==
 
 
 
Система поставляется вместе с [[Flute]], для установки см. статью [[Установка системы Flute]].
 
 
 
= Первый этап: формирование XLS и XLSX-файлов=
 
== Использование системы ==
 
Систему XML2Spreadsheet можно использовать двумя способами:
 
* как Java-библиотеку, используемую в скрипте [[Flute]] или иным другим способом,
 
* из командной строки.
 
 
 
Для использования в качестве библиотеки необходимо проимпортировать класс ru.curs.flute.xml2spreadsheet.XML2Spreadsheet и запустить статический метод process данного класса: '''XML2Spreadsheet.process(...)'''. Существует несколько перегруженных вариантов данного метода со следующими параметрами:
 
 
 
* File/InputStream '''xmlData''' — исходные данные.
 
* File/InputStream '''xmlDescriptor''' — дескриптор, описывающий порядок итерации по исходным данным.
 
* File/InputStream '''template''' — шаблон отчёта.
 
* ru.curs.flute.xml2spreadsheet.OutputType '''outputType''' —  тип шаблона отчёта (OpenOffice, XLS, XLSX). ''Данный параметр не указывается, если для параметров xmlData, xmlDescriptor, template используется тип File. В этом случае тип определяется по расширению файла.''
 
* boolean '''useSAX'''  — режим процессинга (DOM или SAX, описание различий см. ниже).
 
* boolean '''copyTemplate'''  — копировать ли шаблон полностью в результирующий файл перед началом обработки. ''Необязательный параметр, по умолчанию false.'' Данная опция нужна, если необходимо перенести из шаблона в результирующий файл диаграммы, графику и прочие объекты, не переносимые при отключенной данной опции.
 
* OutputStream '''output''' — поток, в который записывается результирующий отчёт.
 
 
 
Имеется также метод '''XML2Spreadsheet.toPOIWorkbook(xmlData, xmlDescriptor, template, useSAX, copyTemplate)''', возвращающий объектное представление результирующего отчёта в виде экземпляра класса org.apache.poi.ss.usermodel.Workbook. Этот метод может быть полезен, если необходимо выполнить пост-обработку отчёта перед окончательным сохранением.
 
 
 
Для запуска из командной строки см. статью [[Запуск XML2Spreadsheet из командной строки]].
 
 
 
== Управляемый обход XML ==
 
Вывод в документ-электронную таблицу осуществляется следующим образом:
 
* система определённым образом обходит элементы (тэги) XML-файла с данными (алгоритм обхода управляется настроечным XML-файлом, или, как мы его ещё называем, файлом-дескриптором),
 
* в заданные моменты осуществляется  копирование фрагментов шаблона в документ результата, при этом поля данных шаблона заполняются информацией в контексте текущего элемента XML-файла с данными.
 
В процессе обхода XML-файла с данными система может находиться в одном из трёх режимов:
 
# Состояние чтения элемента (element)
 
# Состояние вывода (output)
 
# Состояние итерации (iteration)
 
Граф перехода режимов:
 
 
 
[[Файл:xml2spreadsheet_statechart.jpg]]
 
 
 
Далее даётся описание обхода XML-файла с данными через описание трёх возможных режимов.
 
== Режим чтения элемента ==
 
В момент начала обработки система устанавливает в качестве текущего контекста корневой элемент документа с данными и в качестве режима — режим чтения элемента.
 
В начале обработки система ожидает, что корневым элементом настроечного файла будет тэг вида <element name=”[имя_корневого_элемента]”>, т. е. значение атрибута name корневого тэга должно совпадать с именем корневого элемента в файле данных. В противном случае система не осуществит вывода. Т. е., если файл с данными имеет вид
 
<syntaxhighlight lang="xml">
 
<root>
 
    ...
 
</root>
 
</syntaxhighlight>
 
то настроечный файл обязан иметь вид
 
<syntaxhighlight lang="xml">
 
<element name="root">
 
    ...
 
</element>
 
</syntaxhighlight>
 
Атрибут name также обязаны иметь все прочие тэги <element>.
 
 
 
В режиме чтения элемента система читает субэлементы тэга <element> настроечного файла. Они могут быть двух типов: <output> и <iteration>. Соответственно, система переходит в режимы вывода или итерации. Субэлементов вида  <output> и <iteration> в <element> может быть сколько угодно, и они могут идти в любой последовательности — система обрабатывает их последовательно один за другим.
 
 
 
Для атрибута '''name''' поддерживаются следующие варианты значений:
 
# Прямое указание '''имени тэга'''. В этом случае интерпретатор переходит к обработке <element> лишь в том случае, если имя тэга в сканируемом файле данных совпадает с указанным в атрибуте.
 
# Значение '''*''' («звёздочка»). В этом случае для обработки подходит любой тэг в файле данных.
 
# Простое XQuery-выражение типа '''tagname[@attribute='value']'''. Обработка происходит лишь в случае, когда имя тэга совпадает с tagname, а значение атрибута attribute  равно value. ВНИМАНИЕ: поддерживаем именно только такое выражение, с единственным атрибутом и знаком "=". Знаки <, >, а также логические выражения с несколькими условиями работать НЕ БУДУТ. Только выражение по шаблону tagname[@attribute='value'] (кавычки могут быть одинарными или двойными, по обстоятельствам, допускается также &amp;quot;).
 
# Значения '''(before)''' и '''(after)'''. Служат для вывода «пролога» и «эпилога» последовательности элементов.
 
 
 
=== Режим итерации ===
 
В режиме итерации система работает следующим образом:
 
* Запоминается значение контекста текущего элемента данных, чтобы восстановить его после завершения итерации.
 
* Далее, в зависимости от значения атрибута '''index''':
 
** Если тэг <iteration> не имеет атрибута index, читаются все субэлементы текущего элемента документа с данными и последовательно каждый из них выставляется в качестве текущего.
 
** Если тэг <iteration> имеет атрибут index, читается и выставляется в качестве текущего конкретный субэлемент текущего элемента. В качестве значения атрибута index может быть указано целое число, начиная с нуля.
 
* После того, как прочитан и установлен очередной текущий элемент, система последовательно читает все субэлементы тэга <iteration>, которые могут быть только типа <element>.
 
* Если встречается тэг <element> с атрибутом name=”(before)”, то прежде всего обрабатывается родительский элемент данных (это даёт возможность вывести  “header” последовательности элементов).
 
* Если получается так, что значение атрибута name тэга <element> совпадает с именем текущего элемента (или атрибут name установлен в значение '*'), то система переходит в режим чтения элемента, описанный выше.
 
* Если встречается тэг <element> с атрибутом name=”(after)”, то после всего обрабатывается родительский элемент данных (это даёт возможность вывести “footer” последовательности элементов).
 
* Тэг <iteration> может иметь атрибут '''mode''', устанавливающий режим компоновки фрагментов шаблона в результирующем файле. Возможные значения:
 
** отсутствие значения — фрагменты шаблона, выводимые в режиме output, компонуются в результирующем документе сверху вниз.
 
** horizontal — фрагменты шаблона компонуются в результирующем документе слева направо.
 
* Тэг <iteration> может иметь атрибут '''merge'''. Если целочисленное значение этого атрибута больше нуля и равно N, то N первых столбцов (или N первых строк, в зависимости от вертикального или горизонтального режима) блока, образованного итерацией, будут объединены в одну ячейку. Нужно для построения отчётов, в которых объединённая ячейка должна охватывать переменное количество строк или столбцов.
 
* Тэг <iteration> может иметь атрибут '''regionName'''. Если значение этого атрибута задано, то блок, образованный итерацией, будет в конце итерации преобразован в именованный диапазон с указанным именем.
 
* После завершения итерации система восстанавливает значение контекста текущего элемента, по субэлементам которого началась итерация.
 
Т. к. может быть сколько угодно тэгов <iteration> внутри тэга <element> и тэгов <element> внутри тэга <iteration>, это позволяет гибко организовывать сложные обходы файла с данными.
 
Например, если файл с данными имеет структуру
 
<syntaxhighlight lang="xml">
 
<root>
 
    <a></a>
 
    <a></a>
 
    <b></b>
 
    <a></a>
 
    <a></a>
 
    <b></b>
 
    <b></b>
 
    <a></a>
 
    <b></b>
 
</root>
 
</syntaxhighlight>
 
<nowiki>— т. е. внутри корневого элемента тэги <a> и <b> идут в произвольном порядке — то для того, чтобы обрабатывать тэги <a> и <b> в той последовательности, как они идут в файле данных, настроечный файл должен иметь вид:</nowiki>
 
<syntaxhighlight lang="xml">
 
<element name="root">
 
    <iteration>
 
        <element name="a">
 
        </element>
 
        <element name="b">
 
        </element>
 
    </iteration>   
 
</element>
 
</syntaxhighlight>
 
или же вид
 
<syntaxhighlight lang="xml">
 
<element name="root">
 
    <iteration>
 
        <element name="*">
 
        </element>
 
    </iteration>   
 
</element>
 
</syntaxhighlight>
 
<nowiki>а для того, чтобы обработать сначала все тэги <a>, а затем все тэги <b> — вид</nowiki>
 
<syntaxhighlight lang="xml">
 
<element name="root">
 
    <iteration>
 
        <element name="a">
 
        </element>
 
    </iteration>
 
    <iteration>
 
        <element name="b">
 
        </element>
 
    </iteration>
 
</element>
 
</syntaxhighlight>
 
 
 
Для того, чтобы обработать нулевой, а затем первый тэг, вне зависимости от имени этих тэгов, настроечный файл должен иметь вид:
 
 
 
<syntaxhighlight lang="xml">
 
<element name="root">
 
    <iteration index="0">
 
        <element name="*">
 
        </element>
 
    </iteration>
 
    <iteration index="1">
 
        <element name="*">
 
        </element>
 
    </iteration>
 
</element>
 
</syntaxhighlight>
 
 
 
=== Режим вывода ===
 
Оказавшись в режиме вывода, система копирует фрагмент шаблона в определённое место результирующего файла и заполняет этот фрагмент данными на основе текущего элемента файла данных. Тэги <output> могут встречаться только внутри тэга <element>, однако их может быть сколько угодно и они могут идти в произвольном порядке вперемешку с тэгами <iteration>. Атрибутами тэга <output> являются
 
* '''sourcesheet''' — опциональный атрибут, указывающий на лист книги шаблона, с которого берётся диапазон вывода. Если не указывать, то применяется текущий (последний использованный) лист.
 
* '''range''' – опциональный атрибут, диапазон шаблона, копируемый в результирующий документ, например “A1:M10”, или “5:6”, или  “C:C”. Применение диапазонов строк типа “5:6” в режиме вывода left-to-right и диапазонов столбцов типа  “C:C” в режиме вывода top-to-bottom приведёт к ошибке,
 
* '''worksheet''' – опциональный атрибут. Если он определён, то в файле вывода создаётся новый лист и позиция вывода смещается в ячейку A1 этого листа. Если определено значение этого атрибута, равное константе или XPath-выражению, то имя листа подставляется из результата этой константы или выражения.
 
* '''repeatingcols''', '''repeatingrows''' — опциональные атрибуты, работающие совместно с атрибутом '''worksheet'''. Задаёт для нового листа колонтитульные (повторяющиеся при печате на каждом листе) колонки/строки. Значения следует указывать в формате "1:2" с нумерацией С НУЛЯ (например, чтоб повторять на каждой странице первую строку, надо указать repeatingrows="0:0")
 
* '''pagebreak''' — если присутствует данный атрибут в виде pagebreak="true",  то вывод следующей секции отчёта начнётся с новой страницы. ''При этом, если текущий режим вывода — сверху вниз, то формируется горизонтальный разрыв страницы, а если слева направо -- то вертикальный разрыв страницы.'' Иногда недопустимы «висячие строки» в отчёте (чаще всего это относится к элементам «подвала» с итогами и подписями). Если XML2Spreadsheet-отчёт создаётся для моментального вывода на печать  (без ручной подгонки), то разбивка на страницы сразу же должна быть выполнена корректно.
 
 
 
== Репрезентативный пример ==
 
Предположим, что необходимо сформировать отчёт, состоящий из титульного листа и произвольного количества листов-разделов (имена этих листов определеляются данными).
 
Допустим, на титульном листе находится иерархический список элементов, находящихся на разном уровне и требующих различное оформление:
 
 
 
[[Файл:excel1.png]]
 
 
 
В разделах, которых может быть произвольное количество, определяемое данными шаблона, находится сводная таблица, количество строк и столбцов которой переменно:
 
 
 
[[Файл:excel2.png]]
 
 
 
Данные представлены в XML-файле, имеющем следующий вид:
 
<syntaxhighlight lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<report>
 
    <titlepage>
 
        <line name="Строка 1" value="10"/>
 
        <group name = "Строка 2" value = "23">
 
            <line name = "Строка 2.1" value="30"/>
 
            <line name = "Строка 2.2" value="92"/>
 
        </group>
 
        <line name = "Строка 3" value="11"/>
 
    </titlepage>
 
    <sheet name="Раздел А">
 
        <column name="2009"/>
 
        <column name="2010"/>
 
        <column name="2011"/>
 
        <row name="Позиция 1">
 
            <cell value = "1"/>
 
            <cell value = "33"/>
 
            <cell value = "34"/>
 
        </row>
 
        <row name="Позиция 2">
 
            <cell value = "93"/>
 
            <cell value = "9"/>
 
            <cell value = "1"/>
 
        </row>
 
        <row name="Позиция 3">
 
            <cell value = "1"/>
 
            <cell value = "50"/>
 
            <cell value = "2"/>
 
        </row>
 
    </sheet>
 
    <sheet name="Раздел Б">
 
       
 
    </sheet>
 
</report>
 
</syntaxhighlight>
 
В этом случае, шаблон, содержащий оформление и поля подстановки для титульного листа и разделов, может иметь вид:
 
 
 
[[Файл:excel3.png]]
 
 
 
Подстановочные поля имеют формат
 
 
 
'''~{Xpath-выражение}'''
 
 
 
(тильда, фигурная скобка, Xpath-выражение относительно контекста XML, закрывающая фигурная скобка).
 
 
 
[[Файл:danger.png|50px|left|Важная информация]] Умение грамонтно писать XPath-выражения, извлекающие информацию из текущего контекста XML-файла, является ключевым для успешного создания отчётов в технологии Xml2Spreadsheet. Если вы не знакомы с XPath, изучить его можно, например, здесь: [http://www.w3schools.com/xpath/ http://www.w3schools.com/xpath/]
 
 
 
В Xpath-выражении, помимо стандартного синтаксиса, можно применять следующие специальные функции:
 
* '''сurrent()''' --- при обработке меняется на полный XPath-путь к текущей ноде. Полный аналог функции current() в XSLT. Необходимо для сложных XPath-выражений, наличие этой функции оправдано по той же причине, что наличие current() в XSLT (см. документацию по XSLT о функции current() и её отличию от . (точки)).
 
* '''position()''' --- номер итерации. При обработке меняется на номер прохода по iteration. Решает задачу простой последовательной нумерации пунктов в отчёте (в случаях, когда данная функция подходит, не нужно включать нумерацию в тэги файла данных).
 
 
 
Обратите внимание на то, что в шаблоне может присутствовать поясняющая информация, которая потом не попадёт в результирующий документ: хорошей практикой является снабжение шаблона поясняющей информацией, облегчающей дальнейшие доработки.
 
 
 
Файл-дескриптор, управляющий обходом XML, может иметь следующий вид:
 
<syntaxhighlight lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<element name="report">
 
    <!-- Вывод единственного титульного листа, с иерархией -->
 
    <iteration index="0">
 
        <element name="titlepage">
 
            <!-- Статическое название листа -->
 
            <output worksheet="Титульный" range="A3:B4"/>
 
            <iteration>
 
                <element name="line">
 
                    <output range="A5:B5"/>
 
                </element>
 
                <element name="group">
 
                    <output range="A6:B6"/>
 
                    <iteration>
 
                        <element name="line">
 
                            <output range="A7:B7"/>
 
                        </element>
 
                    </iteration>
 
                </element>
 
            </iteration>
 
        </element>
 
    </iteration>
 
    <!-- Вывод всех прочих листов, со сводными таблицами -->
 
    <iteration>
 
        <element name="sheet">
 
            <!-- Динамическое название листа -->
 
            <output worksheet="~{@name}"/>
 
            <!-- И за ним слева направо заголовки столбцов -->
 
            <iteration mode="horizontal">
 
                <element name="(before)">
 
                    <!-- Выводим пустую ячейку в ЛВУ сводной таблицы -->
 
                    <output sourcesheet="src" range="A11"/>
 
                </element>
 
                <element name="column">
 
                    <output sourcesheet="src" range="B11"/>
 
                </element>
 
            </iteration>
 
            <!-- Выводим строки: итерация с режимом вывода умолчанию, сверху вниз -->
 
            <iteration>
 
                <element name="row">
 
                    <!-- И по строке - слева направо -->
 
                    <iteration mode="horizontal">
 
                        <!-- Заголовок строки -->
 
                        <element name="(before)">
 
                            <output range="A12"/>
 
                        </element>
 
                        <element name="cell">
 
                            <output range="B12"/>
 
                        </element>
 
                    </iteration>
 
                </element>
 
            </iteration>
 
        </element>
 
    </iteration>
 
</element>
 
 
 
</syntaxhighlight>
 
 
 
== SAX-режим для огромных отчётов ==
 
 
 
При необходимости быстрого построения отчётов с очень большим количеством данных реализован SAX-режим обработки файла с исходными данными. Данный режим подразумевает, что файл с данными никогда не загружается в память целиком и формирование результирующего файла управляется SAX-событиями, чем достигается высокая скорость и экономия памяти, позволяющая обрабатывать огромные массивы данных.
 
Использование SAX-режима можно указать в параметрах запуска XML2Spreadsheet (по умолчанию режим запуска — DOM).
 
SAX-режим подразумевает следующие структурные ограничения:
 
# Только один тэг <iteration> внутри каждого из тэгов <element>.
 
# XPath-ссылки могут быть только ссылками на атрибуты текущего элемента. Поддерживается функция position().
 
Переструктурировав соответствующим образом XML-файл с данными, можно добиться выполнения п. 1 и 2 для широкого спектра задач (в частности, легко можно переструктурировать XML для «репрезентативного примера»).
 
 
 
=Второй этап: формирование PDF-файлов и вывод на печать=
 
 
 
При помощи модуля Excel2Print получившийся Excel-отчёт можно преобразовать в PDF-формат или сразу же вывести на печать.
 
 
 
[[Файл:danger.png|50px|left|Важная информация]]
 
 
 
 
 
Внимание: так можно поступить только с XLS-файлом, с XLSX-форматом система пока не работает.
 
 
 
Внимание: возможности выводить в PDF/принтер картинки и графики нет и в настоящий момент такая возможность даже не рассматривается!
 
 
 
==Использование модуля Excel2Print==
 
 
 
Рекомендованный паттерн следующий:
 
<syntaxhighlight lang="python">
 
    import java.io.File as File
 
    import java.io.FileInputStream as FileInputStream
 
    import java.io.FileOutputStream as FileOutputStream
 
 
    #Библиотеки XML2Spreadsheet и Excel2Print
 
    import ru.curs.flute.xml2spreadsheet.XML2Spreadsheet as XML2Spreadsheet
 
    import ru.curs.flute.excel2print.Excel2Print as Excel2Print
 
 
    #Готовим файлы и потоки
 
    data=FileInputStream(datafile)
 
    template=File(parameters.flutepath + 'templates/universal_transfer_document.xls')
 
    descriptor=File(parameters.flutepath + 'templates/universal_transfer_document.xml')
 
 
    #получаем объект-книгу при помощи нового метода toPOIWorkbook
 
    workbook = XML2Spreadsheet.toPOIWorkbook(data, descriptor, template, False)
 
 
    #инициируем конвертер Excel2Print созданной книгой
 
    e2p = Excel2Print(workbook)
 
    #Прописываем путь к файлу конфигурации
 
    e2p.setFopConfig(parameters.flutepath + "c:/temp/fop.xconf");
 
    #Прописываем имя принтера в операционной системе (если это необходимо)
 
    e2p.setPrinterName("My LaserJet Printer")
 
 
    #Конверсия в PDF
 
    pdfresult=FileOutputStream('C:/temp/result.pdf')
 
    try:
 
        e2p.toPDF(pdfresult)
 
    finally:
 
        pdfresult.close()
 
 
    #Вывод на принтер (принтер операционной системы, настроенный по умолчанию)
 
    e2p.toPrinter()
 
 
 
</syntaxhighlight>
 
 
 
==Известные особенности и альтернативный метод вывода на печать==
 
 
 
При первом запуске система кэширует метрики шрифтов, и поэтому первый запуск может происходить очень долго.
 
 
 
Быстро и качественно вывести PDF-файл на печать, не используя Acrobat Reader, можно при помощи Open Source системы GhostScript + GhostView ([http://www.ghostscript.com/ www.ghostscript.com]). Команда
 
 
 
    gsprint myfile.pdf
 
 
 
выбрасывает PDF-файл на принтер, а также имеет множество дополнительных параметров.
 
 
 
 
 
{{Legal}}
 
[[Категория:Руководства пользователей]]
 
[[Категория:Xml2spreadsheet]]
 

Текущая версия на 17:46, 24 февраля 2018

Перенаправление на: